// конвертер чисел
(function($){
	$.fn.numbers = function(options){
		var defaults = {
			error: 'error red-h36',
			popupError: 'popup-error',
			resultLinks: 'result-links',
			selected: 'selected bg bg-h36 dc-bg'
		}
		
		options = $.extend(defaults, options);
		
		return this.each(function(){
			var el = $(this);
			
			if (el.data('numbers')) 
				return;
			
			var numbers = new Numbers(this, options);
			
			el.data('numbers', numbers);
		});
	}
	
	var Numbers = function(el, options){
		var el = $(el);
		var self = this;
		
		this.converts = 0;
		
		this.init = function(){
			var items = el.children();
			var itemTag = items[0].tagName;
			
			// type select (row click)
			items.click(function(){
				var item = $(this);
				self.selectType(item);
				if (item.hasClass('fixed'))
				    item.find('> input').focus();
			});
			
			// input events
			var inputs = el.find('li > input');
			inputs.bind('keyup input', function(){
				self.convert(this, inputs);
			});
			$('#custom-base').bind('keyup input', function () {
			    self.convert($('#input-custom')[0], inputs)
			})
			
			//var hash = document.location.hash.toString().replace('#', '');
			var hash = window.location.pathname.replace(/\/measure\/[a-z]+\/?/,'');
			
			if (hash) {
				var aHash = hash.split('/');
				if (aHash[0] && aHash.length) {
				    if (aHash[0] == 'custom')
				    {				        
				        if (aHash[2])
				        {
				            CustomNum.setBase(aHash[2]);				            
				            if (aHash[1])
    				            CustomNum.set(aHash[1]);
				        }				            
				            
				        this.selectType(items.filter(':last'));				        
				    }
					else if (aHash[0] && numberConverter[aHash[0]]) 
					{
						var type = aHash[0];
						if (aHash[1]) {
							if (type == 'roman') {
								aHash.shift();
								inputs.filter('#input-' + type).val(aHash.join(' '));
							} else 
								if (type == 'arabicindic') {
									var value = numberConverter.convert(aHash[1], 'arabic', type);
									inputs.filter('#input-' + type).val(value);
								} else {
									inputs.filter('#input-' + type).val(aHash[1]);
								}
						}
						this.selectType(items.filter('#type-' + type));
						items.filter('#type-' + type).find('input').focus();
					} else {
						this.selectType(items.eq(0));
						items.eq(0).find('input').focus();
					}
				}
			} else {
				var input = inputs.filter('#input-arabic, #input-decimal');
				if (input) {
					input.val('1');
					this.convert(input[0], inputs);
				}
			}
		}
		
		this.convert = function(input, inputs)
		{
			var num = $.trim(input.value), 
			    from = input.id.replace('input-', '');
			    
			inputs = inputs.filter('.fixed');
			
			if (num != '') 
			{
				inputs.parent().removeClass(options.error);
				
				if (from == 'custom')
    			{    			        
    			    inputs.each(function ()
    			    {
    			        this.value = CustomNum.get(this.id.replace('input-',''));
    			    }); 
    			    
    			    CustomNum.setResultLinks();    			    
    			    return;
    			}
				
				var self = numberConverter.convert(num, from, from);
				
				// value self check
				if (self !== false) {
					inputs.each(function(){
						var typeTo = this.id.replace('input-', '')
						if (typeTo != from) {
							var value = num == '' ? '' : numberConverter.convert(num, from, typeTo);
							// result value check
							if (value !== false) {
								this.value = value;
							} else {
								this.value = '';
							}
						}
					});
					
					if (CustomNum.$num.length)
    					CustomNum.set(
    					    num, 
    					    input.id.replace('input-', '')
    					);
					
					//this.setHash(self, from);
					this.setResultLinks(self, from);
					
					this.converts++;
					
					if(from == 'roman' && self != num) {
						if (self.indexOf(' ') != -1) {
							$(input).siblings('.' + options.popupError).show().html('Римское число<br /> в расширенном формате: ' + self);
						} else {
							$(input).siblings('.' + options.popupError).show().html('Это же римское число, <br />записанное по правилам: ' + self);
						} 
					} else {
						el.find('.' + options.popupError).hide();
					}
				} else {
					$(input).parent().addClass(options.error);
				}
			} else {			    
				inputs.parent().removeClass(options.error);
				this.setHash('', from);
				this.setResultLinks('', from);
				if(num == '') {
					inputs.val('');
				}
			}
		}
		
		this.selectType = function(item){
			var o = options;
			
			var items = item.siblings().andSelf();
			if (!item.hasClass(o.selected)) {
				items.removeClass(o.error);
				items.removeClass(o.selected);
				item.addClass(o.selected);
			}
			
			var input = item.find('input.num')[0];
			var value = input.value;
			var type = input.id.replace('input-', '');
			
			this.convert(input, items.find('input.fixed'));
		}
		
		this.setHash = function(num, type){
		    /*
			var hash = '#' + type;
			if (num !== false && num !== '') {
				if(type == 'arabicindic') {
					num = numberConverter.convert(num, type, 'arabic');
				}
				hash += '_' + num.toString().replace(/\s+/g, '_');
			}
			if(this.converts)
				document.location.hash = hash;
			*/
		}
		
		this.setResultLinks = function(num, type){
			var o = options;
			
			var rep = window.location.pathname.replace(/\/measure\/[a-z]+\/?/,''),
				ser = document.location.toString().replace(rep, ''), 
				url = ser + ((/\/$/).test(ser) ? '' : '/') + type;			
			
			var codeText = '';
			
			var resultLinks = $('#' + o.resultLinks);
			
			if (num !== false && num !== '') {
				var arabicNum = numberConverter.convert(num, type, 'arabic');
				
				url += '/';
				
				if(type != 'arabicindic') {
					url += num.toString().replace(/\s+/g, '_'); 
				} else {
					url += arabicNum.toString().replace(/\s+/g, '_');
				}
				
				if(o.converterType == 'hex') {
					codeText = arabicNum + ' '+s('in')+' ' + o.locale[type].prepositional + s('notation');
				} else {
					codeText = arabicNum + ' ' + o.locale[type].ablative + s('digits');
				}
			} else {
			    var base = o.locale[type] 
			        ? o.locale[type].genitive 
			        : CustomNum.getBaseName();
			        
				codeText = s('converter') + base + s('numbers');
			}
			
			resultLinks.find('#result-link-url').val(url);
			resultLinks.find('#result-link-code').val('<a href="' + url + '">' + codeText + '</a>');
		}

		this.init();
	}		
        
})(jQuery);

var CustomNum = 
{
    $num : $('#input-custom'),
    $base: $('#custom-base'),
    
    bases: {'binary':2, 'octal':8, 'decimal':10, 'hexadecimal':16},
    
    getSelf: function ()
    {
        return parseInt(this.$num.val(), this.$base.val()).toString(this.$base.val());
    },
    
    get: function (base)
    {
        var customNum = this.$num.val().replace(/\s/g, ''),
            customBase = this.$base.val().replace(/\s/g, '');
        
        if (customNum == '' || customBase == '' || customBase < 2) return '';
        
        return parseInt(customNum, customBase).toString(this.bases[base]);
    },
    set: function (num, base)
    {        
        var customBase = this.$base.val().replace(/\s/g, '');        
        if (customBase == '' || customBase < 2) return '';
        
        base = base ? this.bases[base] : customBase;
        
        this.$num.val(
            parseInt(num, base).toString(customBase)
        );
    },
    
    setBase: function (value)
    {
        if ((/^[0-9]+$/).test(value))
            this.$base.val(value);
    },
    
    getBaseName: function ()
    {
        return this.$base.val() + s('radix');
    },
    
    setResultLinks: function ()
    {
        if (this.$num.val() == '') return;
        
        var url = 'http://' + BaseServer + '/measure/hex/custom/' + this.$num.val() + '/' + this.$base.val(),        
            codeText = this.$num.val() + ' ' + s('in') + ' ' + this.$base.val() + s('radix system');
            
        $('#result-link-url').val(url);
        $('#result-link-code').val('<a href="' + url + '">' + codeText + '</a>');
    }
};

$(function () 
{
    CustomNum.$num  = $('#input-custom');
    CustomNum.$base = $('#custom-base');
})


var numberConverter = {
	convert: function(value, from, to){
		if (this.isNum(value, from) && this[from] && this[to]) {
			var num = this.prepare(value, from);
			return this[to].from(this[from].to(num));
		} else {
			return false;
		}
	},
	
	prepare: function(value, type){
		var num = value;
		
		switch (type) {
			case 'arabic':
			case 'decimal':
				num = value.toString().replace(/\s+/g, '');
				num = parseInt(num, 10);
				break;
				
			case 'binary':
			case 'octal':
			case 'hexadecimal':
				num = value.replace(/\s+/g, '');
				break;
				
			case 'roman':
				num = value.replace(/^\s+|\s+$/g, '').replace(/\s\s+/g, ' ');
				break;
				
			case 'arabicindic':
				num = value.replace(/\s/g, '');
				break;
		}
		
		return num;
	},
	
	isNum: function(value, type){
		var flag = true;
		
		switch (type) {
			case 'arabic':
			case 'decimal':
				var pattern = /^-?[\d\s]+$/;
				if (value.toString().search(pattern) == -1) 
					flag = false;
				break;
			
			case 'binary':
				var pattern = /^-?[01\s]+$/;
				if (value.search(pattern) == -1) 
					flag = false;
				break;
			
			case 'octal':
				var pattern = /^-?[01234567\s]+$/;
				if (value.search(pattern) == -1) 
					flag = false;
				break;
				
			case 'hexadecimal':
				var pattern = /^-?[\dABCDEF\s]+$/i;
				if (value.search(pattern) == -1) 
					flag = false;
				break;
				
			case 'roman':
				var pattern = /^[IVXLCDM\s]+$/i;
				if (value.search(pattern) == -1) {
					flag = false;
				} else {
					value = this.prepare(value, type);
					var arr = value.split(' ');
					// check extended roman number
					if (arr.length > 1) {
						for (var i in arr) {
							var num = this[type].to(arr[i]);
							if (num > 1000) {
								flag = false;
								break;
							}
						}
					}
				}
				break;
			
			case 'arabicindic':
				var pattern = /^-?[٠١٢٣٤٥٦٧٨٩\s]+$/;
				if (value.search(pattern) == -1) {
					flag = false;
				}
		}
		
		return flag;
	},
	
	/**
	 * to() means from selected type to arabic type number
	 *
	 * from() means from arabic type type to selected type number
	 */
	arabic: {
		to: function(arabic){
			return arabic;
		},
		
		from: function(num){
			return num;
		}
	},
	
	arabicindic: {
		eq: {
			'٠': 0,
			'١': 1,
			'٢': 2,
			'٣': 3,
			'٤': 4,
			'٥': 5,
			'٦': 6,
			'٧': 7,
			'٨': 8,
			'٩': 9,
			'-': '-'	
		},
		
		to: function(arabicindic){
			var num = '', aArabicindic = arabicindic.split('');
			for(var i in aArabicindic) {
				if(this.eq[aArabicindic[i]] != 'undefined') {
					num += this.eq[aArabicindic[i]];
				}
			}
			num = parseInt(num, 10);			
			return num;
		},
		
		from: function(num){
			var arabicindic = '', aNum = num.toString().split('');
			
			for(var i in aNum) {
				for(var j in this.eq){
					if(aNum[i] == this.eq[j]){
						arabicindic += j;
					}
				}
			}
			
			return arabicindic == '' ? false : arabicindic;
		}
	},
	
	roman: {
		romanTo: {
			I: 1,
			V: 5,
			X: 10,
			L: 50,
			C: 100,
			D: 500,
			M: 1000
		},
		
		to: function(roman){
			if (roman.indexOf(' ') != -1) {
				return this.extendedTo(roman);
			}
			var roman = roman.toUpperCase().split(''), num = 0, val = 0;
			while (roman.length) {
				val = this.romanTo[roman.shift()];
				num += val * (val < this.romanTo[roman[0]] ? -1 : 1);
			}
			return num;
		},
		
		extendedTo: function(exRoman){
			var longNum = exRoman.split(' '), num = 0;
			longNum.reverse();
			for (var i in longNum) {
				if (longNum[i] != 'M') {
					num += this.to(longNum[i]) * Math.pow(1000, i);
				}
			}
			return num;
		},
		
		romanFrom: {
			M: 1000,
			CM: 900,
			D: 500,
			CD: 400,
			C: 100,
			XC: 90,
			L: 50,
			XL: 40,
			X: 10,
			IX: 9,
			V: 5,
			IV: 4,
			I: 1
		},
		
		from: function(num){
			if (num > 3999) {
				return this.extendedFrom(num);
			}
			
			var roman = '', i;
			for (i in this.romanFrom) {
				while (num >= this.romanFrom[i]) {
					roman += i;
					num -= this.romanFrom[i];
				}
			}
			return roman === '' ? false : roman;
		},
		
		extendedFrom: function(exNum){
			var roman = '', i, longNum = [''], num = 0;
			
			var arr = exNum.toString().split('');
			arr.reverse();
			
			var k = 0;
			for (var j in arr) {
				longNum[k] = longNum[k] ? arr[j] + longNum[k] : arr[j];
				if (j != 0 && (j + 1) % 3 == 0) 
					k++;
			}
			longNum.reverse();
			
			for (var j in longNum) {
				num = parseInt(longNum[j], 10);
				if (j > 0) 
					roman += ' ';
				roman += num ? this.from(num) : 'M';
			}
			return roman;
		}
	},
	
	/**
	 * default function for base convertion
	 * 
	 * @param {string/integer} baseNum
	 * @param {integer} radix
	 */
	base: {
		to: function(baseNum, radix) {
			var num = parseInt(baseNum, radix)
			return num;
		},
		
		from: function(num, radix) {
			var baseNum = num.toString(radix);
			return baseNum;
		}
	},
	
	binary: {
		to: function(bin){
			var num = numberConverter.base.to(bin, 2);
			return num;
		},
		
		from: function(num){
			var bin = numberConverter.base.from(num, 2);
			return bin;
		}
	},
	
	octal: {
		to: function(oct){
			var num = numberConverter.base.to(oct, 8);
			return num;
		},
		
		from: function(num){
			var oct = numberConverter.base.from(num, 8);
			return oct;
		}
	},
	
	decimal: {
		to: function(dec){
			var num = numberConverter.base.to(dec, 10);
			return num;
		},
		
		from: function(num){
			var dec = numberConverter.base.from(num, 10);
			return dec;
		}
	},
	
	hexadecimal: {
		to: function(hex){
			var num = numberConverter.base.to(hex, 16);
			return num;
		},
		
		from: function(num){
			var hex = numberConverter.base.from(num, 16).toUpperCase();
			return hex;
		}
	}
}
