/**
 * Rounder plugin
 * 
 * @author slava
 */
 var MAX_FRACTION_LENGTH = $.browser.msie ? 16 : 32;
 
(function($){
	$.widget("ui.rounder", {
		_init: function() {		
			var el = this.element;	
			var o = this.options;
			var self = this;
			
			this.getValue();
			var rounder = $('<span class="rounder"></span>');
			el.after(rounder);
			
			rounder.css({
				left: el.position().left + 1 + el.width() - 6 + 'px',
				top: (el.position().top + el.outerHeight() + ($.browser.msie && $.browser.versionX < 8 ? 0 : - 8)) + 'px'
			});
			
			rounder.draggable({
				axis: 'x',
				drag: function(event, ui) {
					if (ui.position.left < self.leftBoundry) {
						ui.position.left = self.leftBoundry;
					}
					if (ui.position.left >= self.rightBoundry) {
						ui.position.left = self.rightBoundry;
					}
					var strOrig = self.str;
					for(var i = 0; i < o.max; i++) {
						strOrig += '0';
					}
					for (var i = 1; i <= strOrig.length; i++) {
						var str = strOrig.slice(0, i);
						o.helper.text(str);
						var Boundry = el.position().left + 1 + o.helper.width() - 6; 
						if (ui.position.left < Boundry) {
							if (str.indexOf(',') == str.length - 1) {
								self.roundStr(i);
							} else {
								self.roundStr(i - 1);
							}
							break;
						} else if(ui.position.left == Boundry) {
							if (Boundry == self.leftBoundry && str.indexOf(',') == -1) {
								self.roundStr(i+1);
							} else {
								self.roundStr(i);
							}
							break;
						}
					}
				}
			});
						
			this.rounder = rounder;
			
			el.focus(function(){
				self.hide();
			});
			
			el.bind('change', function () {
			    el.val(
			        self.formatNum(el.val().replace(',', '.'))
			    );
			})
			
		},
		
		update: function(num) {
			var o = this.options;
			var el = this.element;
			
			this.getValue();
			var value = this.value;
			
			if(typeof num == 'undefined') {
				num = this.num;
			} else {
				this.num = num;
				this.str = this.formatNum(this.num);
			}
			var str = this.expandExponential(num);
			
			if (el.hasClass('value-out')) {
				if (str.indexOf('.') == -1) {
					this.hide();
				} else {
					var arr = str.split('.');
					var intPart = arr[0];
					var fracPart = arr[1];
					
					var fracZeroes = fracPart.match(/^0+/);
					fracZeroes = fracZeroes ? fracZeroes[0] : '';
					var fracZeroCount = fracZeroes ? fracZeroes.length : 0;
					
					if (fracZeroCount + 1 > MAX_FRACTION_LENGTH) fracZeroCount = MAX_FRACTION_LENGTH; 	

					if (fracPart == 0) {
						this.hide();
					} else {
						this.rounder.css({
							left: el.position().left + 1 + el.width() - 6 + 'px'
						});
						//
						// calculate min position
						//
						if(Math.abs(num) > 1) {
							o.helper.text(this.formatNum(num.toFixed())+',');
						} else {
							o.helper.text(this.formatNum(num.toFixed(fracZeroCount+1)).replace(/,(0+$)/, ','));
						}
						
						this.leftBoundry = el.position().left + o.helper.width() + 1 - 6;
						
						//
						// calculate min position
						//
						var precision = o.max;
						if (Math.abs(num) > 1) {
							precision += intPart.length + fracZeroCount;
						}
						//var maxNum = num.toPrecision(precision);
						var maxNum = num.toFixed( 
							fracZeroCount + o.max <= MAX_FRACTION_LENGTH ? fracZeroCount + o.max : MAX_FRACTION_LENGTH
						);
						var maxStr = this.formatNum(maxNum);
						
						o.helper.text(maxStr);
						this.rightBoundry = el.position().left + o.helper.width() + 1 - 6;
						
						this.show();				 
					}
				}
			} else {
				this.hide();
			}
		},
		
		/**
		 * round string
		 * 
		 * @param {Number} index
		 * @param {Boolean} returnVal round string itself or just return the result
		 * @return {String}
		 */
		roundStr: function(index, returnVal){
			var str = this.str;
			var num = this.num;
			
			var fixed = index - 1 - str.split(',')[0].length;
			
			var fixedNum = num.toFixed(fixed);
			var roundedStr = this.formatNum(fixedNum);
			
			if (returnVal) {
				return roundedStr;
			} else {
				this.element.val(roundedStr);
				this.setInputWidth();
			}
		},
		
		/**
		 * number formatting
		 * 
		 * @param {Object} num
		 */
		formatNum: function(num){
			var o = this.options;
			var str = this.expandExponential(num);
			if(typeof num == 'number') {
				var arr2 = str.split('.');
				var precision = o.max;
				if(Math.abs(parseInt(arr2[0])) > 0) {
					precision += arr2[0].replace('-', '').length;
				} else {
					if(Math.abs(num.toPrecision(precision)) >= 1) {
						precision++;
					}
				}
				str = num.toPrecision(precision).toString();
				str = this.expandExponential(str);
			}
			
			var value = str.replace('.', ',').replace(/\s+/g, '');
			var arr = value.split(',');
			if(arr[0].length > 3) {
				arr[0] = arr[0].replace(/(\d{1,3}(?=(\d{3})+$))/g,'\$1 ');
			}
			var value = arr.join(',');
			
			return value;
		},
		
		/**
		 * expand Number
		 * 
		 * @param {Number} n
		 * @return {String} expanded number as string
		 */
		expandExponential: function(n){
			return n.toString().replace(/^([+-])?(\d+).?(\d*)[eE]([-+]?\d+)$/, function(x, s, n, f, c){
				var l = +c < 0, i = n.length + +c, x = (l ? n : f).length, c = ((c = Math.abs(c)) >= x ? c - x + l : 0), z = (new Array(c + 1)).join('0'), r = n + f;
				return (s || '') + (l ? r = z + r : r += z).substr(0, i += l ? z.length : 0) + (i < r.length ? '.' + r.substr(i) : '');
			});
		},
		
		/**
		 * Calculate input width using hidden span after input with the same style
		 */
		setInputWidth: function() {
			var el = this.element;
			var span = el.nextAll('span.value:first');
			span.text(el.val());
			el.width(span.width());
		},
		
		getValue: function() {
			this.value = this.element.val();
			this.num = new Number(this.value.replace(/\s/g, '').replace(',', '.'));
			this.str = this.formatNum(this.num); 
		},
		
		show: function() {
			this.rounder.show();
		},
		
		hide: function() {
			this.rounder.hide();
		},
		
		destroy: function() {
		}
	});
	
	$.extend($.ui.rounder, {
		defaults: {
			round: 2, // default round digits after zero or dot
			max: 5 // max digits after zero or dot
		}
	});
})(jQuery);
