// элемент списка
var suggItem = Class.create();
suggItem.prototype = {
    initialize: function(params) {
        Object.extend(this, params);
        this.selected = false;
        this.currentTemplate = false;
    },

    // применить шаблон и заполнить элемент списка данными
    write: function(tplName, rewrite) {
        var that = this;
        if ((this.currentTemplate == tplName) && (!rewrite))
            return;
        if (!tplName)
            tplName = this.currentTemplate;
        var tplt = "";
        if (this.data.templates) {
            tplt = this.list.itemTemplates[this.data.templates[tplName]];
        } else {
            tplt = this.list.itemTemplates[tplName];
        }
        var tempData = Object.extend({}, this.data);
        if (this.list.highlights) {
            this.list.highlights.each(function(field) {
                tempData[field] = tempData[field].replace(new RegExp(that.list.searchText,'i'), '<b>'+that.list.searchText+'</b>');
            });
        }
        var tpl = new Template(tplt);
        var html = tpl.evaluate(tempData).toLowerCase();
        if (!this.element) {
            new Insertion.Bottom(this.list.suggList, html);
            this.element = this.list.suggList.lastChild;

        } else {
            new Insertion.After(this.element, html);
            var element = this.element.nextSibling;
            this.list.suggList.removeChild(this.element);
            this.element = element;
        }
        $(this.element).show();
        Event.observe(this.element, 'click', this.select.bindAsEventListener(this), false);
        this.currentTemplate = tplName;
    },

    // выбор элемента списка
    select: function(e) {
        var that = this;
        // сначала убрать выбор(выделение) со всех элементов списка
        this.list.unselectAll();
        this.selected = true;
        this.list.selectedItemId = this.id;
        this.write('select');
        this.list.searchInput.value = this.data.suggest.stripTags();
        this.list.searchInput.focus();
    }
};


// редактируемый список с навигацией
var suggests = Class.create();
suggests.prototype = {
    initialize: function(params) {
        Object.extend(this, params);
        // инициализация
        window[this.name] = this;
        this.suggHolder = $(this.name);
        if (!this.suggHolder)
        	return;
        this.suggHolder.style.position = 'absolute';
        this.suggList = $(this.name+'List');
        this.searchInput = $(this.searchInput);
        this.itemTemplates ={};
        this.searchText = "";
        this.getTemplates();
        Event.observe(document, 'keydown', this.keydown.bindAsEventListener(this), false);
        Event.observe(this.searchInput, 'keyup', this.searchChange.bindAsEventListener(this), false);
        Event.observe(document, 'keypress', this.escHide.bindAsEventListener(this), false);
        Event.observe(document, 'mousedown', this.outsideClickHide.bindAsEventListener(this));
    },
    // вставить новый элемент в список
    insertItem: function(itemData, mode) {
        var item = new suggItem({
            id        : itemData.id,
            data      : itemData,
            list      : this
        });
        item.data.num = $H(this.suggItems).size() + 1;
        item.write(mode);
        this.suggItems[itemData.id] = item;
        return this.suggItems[itemData.id];
    },

    // получаем шаблоны строк
    getTemplates: function() {
        var that = this;
        var descendants = this.suggList.descendants();
        descendants.each(function(el) {
            ['normal', 'select'].each(function(tplTitle) {
                if (el.id == tplTitle) {
                    var tempEl = document.createElement(el.parentNode.tagName);
                    tempEl.appendChild(el);
                    var html = tempEl.innerHTML.replace(/#%7B([a-zA-Z0-9_-]+)%7D/gi, "#{$1}");
                    that.itemTemplates[tplTitle] = html;
                    delete tempEl;
                }
            });
        });
    },

    searchChange: function(e) {
        if (this.searchInput.value.length < 1) {
            this.suggHolder.style.display = 'none';
            return;
        }
        if (e.keyCode == Event.KEY_RETURN)
            return;
        if ((e.keyCode == Event.KEY_UP || e.keyCode == Event.KEY_DOWN) && this.suggHolder.visible()) {
            return;
        }
        if ((this.searchText == this.searchInput.value) && this.suggHolder.visible())
            return;
        this.searchText = this.searchInput.value;
        this.getList({searchText:this.searchText});
    },

    // очистка списка
    clearList: function() {
        $H(this.suggItems).each(function(item) {
            item.value.element.remove();
        });
        this.suggItems = {};
        this.selectedItemId = 0;
    },

    // убрать выбор(выделение) со всех элементов списка
    unselectAll: function() {
        $H(this.suggItems).each(function(item) {
           item.value.selected = false;
           item.value.write('normal');
        });
        this.selectedItemId = 0;
    },

    // загрузить список
    getList: function(params) {
        var that = this;
        // прибить все незавершенные запросы
    	if (Ajax.activeRequestCount > 0) {
    		Ajax.Responders.responders.each(function(resp, key) {
    			if ((resp.options) && (resp.options.name) && (resp.options.name == 'suggests'))
    				resp.transport.abort();
            });
    	}
        // новый запрос
        var req = new Ajax.Request(this.backendScript, {
        	name: 'suggests',
            method: 'post',
            parameters: params,
            onFailure: function(tr) {
                //alert('Ошибка передачи данных');
            },
            onSuccess: function(tr) {
                var data = $H(tr.responseJS.data);
                that.clearList();
                var i = 1;
                data.each(function(itemData) {
                    itemData.value.num = i;
                    that.insertItem(itemData.value, 'normal');
                    i++;
                });
                if ((i == 1) || (that.searchInput.value.length < 1))
                    that.suggHolder.style.display = 'none';
                else {
                    that.suggHolder.style.display = 'block';
                    that.setPosition();
                }
                Ajax.Responders.unregister(req);
           }
        });
        Ajax.Responders.register(req);
    },

    keydown: function(e) {
        if (!(e.keyCode && (e.keyCode == Event.KEY_UP || e.keyCode == Event.KEY_DOWN || e.keyCode == Event.KEY_RETURN)) || !this.suggList.visible())
            return;
        if (!this.selectedItemId && this.suggList.visible() && (!(e.keyCode == Event.KEY_UP || e.keyCode == Event.KEY_DOWN)))
            return;

        Event.stop(e);
        if (this.onEnterScript && (e.keyCode == Event.KEY_RETURN) && (this.selectedItemId) && (this.selectedItemId) && this.suggHolder.visible()) {
            var func = this.onEnterScript;
            var data = this.suggItems[this.selectedItemId].data;
            func = func.replace(/#\{data\}/g, Object.toJSON(data));
            //this.suggHolder.style.display = 'none';
            eval(func);
            return;
        }
        // up and down
        var that = this;
        var nav = new Array();
        var curr;
        var i = 0;
        $H(this.suggItems).each(function(item) {
            if (item.key == that.selectedItemId)
                curr = i;
            nav[i]= item.key;
            i++;
        });
        prev = nav[curr-1];
        next = nav[curr+1];
        if (!prev)
            prev = nav[nav.length-1];
        if (!next)
            next = nav[0];


        if ((e.keyCode == Event.KEY_UP) && (prev)) {
            if (curr == 0) {
                this.searchInput.value = this.searchText;
                this.unselectAll();
                return;
            }
            this.suggItems[prev].select();
        }
        if ((e.keyCode == Event.KEY_DOWN) && (next)) {
            if (curr == (nav.length-1)) {
                this.searchInput.value = this.searchText;
                this.unselectAll();
                return;
            }
            this.suggItems[next].select();
        }

    },

    // меняем расположение слоя
    setPosition: function() {
        var callerPos = Position.page(this.searchInput);
        // x и y координата вызывающего элемента относительно видимой области окна
        var cX = callerPos[0], cY = callerPos[1];
        if ((cX == 0) && (cY == 0))
            return;
        // ширина и высота вызывающего элемента
        var cW = this.searchInput.getWidth(), cH = this.searchInput.getHeight();
        // координаты левого верхнего угла вызывающего элемента
        var callerTopLeft = {x:cX, y:cY};
        // координаты левого нижнего угла вызывающего элемента
        var callerBottomLeft = {x:cX, y:(cY + cH)};
        // ширина и высота окна
        var winSize = Position.getWindowSize();
        var winW = winSize.width, winH = winSize.height;
        // ширина и высота подсказки
        var dim = this.suggHolder.getDimensions();
        var hintW = dim.width, hintH = dim.height;
        // сдвигать вправо/влево, если выходит за границы окна
        var offsetLeft = 0;
        if (cX < 0) {
            offsetLeft = Math.abs(cX);
            if (offsetLeft > cW)
                offsetLeft = cW;
        }
        if ((cX + hintW) > winW) {
            offsetLeft = winW - (cX + hintW)-2;
            if (cX > winW)
                offsetLeft = -hintW-2;
        }
        Position.clone(this.searchInput, this.suggHolder, {setWidth: false, setHeight: false, offsetTop: cH, offsetLeft: offsetLeft});
    },

    // добавить сhildElement, при клике на таких элементах hint не будет закрываться
    addChildElement: function(element) {
        var ind = $H(this.childElements).size();
        this.childElements[ind] = element;
    },

    // закрыть при нажатии клавиши ESC
    escHide: function(e) {
        if (e.keyCode && (e.keyCode == Event.KEY_ESC)) {
            this.suggHolder.style.display = 'none';
        };
    },

    within: function(e) {
        var target = Event.element(e);
        if ((target.descendantOf(this.suggHolder)) || (target == this.searchInput))
            return true;
        return false;
    },

    // закрываем всплывающее окно, если кликнули за его пределами
    outsideClickHide: function(e) {
        if (!this.suggHolder.visible())
            return;
        if (!this.within(e))
            this.suggHolder.style.display = 'none';
    }
};
