(function($) {
    $.fn.location_inline = function(o) {
        return this.each(function() {
            new $location($(this), o);
        });
    };

    // Default configuration properties.
    var defaults = {
		'active_level' : [0,2,1,0,1,0,1],
		'level_type' : [
			[141],
			[140],
			[1,3,4,5,136,139],
			[6],
			[2,10,24,14,26,42,65,104],
			[14,26,42,65,104],
			[61,83,91,93,98,111,112,113,116]
		],
		'level_type_suggest' : [
			[141],
			[140],
			[1,3,4,5,136,139],
			[6],
			[2,10,24,14,26,42,65,104],
			[14,26,42,65,104],
			[61,83,91,93,98,111,112,113,116]
		],
		'important' : [0,0,0,0,1,0,0],
		'suggest_limit' : 20,
		'search_limit' : 50,
		'limit' : [null,null,null,null,null,150,150],
		'select_text' : [
			'- Выберите континент -',
			'- Выберите страну -',
			'- Выберите область -',
			'- Выберите район -',
			'- Выберите город -',
			'- Выберите населенный пункт -',
			'- Выберите улицу -'
		],
		'select_tree' : [0,0,0,0,0,0,0],
		'suggest_text' : [
			'Название континента...',
			'Название страны...',
			'Название области...',
			'Название района...',
			'Название города...',
			'Название населенного пункта...',
			'Название улицы...'
		],
		'link_text' : [
			'изменить континент',
			'изменить страну',
			'изменить регион',
			'изменить район',
			'изменить город',
			'изменить поселок',
			'изменить улицу'
		],
		'link_other_text' : [
			'- Другой -',
			'- Другая -',
			'- Другой -',
			'- Другой -',
			'- Другой -',
			'- Другой -',
			'- Другая -'
		],
		'use_link_text' : [0,0,0,0,0,0,0],
		'brake' : ' <em class="brake">»</em> ',

		'code': '',
		'level': 0,

		'brake' : ' <em class="brake">»</em> ',
		'house' : 'house',

		'onSelect' : false,
		'onChangeLocation' : false,
		'onChangeCode' : false
	};

    /**
     * The jcmcarousel object.
     *
     * @constructor
     * @name $.jcmcarousel
     * @param Object e The element to create the carousel for.
     * @param Hash o A set of key/value pairs to set as configuration properties.
     * @cat Plugins/jcmcarousel
     */
    $.location_inline = function(e, o) {

		var self = this;
		this.options    = $.extend({}, defaults, o || {});

		if (typeof this.options.code != 'string') {
			this.options.code = this.options.code.toString().replace(/,/g,'').replace(/\?/g,'0');
		}

		this.container = e;
		this.house = $('.'+this.options.house);
		this.code = '';
		this.cache = {};

		if ( typeof(this.options.onSelect) == "string" && this.options.onSelect != "" )
			this.onSelect = new Function("parent", "level", this.options.onSelect);

		if ( typeof(this.options.onChangeLocation) == "string" && this.options.onChangeLocation != "" )
			this.onChangeLocation = new Function("parent", "level", this.options.onChangeLocation);

		if ( typeof(this.options.onChangeCode) == "string" && this.options.onChangeCode != "" )
			this.onChangeCode = new Function("params", this.options.onChangeCode);

		jQuery.each(this.options.level_type, function(i, v) {
			if (typeof v != 'array')
				self.options.level_type[i] = v.toString();
		});
		
		jQuery.each(this.options.level_type_suggest, function(i, v) {
			if (typeof v != 'array')
				self.options.level_type_suggest[i] = v.toString();
		});		

		this.restoreEnv();
		
		this.container.each(function() {
			this.getObjectLevel = function() {
				return self.objectLevel(self.code);
			}
		});
    };

    // Create shortcut for internal use
    var $location = $.location_inline;

    $location.fn = $location.prototype = {
        location_inline: '0.0.1'
    };

    $location.fn.extend = $location.extend = $.extend;

    $location.fn.extend({
        /**
         * Setups the carousel.
         *
         * @name setup
         * @type undefined
         * @cat Plugins/jcmcarousel
         */
        restoreEnv: function() {

			this.setCode(this.options.code);

			var isLink = false;
			var self = this;
			$('[class*="location-inline"]', this.container).each(function() {
				this.isLevel = self.isLevel(this.className);
			});

			$('[class*="location-inline-s"]', this.container).each(function(i, v) {
				self.registerSuggest($(v), self.stringCode(6), v.isLevel);
				v.value = self.options.suggest_text[v.isLevel];

				$(v).one('focus', function() {
					this.value = '';
				});
			});
			
			$('[class*="location-inline-l"]', this.container).each(function(i, v) {
				var link = $(this);
				var class_ = link.attr('class').match(/(location-inline-l-\d+)/g);

				self.registerLink($(this), self.stringCode(self.prevLevel(v.isLevel)));
			});
			
			$('[class*="location-inline-o"]', this.container).each(function(i, v) {

				$(v).bind('change', function() {

					if ($location.intval(this.value) != -1) {

						var option = $(this.options[this.selectedIndex]);
						
						var link = $(this);
						//var class_ = link.attr('class').match(/(location-inline-o-\d+)/g);

						return self.changeLocation({
							code: this.value,
							name: this.options[this.selectedIndex].text,
							level: v.isLevel,
							parent: self.stringCode(self.prevLevel(v.isLevel)),
							type: option.attr('_type')
						});
					}

					self.createSuggest(self.stringCode(v.isLevel), v.isLevel);
				});
			});
        },

		registerLink: function(link, parent) {
			var self = this;
			
			if (parent == undefined)
				parent = '';

			link.one('click', function() {
				var container = $('.location-inline-c-'+this.isLevel, self.container);
				$('~ *', container).remove();
				link.remove();

				if (self.house.size()) {
					self.house.css('display', 'none');

					$('input', self.house).each(function() {
						this.value = '';
					});
				}

				self.setCode(parent);
				
				self.setupList(container, this.isLevel);

			});
		},

		createSuggest: function(code, level) {
			var container = $('.location-inline-c-'+level, this.container);
			var suggest = $('<input type="text"/>');
			suggest
				.addClass('suggest').addClass('location-inline-s-'+level);

			this.registerSuggest(suggest, code, level);
			suggest.val(this.options.suggest_text[level]).one('focus', function() {
				this.value = '';
			});

			$('*', container).remove();
			container.append(suggest);
		},

		registerSuggest: function(suggest, code, level) {

			var self = this;

			/*var o_code = this.objectCode(code);
			
			for(var i=0; i<7; i++) {
				if (this.options.active_level[i] != 4)
					continue ;
				o_code[i] = (i!=6?'???':'????');				
			}*/

			suggest.val('');
			suggest.autocomplete("/service/source/db.location_inline", {
				dataType: 'json',
				extraParams: {
					parent: code,
					level: level,
					type_in: this.options.level_type_suggest[level],
					limit: this.options.suggest_limit,
					type: 'query'
				},
				parse: function(json) {
					var parsed = [];

					if ( !json.list || !json.list.length )
						return parsed;

					for (var i in json.list) {
						if ( typeof json.list[i] != 'object' )
							continue;
						
						var Desc = '';
						if ( json.list[i].parent && json.list[i].parent.length ) {
							Desc = '<div class="ac_describe">';
							for (var j in json.list[i].parent)
							{
								if ( typeof json.list[i].parent[j] == 'string' )
									Desc += json.list[i].parent[j] + '<br />';
							}
							Desc += '</div>';
						}
												
						parsed[parsed.length] = {
							data: json.list[i].name + Desc,
							value: json.list[i]
						};
					}

					return parsed;
				},
				formatItem: function(text, i, max, value) {
					return text;
				},
				blurClear: true,
				returnFocus: false,
				max: this.options.suggest_limit
			}).result(function(event, name, value) {
				return self.changeLocation({
					name: name.replace(/<.*>/, ''),
					parent: code,
					code: value.id,
					type: value.type,
					level: level
				});
			});
		},

		setupList: function(container, level) {
			this.showLoader(container);
			var self = this;
			var parent = this.stringCode(level);

			if (typeof this.cache[level+'_'+parent] == 'object')
				return self.loadListSuccess(this.cache[level+'_'+parent], parent, container, level);

			$.ajax({
				mode: 'abort',
				port: 'list',
				type: 'POST',
				url: '/service/source/db.location_inline',
				dataType: 'json',
				data: {
					level: level,
					parent: parent,
					important: this.options.important[level],
					limit: this.options.limit[level],
					type_in: this.options.level_type[level],
					type: 'subordinate_objects',
					tree: this.options.select_tree[level]
				},

				error: function() {
					if (confirm("Не удалось получить список.\nПовторить?"))
						self.setupList(container, level);
				},

				success: function(json) {
					self.loadListSuccess(json, parent, container, level);
				}
			});
		},

		loadListSuccess: function(json, parent, container, level) {
			var self = this;
			var limit = $location.intval(this.options.limit[level]);
			var search_limit = $location.intval(this.options.search_limit);

			//if (!limit || json.count <= limit) {
			if (!limit || limit) {

				var list = $('<select/>');
				list
					.addClass('list').addClass('location-inline-o-'+level);

				list.bind('change', function() {

					if ($location.intval(this.value) != -1) {

						var option = $(this.options[this.selectedIndex]);

						return self.changeLocation({
							code: this.value,
							parent: parent,
							name: this.options[this.selectedIndex].text,
							level: level,
							type: option.attr('_type')
						});
					}

					self.createSuggest(parent, level);
				});

				list.append($('<option/>').attr({value: undefined}).html(this.options.select_text[level]));

				if (this.options.important[level]) {
					list.append($('<option></option>').val(-1).html( self.options.select_other_text[level] ).css('font-weight','bold'));
				}

				var group = null;
				var _type = 0;
				
				jQuery.each(json.list, function(i, v) {
					if ( self.options.select_tree[level] && v.type != _type ) {
						group = $('<optgroup></optgroup>').attr({ label: json.types[parseInt(v.type)].SocrText });
						list.append(group);
						_type = v.type;
					}
					
					var option = $('<option></option>').attr({
							value: v.id,
							_type: v.type
						}).html(v.name);
					
					if ( self.options.select_tree[level] ) {
						group.append(option);
					} else {
						list.append(option);
					}
				});				
								
				if (this.options.important[level]) {
					list.append($('<option></option>').val(-1).html( self.options.select_other_text[level] ).css('font-weight','bold'));
				}

				container.append(list);

				if (this.cache[level+'_'+parent] == undefined)
					this.cache[level+'_'+parent] = json;
			} else
				this.createSuggest(parent, level);

			//this.setCode(parent);

			if ( typeof(self.onSelect) == "function" ) {
				try {
					self.onSelect(parent,level);
				} catch(e) {}
			}

			this.removeLoader();
		},

		changeLocation: function(params) {

			var container = $('.location-inline-c-'+params.level, this.container);
			
			if ( this.options.use_link_text[params.level] ) {
				var link = $('<span></span>')
					.addClass('location-inline-l-'+params.level)
					.html('<span class="link-title">'+params.name+'</span>&nbsp;');
				
				var link_href = $('<a href=""></a>')
					.attr({href: 'javascript:;'})
					.addClass('subscript')
					.html(this.options.link_text[params.level]);
				
				link.append(link_href);
			} else {
				var link = $('<a href=""></a>').attr({
					href: 'javascript:;',
					title: params.name
				})
				.addClass('link').addClass('location-inline-l-'+params.level).html(params.name);
			}
			
			link.get(0).isLevel = params.level;

			$('*', container).remove();

			container.append(link);
			this.registerLink(link, params.parent);
			this.setCode(params.code);

			//if (params.type == 2)
				//params.level = 4;

			level = false;
			if (this.objectLevel(this.objectCode(params.code)) != 7)
				level = this.nextLevel(params.level);

			if (level == false) {
				if (this.house.size()) {
					this.house.css('display', '');

					$('input', this.house).each(function() {
						this.value = '';
					});
				}

				if ( typeof(this.onChangeLocation) == "function" ) {
					try {
						this.onChangeLocation(params.parent,level);
					} catch(e) {}
				}

				return ;
			}

			this.house.css('display', 'none');
			$('input', this.house).each(function() {
				this.value = '';
			});

			this.container.append(this.options.brake);

			var container = $('.location-inline-c-'+level, this.container);

			if (container.size() == 0) {			
				container = $('<span></span>')
					.addClass('container').addClass('location-inline-c-'+level);
				this.container.append(container);
			}

			if ( typeof(this.onChangeLocation) == "function" ) {
				//try {
					this.onChangeLocation(params.parent,level);
				//} catch(e) {}
			}

			this.setupList(container, level);
		},

		nextLevel: function(level) {

			for(level+=1;this.options.active_level[level] !== undefined;level++) {
				if (this.options.active_level[level] !== 0 && this.options.active_level[level] !== 4)
					return level;
			}
			return false;
		},

		prevLevel: function(level) {

			for(level-=1;this.options.active_level[level] !== undefined;level--) {
				if (this.options.active_level[level] !== 0 && this.options.active_level[level] !== 4) {
					return level+1;
				}
			}
			return level--;
		},

		showLoader: function(container) {

			this.removeLoader();

			var loader = $('<input type="text" />');
			loader.attr({
				readonly: true, disabled: true,
				value: 'Загрузка...'
			}).addClass('loader');

			container.append(loader);
		},

		removeLoader: function() {
			$('.loader', this.container).remove();
		},

		isLevel: function(cl) {
			return parseInt(cl.replace(
				new RegExp("location-inline-(s|l|c|o)-(\\d)"), '$2'));
		},

		setCode: function(code) {
			var input = $('.location-inline-v', this.container);

			if (typeof code != 'string') {
				input.val(code.toString().replace(/,/g,'').replace(/\?/g,'0'));

				this.code = code;
				code = this.stringCode(7);
			} else {

				input.val(code.replace(/\?/g,'0'));
				this.code = this.objectCode(code);
			}

			if ( typeof(this.onChangeCode) == "function" ) {
				try {
					this.onChangeCode({
						'code': input.val(),
						'level': this.objectLevel(this.code)
					});
				} catch(e) {}
			}

			return code;
		},

		objectLevel: function(code) {
			if ( code[6] !== undefined && code[6] !== '0000' )
				return 7;

			if ( code[5] !== undefined && code[5] !== '000' )
				return 6

			if ( code[4] !== undefined && code[4] !== '000' )
				return 5;

			if ( code[3] !== undefined && code[3] !== '000' )
				return 4;

			if ( code[2] !== undefined && code[2] !== '000' )
				return 3;

			if ( code[1] !== undefined && code[1] !== '000' )
				return 2;

			if ( code[0] !== undefined && code[0] !== '000' )
				return 1;

			return 0;
		},

		objectCode: function(code) {
			return new Array(
				code.substr(0, 3) ? code.substr(0, 3) : '000',
				code.substr(3, 3) ? code.substr(3, 3) : '000',
				code.substr(6, 3) ? code.substr(6, 3) : '000',
				code.substr(9, 3) ? code.substr(9, 3) : '000',
				code.substr(12, 3) ? code.substr(12, 3) : '000',
				code.substr(15, 3) ? code.substr(15, 3) : '000',
				code.substr(18, 4) ? code.substr(18, 4) : '0000'
			);
		},

		stringCode: function(level) {

			var self = this;
			var codeString = '';
			var code = this.code.slice(0, level);
			var current_level = this.objectLevel(this.code);

			//alert(code+' - '+);
			//alert(level+' - '+current_level+' - '+self.options.active_level[current_level]);
			jQuery.each(this.code, function(i) {

				if (code[i] === undefined || 
					(i == 3 && level != 6 && self.options.active_level[3]==0 && self.code[4] == '000')
				)// || i > level-2)
					codeString += (i!=6?'000':'0000');
				//else if ($location.intval(self.options.active_level[i]) == 0)
					//codeString += (i!=6?'???':'????');
				else
					codeString += code[i];

				if (level == i && ($location.intval(self.options.active_level[i+1]) === 3 || $location.intval(self.options.active_level[i+1]) === 4)) {
					codeString =
						codeString.substr(0, ((i+1)*3)).replace(/./g, '?');
				}
			});
			return codeString;
		}


    });

    $location.extend({
        defaults: function(d) {
            return $.extend(defaults, d || {});
        },

        margin: function(e, p) {
            if (!e)
                return 0;

            var el = e.jquery != undefined ? e[0] : e;

            if (p == 'marginRight' && $.browser.safari) {
                var old = {'display': 'block', 'float': 'none', 'width': 'auto'}, oWidth, oWidth2;

                $.swap(el, old, function() { oWidth = el.offsetWidth; });

                old['marginRight'] = 0;
                $.swap(el, old, function() { oWidth2 = el.offsetWidth; });

                return oWidth2 - oWidth;
            }

            return $jcm.intval($.css(el, p));
        },

        intval: function(v) {
            v = parseInt(v);
            return isNaN(v) ? 0 : v;
        }
    });

})(jQuery);
