/*
a = [A-z]/i, [0-9]
A = [À-ÿ], [0-9], [A-Z]
9 = [0-9]
C = [À-ÿ]/i, [A-Z]
c = [A-Z]
* = .
money = money format -00.000,00
*/
(function(jQuery){
	var patterns = {
		'1': /[A-Z]/i, 
		'2': /[0-9]/, 
		'4': /[À-ÿ]/i, 
		'8': /./, 
		'16': /\s/};

	var rules = {
		'a': 3, 
		'A': 7, 
		'9': 2, 
		'C': 5, 
		'c': 1, 
		'*': 8, 
		'W': 21};

	var allpatterns = /([A-Z]|[0-9]|[À-ÿ])/i;
	var rulepattern = /[A|9|C|W|\*|\^]/i;

	function accept(c, rule){
		for(var i = 1, r = rules[rule] || 0; i <= r; i <<= 1){
			if(r & i && patterns[i].test(c)){
				break;
			}
		}
		return i <= r || c == rule;
	}

	jQuery.fn.mask = function(mask, options)
	{
		var defaultOptions = {
				upperCase: false,
				maxlength: false,
				decimal: '.',
				thousands: ',',
				decimalPlace: 2};

		var options = jQuery.extend(defaultOptions, options || {});

		function inputmask(e){
			var seletionText = jQuery(this).selection();
			var selected = seletionText.length;
			var selection = jQuery(this).getSelection();
			var start = selection.start;
			var end = selection.end;
			var allow = true;
			var k = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;

			var c = String.fromCharCode(k);
		
			if(!k || k == 9 || k == 13 || k == 16 || k == 35 || k == 36 || k == 37 || k == 38 || k == 39 || k == 40 || k == 116){
				return true;
			}
			// allow Ctrl+A 
			if((e.ctrlKey && k == 97 /* firefox */) || (e.ctrlKey && k == 65) /* opera */) return true;
			// allow Ctrl+X (cut)
			if((e.ctrlKey && k == 120 /* firefox */) || (e.ctrlKey && k == 88) /* opera */) return true;
			// allow Ctrl+C (copy)
			if((e.ctrlKey && k == 99 /* firefox */) || (e.ctrlKey && k == 67) /* opera */) return true;
			// allow Ctrl+Z (undo)
			if((e.ctrlKey && k == 122 /* firefox */) || (e.ctrlKey && k == 90) /* opera */) return true;
			// allow or deny Ctrl+V (paste), Shift+Ins
			if((e.ctrlKey && k == 118 /* firefox */) || (e.ctrlKey && k == 86) /* opera */
			|| (e.shiftKey && k == 45)) return true;


			var thismask = mask;
			if(options.maxlength){
				var thismask = thismask.pad(jQuery(this).attr('maxlength'), thismask, 1);
			}

			if(k == 8 || k == 46){ // Backspace and Delete
				allow = true;
			}else if(/m|n/i.test(thismask)){
				allow = false;
				if((c == '-' && selection.start == 0 && this.value.indexOf('-') == -1) || (/[0-9]/.test(c)) || (c == options.decimal && this.value.indexOf(options.decimal) == -1 && this.value.length > 0)){
					allow = true;
				}
				if(c == options.decimal){
//				if(/,\./.test(c)){
					jQuery(this).one('keyup', function(){
						jQuery(this).setSelection({start: this.value.indexOf(options.decimal) + 1});
					});
				}
			}else if(this.value.length - selected < thismask.length){
				var rule = '';
				var i = start;
				do{
					rule = thismask.substr(i, 1);
					if(rulepattern.test(rule)){
						if(accept(c, rule)){
							allow = true;
							start++;
							end = start;
						}
					}else{
						allow = false;
						start++;
						end = start;
					}
					i++;
				}while(!(rulepattern.test(rule)) && i < thismask.length);
			}
			return allow;
		}
		
		function inputformat(e){
			var k = (e.charCode?e.charCode:e.keyCode);
			if(!k || k == 9 || k == 116 || k == 37 || k == 38 || k == 39 || k == 40 || k == 16 || k == 35 || k == 36 || k == 13){
				return true;
			}
			format(this, mask, options, k);
			return true;
		}

		this.each(function(){
			if(!(/m|n/i.test(mask)) && !options.maxlength){
				jQuery(this).attr({maxlength: mask.length, size: mask.length});
			}
			if(this._inputmask){
				jQuery(this).unbind('keypress', this._inputmask).unbind('keyup', this._inputformat);
			}
			this._inputmask = inputmask;
			this._inputformat = inputformat;
			jQuery(this).format(mask, options).bind('keypress', this._inputmask)
			.bind('keyup', this._inputformat)
			.bind('focus', function(){
				this.oldValue = this.value;
			}).bind('blur', function(){
				if(this.value != this.oldValue){
					jQuery(this).trigger('change');
				}
			});
		});
		return this;
	}
	
	function format(element, mask, options, key){
		var defaultOptions = {
				upperCase: false,
				maxlength: false,
				decimal: '.',
				thousands: ',',
				decimalPlace: 2};
		var options = jQuery.extend(defaultOptions, options || {});
		var thismask = mask;
		if(options.maxlength){
			thismask = thismask.pad(jQuery(element).attr('maxlength'), thismask, 1);
		}

		var originalSelection = jQuery(element).getSelection();
		var val = element.value;
		var selection = {start:originalSelection.start, end:originalSelection.end};
		var length = Math.min(thismask.length, val.length);
		var value = '';
		var formated = '';
		if(/m|n/i.test(thismask)){
			if(val.trim() != ''){
				var value = val;
				var originalThousands = 0;
//				if(!isFinite(value)){
					while(value.indexOf(options.thousands) != -1){
						value = value.replace(options.thousands, '');
						originalThousands++;
					}
					value = value.replace(options.decimal, '.');
//				}
				value = parseFloat(value);
				if(/m/i.test(thismask)){
					formated = value.toMoney(options.decimalPlace, options.decimal, options.thousands);
				}else{
					formated = value + '';
//					formated = value.toMoney(0, options.decimal, options.thousands);
				}
				var thousands = 0;
				var pos = 0;
				while((pos = formated.indexOf(options.thousands, pos)) != -1){
					pos++;
					thousands++;
				}
				if(thousands > originalThousands){
					selection.start += (thousands - originalThousands);
					selection.end += (thousands - originalThousands);
				}
			}else{
				formated = '';
			}
		}else{
			for(var i = 0; i < val.length; i++){
				var c = val.substr(i, 1);
				if(allpatterns.test(c) || (mask == '*' && options.maxlength)){
					value += c;
				}else{
					if(i < originalSelection.start){
						selection.start--;
						selection.end--;
					}else if(i < selection.end){
						selection.end--;
					}
				}
			}
			if(value.trim() == ''){
				element.value = '';
				return true;
			}
			var vali = 0;
			for(var i = 0; vali < value.length && i < thismask.length; i++){
				var rule = thismask.substr(i, 1);
				var c = value.substr(vali, 1);
				if(!c){
					c = '';
				}
				if(rulepattern.test(rule)){
					if(accept(c, rule)){
						formated += c;
					}
					vali++;
				}else{
					formated += rule;
					if(i < originalSelection.start){
						selection.start++;
						selection.end++;
					}else if(i < originalSelection.end){
						selection.end++;
					}
				}
			}
			if(i < thismask.length && !(key == 8 || key == 46)){
				i--;
				do{
					var rule = thismask.substr(i, 1);
					if(!rulepattern.test(rule)){
						formated += rule;
						if(i < originalSelection.start || originalSelection.start == val.length){
							selection.start++;
							selection.end = selection.start;
						}
					}
					i++;
				}while(!(rulepattern.test(thismask.substr(i, 1))) && i < thismask.length);
			}
		}
		if(options.upperCase){
			formated = formated.toUpperCase();
		}
		element.value = formated;
		jQuery(element).setSelection(selection);
	}

	jQuery.fn.format = function(mask, options){
		this.each(function(){
			format(this, mask, options);
		});
		return this;
	}
})(jQuery);