function ProductSell(o){
	
	/**reference for the instance*/
	var app = this;
	/**currently loaded product*/
	this.product = null;
	
	this.defaults = {
		/**id of filter text*/
		selector_filter_text: '#filter_text',
		/**selector for customer cart container*/
		selector_customer_cart: '.customer-cart',
		selector_user_cart: '.user-cart',
		selector_btn_pay_customer_cart: '#btn_pay_customer_cart',
		selector_btn_pay_user_cart: '#btn_pay_user_cart',
		/**mark list paid url*/
		url_pay_customer_card: '-',
		url_pay_user_cart: '',
		/** ajax url for lookup service*/
		lookup_product_url: '',
		find_product_url: '',
		/**the id of form*/
	    selector_form: '#product_form',
	    /**form contains error text*/
	    form_invalid: 'Az ürlap hibákat tartalmaz',
	    message_paid: 'Tételek fizetve',
	    /**list of sold items by current user*/
	    user_cart: [],// the user cart items
	    discounts: [],
	    customer_cart: [], //the customer cart items
	    id_account: null,
	    products : [],
	    url_delete_transaction : '',
	    url_pay_transaction : '',
	    value_number: '',
	    name_number: '',
	    user_cart_on: true,
	    //if enter pressed in count field, what to submet: 'paid' or 'add_to_user_cart' action
	    product_count_enter_action: 'user_cart',
	    //barcode or autocomplete
	    product_sale_default_focus: 'barcode',
	    
	    
	};
	
	init();
	
	function init(){
		$.extend(app.defaults, o );
		app.keyDate = null;
		app.keySeq = '';
		app.enterPressed = 0;
		
		addSellBehaiours();
		
		setFocus();
		
		addListenerBehaviours();
		
		createCarts();
		
		productChanged();
		
		initAutocomplete();
		disalbeMousewheelSpinOnNumberInput();
	}
	
	function disalbeMousewheelSpinOnNumberInput(){
		$(':input[type=number]').on('mousewheel', function(e){
		    e.preventDefault();
		});
	}
	
	/**
	 * display user and customer cart on page load
	 * */
	function createCarts(){
		createUserCartTable();
		createCustomerCartTable();
	}
	
	/**
	 * add change and   enter pressed listeners
	 * */
	function addListenerBehaviours(){
		addEnterPressedBehaviours();
		addBehaviorCountChangedListener();
		addBehaviourDisocuntChanged();
	}
	
	/**
	 * sell a product. there are three type of sell:
	 *  - sell inmadietly
	 *  - sell and put it to user cart
	 *  - sell and put it to customer cart
	 * */
	function addSellBehaiours(){
		addBehaviourBtnSell();
		addBehaviourBtnSellAndAppendToBill();
		addBehaviourBtnAddToCustomerCart();
		
		addBehaviorAjaxSubmit();
	}
	
	
	function addBehaviourDisocuntChanged(){
		$("#productsaleform-id_discount").change(refreshCalculatedValues);
	}
	
	function resetSequenceIfToMuchTimePassedOrEnterWasPressed(){
		var prevDate, timePassed;
		prevDate= app.keyDate;
		app.keyDate = new Date().getTime();
		timePassed = app.keyDate - prevDate;
		if ( app.enterPressed > 0 || timePassed > 2000 ){
			app.seq = '';
			app.enterPressed = 0;
		}
	}
	
	function appendCharToSeq(event){
		var code,s;
		code = event.which;
		if ( code ){
			s = String.fromCharCode(code);
			app.seq += s;
		}
	}
	
	/**
	 * Add traversing between input elements on enter key pressed
	 * */
	function addEnterPressedBehaviours(){
		addBehaviorEnterPressedListener();
		addBehaviorCountEnterPressedListener();
		addBehaviorCurrencyEnterPressedListener();
		addBehaviorAccountEnterPressedListener();
		addBehaviorDiscountEnterPressedListener();
	}
	
	/**
	 * set the focus for text input #filter_text
	 * */
	function setFocus(){
		if ( app.defaults.product_sale_default_focus == 'barcode'){
			$("#filter_text").focus();
		}else{
			$("#product_search").focus();
		}
		
	}
	
	/**listen for enter key pressed on #filter_text*/
	function addBehaviorEnterPressedListener(){
		$(   app.defaults.selector_filter_text ).keypress(function( event ) {
			  if ( event.which == 13 ) {
				  event.preventDefault();
				  $('#product_search').val('');
				  $(this).val( fixBarcode($(this).val()));
				  lookupProduct();
			  }
		});	
	}
	
	function fixBarcode(s){
		var result;
		result = s;
		result = result.replace(/ö/g, "0");
		return result;
	}
	
	/**listen for enter key pressed on #productsaleform-count*/
	function addBehaviorCountEnterPressedListener(){
		$(   '#productsaleform-count' ).keypress(function( event ) {
			if ( event.which == 13 ) {
				event.preventDefault();
				event.stopImmediatePropagation();
				
				if ( app.defaults.user_cart_on ){
					if ( app.defaults.product_count_enter_action == 'user_cart'){
						submitSellAndAppend();	
					}else{
						submitSell();
					}
				}else{
					submitSell();
				}
				
							
			}
		});	
	}
	/**listen for enter key pressed on #productsaleform-id_currency*/
	function addBehaviorCurrencyEnterPressedListener(){
		$(   '#productsaleform-id_currency' ).keypress(function( event ) {
			if ( event.which == 13 ) {
				event.preventDefault();
				event.stopImmediatePropagation();
				$('#productsaleform-id_account').focus();
			}
		});	
	}
	/**listen for enter key pressed on #productsaleform-id_account*/
	function addBehaviorAccountEnterPressedListener(){
		$(   '#productsaleform-id_account' ).keypress(function( event ) {
			if ( event.which == 13 ) {
				event.preventDefault();
				event.stopImmediatePropagation();
				$('#productsaleform-id_discount').focus();
			}
		});	
	}
	/**listen for enter key pressed on #productsaleform-id_discount*/
	function addBehaviorDiscountEnterPressedListener(){
		$(   '#productsaleform-id_discount' ).keypress(function( event ) {
			if ( event.which == 13 ) {
				event.preventDefault();
				event.stopImmediatePropagation();
				$('#productsaleform-comment').focus();
			}
		});	
	}
	/**listen for event "change"  #productsaleform-count*/
	function addBehaviorCountChangedListener(){
		$(   '#productsaleform-count' ).change(function( event ) {
			refreshCalculatedValues();
			 
		});	
	}
	
	/**load a product from server by exact barcode or product number*/
	function lookupProduct(){
		_lookupProduct($(  app.defaults.selector_filter_text ).val());
	}
	
	function _lookupProduct(s){
		var data, url;
		
		url = app.defaults.lookup_product_url;
		data = {
				'query' : 	s,
		};
		$.ajax({
			dataType: "json",
			url: url,
			data: data,
			success: onLookupProductReady
		});
	}
	
	function _findProduct(id){
		var data, url;
		
		url = app.defaults.find_product_url;
		data = {
				'id' : 	id,
		};
		
		$.ajax({
			dataType: "json",
			url: url,
			data: data,
			success: onLookupProductReady
		});
	}
	
	/**succes event handler after lookup product event*/
	function onLookupProductReady(  data  ){
		app.product = data.product;
		productChanged();
	}
	
	/**handles product change.*/
	function productChanged( ){
		clearForm();
		if ( app.product != null){
			applyProduct( app.product);
			refreshCalculatedValues();
			$("#productsaleform-count").focus().select();
		}
	}
	
	function clearForm(){
		var table;
		table = $('.table-product');
		table.find('.product-name').html('-');
		table.find('.product-price').html('-');
		table.find('.product-number').html('-');
		table.find('.product-barcode').html('-');
		table.find('.product-stock').html('-');
		table.find('.product-price').html('-');
		table.find('.product-sale-price').html('-');
		$('#productsaleform-id_product').val('');
		$('#productsaleform-id_account').val( app.defaults.id_account ?  app.defaults.id_account  : '');
		$("#productsaleform-count").val(1);
//		
	}
	
	function applyProduct(product){
		var table;
		table = $('.table-product');
		table.find('.product-name').html(product.name);
		table.find('.product-sale-price').html(product.sale_price);
		table.find('.product-number').html(product.product_number);
		table.find('.product-barcode').html(product.barcode);
		table.find('.product-stock').html(product.stock);
		$('#productsaleform-id_product').val(product.id_product);
		if ( app.defaults.id_account == null)
			$('#productsaleform-id_account').val(product.id_account);
		
	}
		
	function refreshCalculatedValues(){
		var count,price,id_discount,discount;
		var table;
		table = $('.table-product');
		count = $("#productsaleform-count").val();
		id_discount = $("#productsaleform-id_discount").val();
		app.discount = findDiscount( id_discount );
		count = +count;
		if ( isNaN(count)){
			count = 0;
		}
		price = count * app.product.sale_price;
		
		
		discount = calcDiscount(price, app.discount);
		price = deductDiscount(price,discount);
		price = normalizePrice(price);
		
		table.find('.product-count').html(count);
		table.find('.product-price').html(price);
	}

	function addBehaviourBtnSell(){
		$('#btn_sell').on('click',submitSell);
	}
	
	function addBehaviourBtnSellAndAppendToBill(){
		$('#btn_sell_append').on('click',submitSellAndAppend);
	}

	function addBehaviourBtnAddToCustomerCart(){
		$('#btn_add_to_customer_cart').on('click',submitAddToCustomerCart);
	}
	
	function submitSell(){
		$('#productsaleform-cart').val('');
		$('#product_form').submit();
	}
	function submitSellAndAppend(){
		$('#productsaleform-cart').val('user');
		$('#product_form').submit();
	}
	
	
	function submitAddToCustomerCart(){
		$('#productsaleform-cart').val('customer');
		$('#product_form').submit();
	}
	
	function addBehaviorAjaxSubmit(){
		
		$('body').on('afterValidate', '#product_form', function () {
		     var form = $(this);
		     // return false if form still have some validation errors
		     if ( form.find('.has-error').length ) {
		    	 $.notify(app.defaults.form_invalid, { 'type' : 'warning' });
		     }
		     
		});
		
		
		$('body').on('beforeSubmit', '#product_form', function () {
			     var form = $(this);
			     // return false if form still have some validation errors
			     if (form.find('.has-error').length) {
			    	 $.notify(app.defaults.form_invalid, { 'type' : 'success' });
			          return false;
			     }
		
			     // submit form
			     $.ajax({
			          url: form.attr('action'),
			          type: 'post',
			          data: form.serialize(),
			          success: function (response) {
			        	  if ( response.code == 'success'){
			        		  $.notify(response.message, { 'type' : 'success' });
			        		  clearForm();
			        		  refreshCalculatedValues();
			        		  $("#filter_text").val('');
			        		  $("#product_search").val('');
			        		  setFocus();
			        		  app.defaults.user_cart = response.transfers; 
			        		  app.defaults.customer_cart = response.customer_cart;
			        		  refreshUserCart();
			        		  refreshCustomerCart();
			        	  }else if ( response.code == 'invalid'){
			        		  if ( response.errors ){
			        			  	$.each(response.errors, function (key, value){
			        			  		var message;
			        			  		message = $.map(value, function(obj){ return obj }).join('  ');
				        			  		$.notify(message, { 'type' : 'danger' });
				        			  	}
			        			  	);
			        		  }
			        	  }
			          }
			     });
			     return false;
			});
		
	}
	
	
	function refreshUserCart(){
		$(app.defaults.selector_user_cart).transferList( 'option', 'transfers' , app.defaults.user_cart );
	}
	function refreshCustomerCart(){
		$(app.defaults.selector_customer_cart ).transferList( 'option', 'transfers' , app.defaults.customer_cart );
	}
	
	
	function createUserCartTable(){
		$(app.defaults.selector_user_cart).transferList({
			'transfers' : app.defaults.user_cart,
			'url_delete' : app.defaults.url_delete_transaction,
			'url_pay' : app.defaults.url_pay_transaction,
			'url_pay_all_visible' : app.defaults.url_pay_user_cart,
			'name_transfers' : 'UserCartPayoutForm[transfers][]',
			'id_form' : 'frm_user_cart',
//			'value_number': app.defaults.value_number,
//			'name_number':app.defaults.name_number,
		});
	}
	function createCustomerCartTable(){
		$(app.defaults.selector_customer_cart).transferList({
			'transfers' : app.defaults.customer_cart,
			'url_delete' : app.defaults.url_delete_transaction,
			'url_pay' : app.defaults.url_pay_transaction,
			'url_pay_all_visible' : app.defaults.url_pay_customer_card,
			'name_transfers' : 'CustomerCartPayoutForm[transfers][]',
			'id_form' : 'frm_customer_cart',
//			'value_number': app.defaults.value_number,
//			'name_number':app.defaults.name_number,
		});
	}
	
	function findDiscount(id_discount){
		var discount;
		discount = null;
		$.each( app.defaults.discounts , function(i,e){
			if ( e.id_discount == id_discount ){
				discount = e;
			}
			
		});
		return discount;
	}
	
	function calcDiscount(price,discount){
		var result, discountMoney;
		result = 0;
		
		if ( discount ){
			result = price * ( discount.value / 100 );
			result = Math.floor( result );
		}
		
		return result;
	}
	
	function deductDiscount( price , discount ){
			var result;
			result = price - discount;
			return result;
	}
	
	function normalizePrice( price ){
		var result;
//		result = hufRound(price);
		return price;
	}
	
	function hufRound(x)
    {
        if (x > 0) {
            return (x % 5) >= 2.5 ? parseInt(x / 5) * 5 + 5 : parseInt(x / 5) * 5;
        } else {
            return ((Math.abs(x) % 5) >= 2.5 ? parseInt(Math.abs(x) / 5) * 5 + 5 : parseInt(Math.abs(x) / 5) * 5)*-1;
        }
    }
	
	
	function initAutocomplete(){
//		var colors = ["red", "blue", "green", "yellow", "brown", "black"];
//		$('#product_search').typeahead( {source: colors } );
		
		var $input = $('#product_search');
		$input.typeahead({source: app.defaults.products, 
		            autoSelect: true,
		            items: 20,
		            minLength: 3,
//		            displayText: function (item){
//		            	return item.
//		            }
		            
		}); 
		$input.change(function() {
		    var current = $input.typeahead("getActive");
		    $("#filter_text").val('');
		    if (current) {
		        // Some item from your model is active!
		        if (current.name == $input.val()) {
		            // This means the exact match is found. Use toLowerCase() if you want case insensitive match.
//		        	console.info(current);
		        	_findProduct(current.id_product);
		        } else {
		            // This means it is only a partial match, you can either add a new item 
		            // or take the active if you don't want new items
//		        	console.info('partial');
		        	app.product = null;
		        	productChanged();
		        }
		    } else {
		        // Nothing is active so it is a new value (or maybe empty value)
//		    	console.info('incactive');
		    	app.product = null;
		    	productChanged();
		    }
		});
	}
	
}
