/**
 *
 * @param options
 */
var KEY = {
    TAB     : 9,
    ESC     : 27,
    ENTER   : 13,
    SPACE   : 32,
    LEFT    : 37,
    UP      : 38,
    RIGHT   : 39,
    DOWN    : 40,
    BACKSPACE :  8,
    DEL     : 46,
    DIGIT_0 : 48,
    DIGIT_9 : 57,
    ALPHA_A : 65,
    ALPHA_Z : 90,
    HOME    : 36,
    END     : 35,
    INSERT  : 45,
    SHIFT   : 16,
    PG_UP   : 33,
    PG_DOWN : 34,
    CTRL    :  17,
    ALT     :  18,

    DOT     : 190,
    COMA    : 188,

    isControlKey : function ( keyCode){
        switch( keyCode){
            case KEY.ESC:
            case KEY.ENTER:
            case KEY.LEFT:
            case KEY.RIGHT:
            case KEY.UP:
            case KEY.DOWN:
            case KEY.DEL:
            case KEY.BACKSPACE:
            case KEY.HOME:
            case KEY.END:
            case KEY.PG_UP:
            case KEY.PG_DOWN:
                return true;
        }
        return false;
    },

    isAlpphaKey : function (keyCode){
        return KEY.ALPHA_A <= keyCode && keyCode <= KEY.ALPHA_Z || keyCode == KEY.SPACE;
    },

    isDigitKey : function (keyCode){
        return KEY.DIGIT_0 <= keyCode && keyCode <=KEY.DIGIT_9;
    }
}

var SugestUtils = {
    getResponseMesg : function (respPage) {
        if( respPage){
            return respPage.substring(respPage.indexOf("<respmesg>") + 10,
                    respPage.indexOf("</respmesg>"));
        }
        return "";
    }
}

function DataProvider( options){
    Object.extend( this, options || {});
}

DataProvider.prototype = {
    /**
     * Functie apelata de Componenta suggest
     * @param queryConfig parametri de interogare
     * @param callBackFunction functia callback apelata la disponibilitatea datelor
     */
    getData : function (queryConfig, callBackFunction){
        return [];
    }
}

function AjaxDataProvider( getUrlFunction){
    this.getUrl = getUrlFunction;
}

AjaxDataProvider.prototype = {
    /**
     * 
     * @param queryConfig
     * @param callbackFunction
     */
    getData : function ( queryConfig, callbackFunction){
        this.callbackFunction = callbackFunction;
        var url = this.buildUrl( queryConfig);
        Logger.log( url);
        if( url) {
            new Ajax.Request(
                url, {
                    method: "post",
                    parameters: "",
                    onComplete: this.onCompleteRequest.bind(this)
                }
            );
        }else {
            callbackFunction([]);
        }
    },

    onCompleteRequest : function ( originalRequest){
        var resultObject = null;
        Logger.log("AjaxDataProvider.onCompleteRequest response:" + this.getResponseMesg( originalRequest.responseText));
        try{
            resultObject = eval( "(" + this.getResponseMesg( originalRequest.responseText) + ")");
        }catch(e){
            Logger.log("AjaxDataProvider.onComplete error on eval " + e.message);
        }
        this.callbackFunction( resultObject);
    },

    getResponseMesg : function (respPage) {
        if( respPage){
            return respPage.substring(respPage.indexOf("<respmesg>") + 10,
                    respPage.indexOf("</respmesg>"));
        }
        return "";
    },

    getUrl : function(){},

    buildUrl : function( queryConfig){
        var url = this.getUrl();

        return url ? url + queryConfig.q +
               "&"+InputSugestConstants.REQUEST_BEGIN_INDEX + "="+ queryConfig.beginIndex +
               "&"+InputSugestConstants.REQUEST_MAX_COUNT   + "="+ queryConfig.maxCount : null;
    }
}


var InputSugestConstants = {
    TYPING_TIMEOUT : 500,
    SELECT_EVENT :  "onSelect",
    ie : ((navigator.userAgent.toLowerCase().indexOf("msie") != -1)
            && (navigator.userAgent.toLowerCase().indexOf("opera") == -1)),
    highlightHandler : function() {
        this.highlight( Event.element(arguments[0]).getAttribute("aa_index"));
    },

    containerStyle: "aa",
	highlightStyle: "aa_highlight",
	waitingStyle: "aa_wait",
	newValueStyle: "aa_new_value",

    REQUEST_BEGIN_INDEX : "SUGEST_Q_BEGIN_INDEX",
    REQUEST_MAX_COUNT   : "SUGEST_Q_MAX_COUNT",
    INITIAL_BEGIN_INDEX : 0,
    MAX_COUNT : 10,
    NO_SELECT_KEY_VALUE : -1
}

function InputSugest( valueTxtBox, keyTxtBox, dataProvider, options){
    this.txtBox = $( valueTxtBox);
    this.keyTxtBox = $( keyTxtBox);
    this.whenNoSelecteKeyValue = InputSugestConstants.NO_SELECT_KEY_VALUE;
    this.dataProvider = dataProvider;
    this.typingTimeout = InputSugestConstants.TYPING_TIMEOUT;
    this.beginIndex = InputSugestConstants.INITIAL_BEGIN_INDEX;
    this.maxCount = InputSugestConstants.MAX_COUNT;

    this.totalCount = 0;

    Object.extend( this, options || {});
    this.init();
}

InputSugest.prototype = {
    init : function (){
        if (this.txtBox == null) {
            return;
        }
        this.constant = InputSugestConstants;
        this.txtBox.setAttribute("autocomplete", "off");
        this.txtBox.onkeydown = this.onkeydown.bindAsEventListener(this);


        if (this.constant.ie) {
            this.iframe = document.createElement("iframe");
            this.iframe.style.position = "absolute";
            this.iframe.style.margin = "1px 0 0 0";
            this.iframe.style.padding = "0";
            Element.hide(this.iframe);
            document.body.appendChild(this.iframe);
        }

        this.bufferDiv = document.createElement("div");

        this.floatDiv = document.createElement("div");
        this.floatDiv.style.position = "absolute";

        Element.addClassName(this.floatDiv, this.constant.containerStyle);
        Element.hide(this.floatDiv);
        document.body.appendChild(this.floatDiv);

        this.currentNodeIndex = -1;
        this.size = -1;

        Event.observe( document, "click", this.hide.bindAsEventListener(this));
        Event.observe( this.floatDiv, "click", this.show.bindAsEventListener(this));
//        Event.observe( this.txtBox, "dblclick", this.getData.bindAsEventListener(this));
    },

    onkeydown: function(evt) {
        Logger.log("onkeydown");
        switch (evt.keyCode) {
            case KEY.TAB:
            case KEY.END:
            case KEY.INSERT:
            case KEY.SHIFT:
            case KEY.LEFT:
                this.left();
                return;
            case KEY.RIGHT:
                this.right();
                return;
            case KEY.CTRL:
            case KEY.ALT:
            case KEY.PG_UP:
            case KEY.PG_DOWN:
                return;
            case KEY.ESC:
                this.hide();
                return;
            case KEY.UP:
                this.up();
                this.show();
                return;
            case KEY.DOWN:
                if( this.visible){
                    this.down();
                    this.show();
                }else{
                    this.doSugest( evt);
                }
                return;
            case KEY.ENTER:
                if (this.visible) {
                    this.select();
                }
                return false;
            default:
                this.doSugest( evt);
        }
    },

    doSugest : function( evt){
        this.log("evt.keyCode:" + evt.keyCode);
        if (this.timeoutId != 0) {
            clearTimeout(this.timeoutId);
        }
        this.timeoutId = setTimeout(this.requestData.bind(this), this.typingTimeout);
        this.hide();
    },

    previewSearchInput : "",

    isDiferentSearchInput : function(){
        var is = this.previewSearchInput != this.txtBox.value;
        if( is) this.previewSearchInput = this.txtBox.value; 
        return is;
    },

    requestData : function (){
        this.log("requestData");
//        if( (this.isDiferentSearchInput() || this.leftRightPressed) && this.isValidSearchInput()){
        if( this.isValidSearchInput()){
            this.log("valid search input");

            this.onLoading();
            this.setKeyValue( InputSugestConstants.NO_SELECT_KEY_VALUE);
            if( !this.leftRightPressed){
                this.beginIndex = InputSugestConstants.INITIAL_BEGIN_INDEX;
            }

            this.leftRightPressed = false;
            this.dataProvider.getData({
                    q : this.txtBox.value,
                    beginIndex : this.beginIndex,
                    maxCount : this.maxCount
                },
                this.requestDataComplete.bind(this)
            );
        }else{
            this.log("invalid search input");
        }
    },

    requestDataComplete : function ( responseObject){
        this.log("InputSugest.requestDataComplete: " + responseObject);

        if( !responseObject || responseObject == "undefinde"){
            this.log("InputSugest EROR : responseObject is undefined");
            this.noDataAvalilable();
            return;
        }

        try{
            this.list = responseObject.list;
        }catch (e){  this.list = null;}

        if( this.list == null){
            this.log("InputSugest EROR : responseObject.list is undefined");
            this.noDataAvalilable();
            return;
        }

        if( this.list.length  == 0){
            this.noDataAvalilable();
            return;
        }

        this.responseObject = responseObject;
        this.updateContent( this.list);
    },

    noDataAvalilable : function(){
        this.cleanup();
        this.setKeyValue( InputSugestConstants.NO_SELECT_KEY_VALUE);
        Element.addClassName( this.txtBox, this.constant.newValueStyle);
        
        //notificam userul
        this.onNoData();
    },

    setKeyValue : function( value){
        if( !value || value == 'undefined'){
            value = this.whenNoSelecteKeyValue;
        }

        try{
            this.keyTxtBox.value = value;
        }catch(e){
            Logger.log( "InputSugest.noDataAvalilable error: " + e.message);
        }
    },

    updateContent : function( list) {
        this.log("InputSugest.updateContent: " + list);
        this.floatDiv.innerHTML = this.buildSugest( list);

        this.size = 0;
        this.currentNodeIndex = -1;
        var children = this.floatDiv.childNodes;
        this.children = [];

        for (var i = 0; i < children.length; i++) {
            var item = children[i];
            var bean = list[i];

            if( !this.constant.ie){
                item.style.width = "99%";
            }

            var textOriginal = item.innerHTML;
            var textLower = item.innerHTML.toLowerCase();
            var what = this.txtBox.value.toLowerCase();
            var indexOf = textLower.indexOf( what);
            if( indexOf >=0 ) {
                
                item.innerHTML = textOriginal.substring(0, indexOf) +
                                 "<span style='font-weight:bold; margin:0; padding:0' aa_index='" + this.size + "' >" + textOriginal.substring( indexOf, indexOf + what.length) + "</span>" +
                                 textOriginal.substring( indexOf + what.length, indexOf + what.length + textOriginal.length);

                if(bean.sters && bean.sters == "true"){
                    Element.addClassName(item, "line-through gray-text");
                }
            }

            this.setIndexAsAttribute( item, this.size);
            item.onmouseover = this.constant.highlightHandler.bindAsEventListener( this);
            item.onclick = this.select.bindAsEventListener(this);
            this.children.push(item);
            this.size++;
        }

        this.totalCount = 0;
        try{
            this.totalCount = this.responseObject.totalResultCount;
        }catch(e){}

        if( this.beginIndex > 0 || this.size < this.totalCount){
            var totalDiv = document.createElement("div");
            totalDiv.className = "aa_total";
            totalDiv.innerHTML = (this.beginIndex+1) + " - " + (this.beginIndex + this.size) + " din " + this.totalCount;
            this.floatDiv.appendChild( totalDiv);
        }

        this.down();
        this.show();
        Element.removeClassName(this.txtBox, this.constant.waitingStyle);
    },

    setIndexAsAttribute : function( element, index){
        element.setAttribute("aa_index", index);

        try{
            var childs = element.childNodes;
            if( childs) for( var i = 0; i < childs.length; i++){
                this.setIndexAsAttribute( childs[i], index);
            }
        }catch(e){}
    },

    buildSugest : function ( list){
        var html = "";
        for( var i = 0; i < list.length; i++){
            html += "<div>"
            html += list[i].value;
            html += "</div>";
        }
        return html;
    },

    onLoading: function() {
        this.cleanup();
        this.hide();
        Element.addClassName(this.txtBox, this.constant.waitingStyle);
        this.beforeRequest();
    },

    cleanup: function() {
        this.size = 0;
        this.currentNodeIndex = -1;
        this.floatDiv.innerHTML = "";
        this.bufferDiv.innerHTML = "";
        Element.removeClassName(this.txtBox, this.constant.newValueStyle);
        Element.removeClassName(this.txtBox, this.constant.waitingStyle);
    },

    leftRightPressed : false,

    left : function(){
        Logger.log("left");
        if( this.canDoLeft()){
            if( this.beginIndex > 1){
                this.beginIndex -= this.maxCount;
            }
            this.leftRightPressed=true;
            this.requestData();
        }
    },

    canDoLeft : function (){
//        return false;
        return this.visible && this.beginIndex > 1;
    },

    right : function(){
        if( this.canDoRight()){
            this.beginIndex += this.maxCount;
            this.leftRightPressed=true;
            this.requestData();
        }
    },

    canDoRight : function (){
//        return false;
        return this.visible && this.size < this.totalCount && this.beginIndex + this.size < this.totalCount;
    },

    inIframe:function () {
        try {
            return window.self !== window.top;
        } catch (e) {
            return true;
        }
    },

    show : function (){
        this.log("show");
        var _w;
        if( this.txtBox.getAttribute("sugestWidth")){
            _w = this.txtBox.getAttribute("sugestWidth");
        }else{
            _w = this.txtBox.offsetWidth  + (this.constant.ie ? 0 : - 4);
        }

        this.floatDiv.style.width = _w + "px";

        var top = null;
        var left = null;

        if( this.inIframe()){
            var position = Position.cumulativeOffset(this.txtBox);
            top = position[1];
            left = position[0];
        }else{
            var viewportOffset = this.txtBox.getBoundingClientRect();
            top = viewportOffset.top;
            left = viewportOffset.left;
        }

        // these are relative to the viewport, i.e. the window

        this.floatDiv.style.top = (top + this.txtBox.offsetHeight - 1) + (this.constant.ie ? 6 : 3) + "px";
        this.floatDiv.style.left = left + (this.constant.ie ? -3 : 0) + "px";
        if (this.constant.ie) {
            this.iframe.style.width = this.floatDiv.style.width;
            this.iframe.style.top = this.floatDiv.style.top;
            this.iframe.style.left = this.floatDiv.style.left;
        }
        Element.show(this.floatDiv);
        if (this.constant.ie) {
            this.iframe.style.height = Element.getHeight(this.floatDiv) + "px";
            Element.show(this.iframe);
        }
        this.visible = true;
    },

    hide: function() {
        this.log("hide");
        this.visible = false;
        Element.hide(this.floatDiv);
        if (this.constant.ie) {
            Element.hide(this.iframe);
        }
    },

    currentNode: function() {
        var node = null;
        try{
            if (this.children) {
                return this.children[this.currentNodeIndex];
            }
        }catch (e){
        }
        return node;
    },

    highlight : function(h) {
        window.status = h;
        if (this.currentNode()) {
            Element.removeClassName(this.currentNode(), this.constant.highlightStyle);
        }

        this.currentNodeIndex = h;

        if (this.currentNode()) {
            Element.addClassName(this.currentNode(), this.constant.highlightStyle);
        }
    },

    up: function() {
        if (this.currentNodeIndex > 0) this.highlight(this.currentNodeIndex - 1);
    },

    down: function() {
        if (this.currentNodeIndex < this.size - 1) this.highlight(this.currentNodeIndex + 1);
    },

    select: function() {
        this.log("select");
        var node = this.currentNode();
        if ( node){
            var index = node.getAttribute("aa_index");

            //set value
            this.txtBox.value = this.list[ index].value;
            this.txtBox.style.color = "";
            //set key
            var keyValue;
            try{  keyValue = this.list[ index].key; }catch(e){}

            this.setKeyValue( keyValue);
            this.hide();

            //call user handler
            this.onSelect( this.list[ index]);
		}
	},

    /**
     * Apelata pentru validarea textului inainte de a cere datele de la provider
     */
    isValidSearchInput : function ( ){
        return true;
    },

    /**
     * User function. Apelata cand se selecteaza un element din lista de rezultte.
     */
    onSelect : function( item){
    },

    /**
     * User function. Apelata cand nu sunt receptionate date.
     */
    onNoData : function(){
    },

    beforeRequest : function (){

    },

    log: function ( msg){
        Logger.log( msg);
    }
}

var Logger = {
    log : function ( msg){
        try{
            $('log').innerHTML = $('log').innerHTML + "<br>" + msg;
        }catch(e){}
    }
}
