/******************** Hilfs-Funktionen ********************/
// Debug-Console vorhanden?
if(!window.console)
console = { log: function(msg) {} };


/**
 * this function determines whether the event is the equivalent of the microsoft
 * mouseleave or mouseenter events.
 *
 * http://www.dynamic-tools.net/toolbox/isMouseLeaveOrEnter
 */
function isMouseLeaveOrEnter(e, handler) {
    if (e.type != 'mouseout' && e.type != 'mouseover')
    return false;

    var reltg = e.relatedTarget ?
                    e.relatedTarget :
                    e.type == 'mouseout' ?
                        e.toElement :
                        e.fromElement;

    /* previous line translates to:
    var relatedTarget = e.relatedTarget;
    if(!relatedTarget) {
        if(e.type == 'mouseout') {
            relatedTarget = e.toElement;
        } else {
            relatedTarget = e.fromElement;
        }
    }
    */

    while(reltg && reltg != handler)
    reltg = reltg.parentNode;

    return (reltg != handler);
}

function FadeEffect(element){
       new Effect.Fade(element,
       { duration:1});
   }
   function ShowEffect(element){
       new Effect.Appear(element,
       {duration:1, from:0.0, to:1.0});
   }


/******************************************************************************/

var SuggestBox = Class.create();
SuggestBox.prototype = {

    /**
     * Konstruktor
     */
    initialize: function() {

        //console.log('Args:' + $H(arguments[0]).inspect());

        // Optionen
        this.options = Object.extend({

            field:      null,            // Form-Feld, zu dem die Suggestbox gehört
            options:   $A(),             // Auswahl, die angezeigt werden soll
            className: 'pc_autosuggest', // Klassenname für container-div

            onSelect:   null,            // Callbackfunktion, wenn eine Option gewählt wurde

            singleChoiceAutocomplete: true,      // Feld füllen, wenn nur eine Option?

            threshold: 200              // Keine Anzeige, wenn mehr Optionen

        }, arguments[0] || {});

        // Wenn kein InputField angegeben wurde, macht eine SuggestBox keinen Sinn
        if(!this.options.field)
        return false;

        this.field = $(this.options.field);

        this.lastValue = this.options.lastValue;

        ////console.log()

        // Momentan per Hover oder Cursortasten gewähltes  Element
        this.highlighted = null;

        // ID des div's nach dem Feld benennen
        var containerId = this.field.id + '_autosuggest';

        //console.log('Container ID: ' + containerId);

        // Div erzeugen
        this.container = $(document.createElement('div'));
        this.container.id = containerId;

        // Klassenname setzen
        this.container.addClassName(this.options.className);

        // Default-Styles
        this.container.setStyle({
            display:  'none',
            position: 'absolute',
            /*width:    this.field.getStyle('width'),*/
            overflow: 'auto'
        });

        ////console.log('insert into dom');
        document.body.appendChild(this.container);

        // Container auf Pos. des Formfelds setzen
        // (y-offset: Höhe des Feldes, damit Feld noch zu sehen ist u. das Ganze nach Dropdown aussieht)
        new Position.clone(this.field, this.container, {
            setWidth:  true,
            setHeight: false,
            offsetTop: this.field.getHeight()
        });

        //console.log('set up event handlers');

        /***** Event-Handling *****/
        // Container verstecken, wenn Klick außerhalb
        $(document).observe('click', function(event) {
            if(!isMouseLeaveOrEnter(event, this.container))
            FadeEffect(containerId);
        }.bind(this));



        // Keyup's:
        // (aus irgendeinem Grund frißt IE (6?) Up/Down nicht als Keypress, sonder keyup/down)
        //
        // Up-Down: nächstes/vorheriges El. auswählen
        this.field.observe('keyup', function(event) {

            var listElements = [];

            if(this.container.descendants() && this.container.descendants().length > 0)
            listElements = this.container.descendants()[0].descendants();

            // Down -> nächste Opt oder 1., wenn vorher keine oder letzte
            if(event.keyCode == Event.KEY_DOWN) {
                //console.log('KeyDown');

               if(!this.highlighted || this.highlighted == listElements[listElements.length - 1]) {
                   this.highlighted = listElements[0];
               } else {
                   this.highlighted = this.highlighted.next();
               }

               this.setHighlight();
               return;
            }

            // Up -> vorherige
            else if(event.keyCode == Event.KEY_UP) {

                if(!this.highlighted || this.highlighted == listElements[0]) {
                   this.highlighted = listElements[listElements.length - 1];
               } else {
                   this.highlighted = this.highlighted.previous();
               }

               this.setHighlight();
               return;
            }

        }.bind(this));

        // Keypresses
        // (und Return frißt er nicht als keyup/down)
        // Return: ausgewähltes El. wählen
        // Esc.: abbrechen
        this.field.observe('keypress', function(event) {

            if(event.keyCode == Event.KEY_ESC) {
                this.container.hide();
                return;
            }

            // Return -> auswählen
            else if(event.keyCode == Event.KEY_RETURN || event.keyCode == Event.KEY_TAB) {

                if(this.highlighted) {
                    this.select(this.field, this.highlighted.innerHTML);
                    this.highlighted = null;
                }

                this.hide();

                // Wenn Return, Eventpropagation stoppen, sonst wird Formular abgeschickt
                if(event.keyCode == Event.KEY_RETURN)
                Event.stop(event);

                return;
           }


        }.bind(this));

        this.field.observe('focus', function(event) {

            if(this.options.options.length >= 2 && $F(this.field).length >= 2)
            this.show();

        }.bind(this));

        /*
        this.field.observe('blur', function(event) {

            this.hide();

        }.bind(this));
        */

        this.updateOptions(this.options.options);
    },

    select: function(field, value) {
        $(field).value = value;

        this.lastSelectedValue = value;

        if(this.options.onSelect)
        this.options.onSelect(this.field, $F(this.field));
    },

    /**
     * Optionen, die in der Box erscheinen sollen, setzen
     */
    updateOptions: function(options) {

        //console.log('Update options');

        this.options.options = $A(options);

        ////console.log('Options:' + this.options.options.inspect());

        // Wenn keine oder mehr als 200 Optionen, kein Suggest
        if(this.options.options.length == 0
           || (this.options.threshold != null && this.options.options.length > this.options.threshold)) {

            //console.log('Above threshold (' + this.options.threshold + ')');

            this.hide();
            return;
        }

        // Wenn nur eine Option, diese direkt ins Feld eintragen & Container verstecken
        if(this.options.options.length == 1 && this.options.singleChoiceAutocomplete) {

            //console.log('LastVal:' + this.lastValue);

            if(this.lastValue != this.options.options[0]) {
                this.field.value = this.options.options[0];
                this.lastValue   = this.options.options[0];
            }

            this.hide();
            return;
        }

        // Wenn Inhalt seit letzter Auswhl niicht geändert -> Box nicht anzeigen
        if(this.lastSelectedValue && this.field.value == this.lastSelectedValue) {
            this.hide();
            return;
        }

        // Sonst Liste aufbauen und im Container zeigen
        this.populateContainer();

        this.show();
    },

    /**
     * Liste mit den anzuizeigenden Optionen aufbauen
     */
    populateContainer: function() {

        ////console.log('populateContainer');

        // Erstmal leermachen
        this.container.innerHTML = '';

        // Liste erzeugen
        this.list = $(document.createElement('ul'));
        this.list.addClassName('autosuggest_list');

        // Listeninhalte erzeugen
        this.options.options.each(function(opt) {

            var listElement = $(document.createElement('li'));
            listElement.setStyle({ cursor: 'pointer' });
            listElement.addClassName('autosuggest_element');
            listElement.innerHTML = opt;

            ////console.log('click 4 li');

            // Bei Klick wird dieses Element ausgewählt
            listElement.observe('click', function() {
                //console.log('set val to: ' + opt);

                this.select(this.field, opt);
                this.field.value = opt;
                this.hide();
            }.bind(this));

            Event.observe(listElement, 'mouseover', function(event) {

                this.highlighted = Event.element(event);
                this.setHighlight();

            }.bind(this));

            this.list.appendChild(listElement);
        }.bind(this));

        this.container.appendChild(this.list);
        this.highlighted = null;
    },

    /**
     *
     */
    show: function() {
        if(this.container) {
            //this.populateContainer();
            //this.container.show();
            ShowEffect(this.container);
        }
    },

    /**
     *
     */
    hide: function() {
        if(this.container) {
        //this.container.hide();
        FadeEffect(this.container);
        }
    },

    /**
     *
     */
    setHighlight: function() {

        ////console.log('Selected: ' + this.highlighted.innerHTML)

        $$('#'+this.container.id+' ul li').each(function(element) {

            if(element == this.highlighted) {
                element.addClassName('highlight');
            } else {
                element.removeClassName('highlight');
            }

        }.bind(this));

        var containerHeight    = this.container.getHeight();
        var borderTopWidth     = parseInt(this.container.getStyle('border-top-width').sub('px', ''));
        var borderBottomWidth  = parseInt(this.container.getStyle('border-bottom-width').sub('px', ''));

        var pad = borderTopWidth + borderBottomWidth + 1;
        var containerNettoHeight = containerHeight - pad;

        var elementOffset = Position.positionedOffset(this.highlighted)[1];
        var elementHeight = this.highlighted.getHeight();

        if(this.highlighted == $$('#'+this.container.id+' ul li').first()) {
            this.container.scrollTop = 0;
        }

        else if(this.highlighted == $$('#'+this.container.id+' ul li').last()) {
            this.container.scrollTop = this.container.getHeight();
        }

        else if(elementOffset >= containerNettoHeight) {
            this.container.scrollTop += elementHeight;
        }

        else if(elementOffset < (this.container.scrollTop - elementHeight)) {
            this.container.scrollTop -= elementHeight;
        }
    }
};

/******************************************************************************/

/**
 *
 */
var Verfuegbarkeit = Class.create();

// Static members
Verfuegbarkeit.selectTemplate =   '<select name="#{name}" id="#{id}" class="#{classes}">#{content}</select>';
Verfuegbarkeit.optionTemplate =    '<option value="#{value}">#{text}</option>';
Verfuegbarkeit.textinputTemplate = '<input type="text" class=#{classes}" name="#{name}" id="#{id}" value="#{value}" />';

Verfuegbarkeit.prototype = {

    /**
     *
     */
    initialize: function() {

        //console.log('Verfuegbarkeit.initialize()');


        this.options = Object.extend({

            formId:        null,
            inputClass:    null,
            inputIdPrefix: null,
            requestUrl:    null

        }, arguments[0] || {})

        this.fields     = {};
        this.fieldsById = {};

        this.inFocus = null;

        this.suggestBoxes = {};

        //console.log($(this.options.formId).inspect());

        $$('input').each(function(field) {

            if(!field.id)
            return;

            if((field.id).substr(0, this.options.inputIdPrefix.length) != this.options.inputIdPrefix)
            return;

            if(field.type == 'hidden')
            return;

            field = $(field);

            var fieldName = field.id.substr(this.options.inputIdPrefix.length);

            //console.log('Setup array for ' + fieldName);

            var fieldArray = {
                id:           field.id,
                fieldName:    fieldName,
                defaultValue: $F(field.id+'_default'),
                initialValue: $F(field),
                lastValue:    $F(field),
                filled:       false
            };

            this.fields[fieldName]    = fieldArray;
            this.fieldsById[field.id] = fieldArray;

            // Kein Autcomplete für Hausnummer

            //console.log('Setup events');

            // Focus auf Feld überwachen, bei Erstklick Inhalt löschen
            field.observe('focus', function() {
                this.inFocus = fieldName;

                if($F(field) == this.fields[fieldName]['defaultValue'])
                field.value = '';

                this.checkField(field, null);

            }.bind(this));

            // Blur überwachen, wenn nach Verlassen leer, wieder mit Default füllen
            field.observe('blur', function() {

                if($F(field).strip() == '')
                field.value = this.fields[fieldName]['defaultValue'];

            }.bind(this));

            // Browser-eigenes autocomplete abschalten
            field.setAttribute("autocomplete", "off");

            // Feldinhalt überwachen (nicht nummer)
            if(fieldName != 'nr')
            new Form.Element.Observer(field, 0.3, this.checkField.bind(this));
        }.bind(this));
    },

    /**
     * Wird aufgerufen, wenn ein Feldinhalt verändert wurde
     * Prüft, ob der eingegebene Wert gültig ist
     */
    checkField: function(element, value) {
        ////console.log('CheckField');

        if(this.fieldsById[element.id]['name'] == 'nr')
        return;

        // Im Zweifel das Feld anhand der ID holen
        var field = null;
        if(element.id) {
            field = $(element.id);
        } else {
            field = $(element);
        }

        // Nr nicht checke

        //console.log('checkFeld ' + field.inspect());

        // und den gelieferten Wert mit dem aktuellen überchreiben
        value = $F(field);

        var fieldName = field.id.substr(this.options.inputIdPrefix.length);

        // Wert geändert?
        if(value == this.fields[fieldName]['lastValue'])
        return;


        // Wert(e) prüfen
        // (nur grundsätzliches Zeug)

        // Ort - egal, solange nicht leer
        //if(fieldName == 'ort') {
        //    if($F(field).strip() == '')
        //    return;
        //}

        // Wert merken
        this.fields[fieldName]['lastValue'] = value;

        if(this.inFocus == fieldName && this.checkIsRequestable())
        this.fireRequest();
    },

    /**
     *
     */
    checkIsRequestable: function() {

        // PLZ
        if(this.inFocus == 'plz') {

            //console.log('Check plz');

            // Wenn Ort leer & PLZ vollständig: Sprung nach Ort,
            // aber keinen Request absetzen
            if(this.fields['plz']['lastValue'].length == 5
            	&& this.fields['plz']['lastValue'] != ''
               && this.fields['plz']['lastValue'] != this.fields['plz']['defaultValue']) {

                // Autofokus hat Nebeneffekt, erstmal aus
                FadeEffect($(this.fields['plz']['id']+'_autosuggest'));
                if(this.fields['ort']['lastValue'] == this.fields['ort']['defaultValue'])
                	$(this.fields['ort']['id']).focus();
                else if(this.fields['ort']['lastValue'] != this.fields['ort']['defaultValue'])
                	$(this.fields['strasse']['id']).focus();
                return true;
            }

            // Wenn Ort nicht leer, PLZ aber schon: Suche nach PLZ über Ort
            if(this.fields['ort']['lastValue'] != ''
               && this.fields['ort']['lastValue'] != this.fields['ort']['defaultValue']) {
                //console.log('PLZsuche')
                return true;
            }

            /*
            ret = (this.fields['plz']['lastValue'].length == 5);

            if(ret)
            this.fields['plz']['filled'] = true;

            return ret;
            */
            return (this.fields['plz']['lastValue'].strip() != ''
                    && this.fields['plz']['lastValue'] != this.fields['plz']['defaultValue']
                    && this.fields['plz']['lastValue'].strip().length >= 2);
        }


        // Ort
        if(this.inFocus == 'ort') {

            //console.log('Check ort');

            // Wenn PLZ gesetzt: Suche PLZ->Ort
            if(this.fields['plz']['lastValue'].length == 5) {
                return true;
            }

             // todo onselect ort from list click select what ever autofocus set to strasse
            //$(this.fields['ort']['id']+'_autosuggest').observe('click', function() {
            //if(this.fields['ort']['id'].clicked == true) {
            //FadeEffect($(this.fields['ort']['id']+'_autosuggest'));
            //$(this.fields['strasse']['id']).focus();
            //return true;
            //}
            //}.bind(this));

            // mind. 2 Zeichen
            return (this.fields['ort']['lastValue'].strip() != ''
                    && this.fields['ort']['lastValue'] != this.fields['ort']['defaultValue']
                    && this.fields['ort']['lastValue'].strip().length >= 2);

        }

        // Straße -> mind. 2 Zeichen
        if(this.inFocus == 'strasse') {
            return (this.fields['strasse']['lastValue'].strip() != '' // nicht leer
                    && this.fields['strasse']['lastValue'] != this.fields['strasse']['defaultValue']  // nicht default
                    && this.fields['strasse']['lastValue'].strip().length >= 2); //
        }

        // Hausnummer -> PLZ, Ort, Strasse müssen ausgefüllt sein
        if(this.inFocus == 'nr') {

            return (this.fields['plz']['lastValue'].strip() != ''
                    && this.fields['ort']['lastValue'].strip() != ''
                    && this.fields['strasse']['lastValue'].strip() != '');

        }

        return false;
    },

    /**
     *
     */
    fireRequest: function() {

        //console.log('fireRequest');

        // Construct request
        var request = { focus: this.inFocus };

        $H(this.fields).keys().each(function(fieldName) {

            var fieldId = (this.fields[fieldName]['id']);

            var value = $F(fieldId);

            if(value == this.fields[fieldName]['defaultValue'])
            value = '';

            request[fieldName] = value;

        }.bind(this));

        //console.log('Request: ' + $H(request).inspect());

        this.lastRequest = new Ajax.Request(this.options.requestUrl, {
            method:       'post',
            parameters:   request,
            evalJSON:     true,
            sanitizeJSON: true,

            onSuccess:    this.handleResponse.bind(this)
        });
    },

    /**
     *
     */
    handleResponse: function(t, json) {

        if(t.request != this.lastRequest) {
            //console.log('Dump response');
            return;
        }

        eval('response=' + t.responseText);

        //console.log('Response: ' + $H(response).inspect());

        // Fokus in PLZ
        if(response['focus'] == 'plz') {

            if(this.fields['ort'].lastValue.strip() != '') {
                this.createSuggestbox(this.fields['plz']['id'], response['plz']);
            }

            return;
        }

        if(response['focus'] == 'strasse') {
            this.createSuggestbox(this.fields['strasse']['id'], response['strasse']);

            if(response['strasse'].length == 1
                && ($F(this.fields['strasse']['id']) != this.fields['strasse']['defaultValue']
                    && $F(this.fields['strasse']['id']).strip() != '')) {
            FadeEffect($(this.fields['strasse']['id']+'_autosuggest'));
            $(this.fields['nr']['id']).focus();
            }

            return;
        }

        if(response['focus'] == 'ort') {

            this.createSuggestbox(this.fields['ort']['id'], response['ort']);

            if(response['ort'].length == 1
                && ($F(this.fields['ort']['id']) != this.fields['ort']['defaultValue']
                    && $F(this.fields['ort']['id']).strip() != '')) {
            FadeEffect($(this.fields['ort']['id']+'_autosuggest'));
            $(this.fields['strasse']['id']).focus();
            }
            //if(response['ort'].length == 1
                //&& ($F(this.fields['plz']['id']) == this.fields['plz']['defaultValue']
                    //|| $F(this.fields['plz']['id']).strip() == '')) {

                //console.log('1 Ort, filling plz');

                //this.createSuggestbox(this.fields['plz']['id'], response['plz']);

                //if(response['plz'].length > 1)
                //$(this.fields['plz']['id']).focus();
            //}

            return;
        }

        if(response['focus'] == 'nr') {

            if(response['nr'].length == 1) {
                $(this.fields['nr']['id']).value = response['nr'][0]
            } else {
                this.createSuggestbox(this.fields['nr']['id'], response['nr']);
            }
        }
    },

    createTextField: function(fieldId, value) {

        var currentField = $(fieldId);

        if(!currentField)
        return false;

        if(currentField.tagName.toLowerCase() == 'input' && currentField.getAttribute('type') == 'text') {

            currentField.value = value;

        } else {

            currentField.replace(new Template(Verfuegbarkeit.textinputTemplate).evaluate({
                name:    currentField.getAttribute('name'),
                id:      fieldId,
                value:   value,
                classes: $A(currentField.classNames()).join(' ')
            }));
        }
    },

    /**
     *
     */
    createDropdown: function(fieldId, opts) {

        // Momentanes Feld holen
        var currentField = $(fieldId);

        if(!currentField)
        return false;

        var options = $A();
        opts.each(function(opt) {
            options.push(new Template(Verfuegbarkeit.optionTemplate).evaluate({
                value: opt,
                text:  opt
            }));
        });

        if(currentField.tagName.toLowerCase() == 'select') {

            currentField.update(options.join(' '));

        } else {

            // Altes gegen neues Feld austauschen
            currentField.replace(new Template(Verfuegbarkeit.selectTemplate).evaluate({
                name:    currentField.getAttribute('name'),
                id:      fieldId,
                classes: $A(currentField.classNames()).join(' '),
                content: options.join(' ')
            }));
        }
    },

    /**
     *
     */
    createSuggestbox: function(fieldId, suggestions) {

        var field = $(fieldId);

        if(this.suggestBoxes[fieldId]) {

            //console.log('reuse sb');

            var box =  this.suggestBoxes[fieldId];
            box.updateOptions(suggestions);

        } else {

            //console.log('new sb');
            //console.log('Suggestions: ' + $A(suggestions).inspect());
            //console.log('initVal: ' + this.fieldsById[fieldId]['initialValue']);

            // Wenn PLZ, keine maximale Anzahl
            //var threshold = (this.fields['plz']['id'] == fieldId) ? null : 200;

            this.suggestBoxes[fieldId] = new SuggestBox({
                field:     field,
                options:   suggestions,
                lastValue: this.fieldsById[fieldId]['initialValue']/*,

                onSelect: function(field, value) { console.log('onSelect ' + $(field).inspect() +': ' + value );}*/
            });
        }
    }
};
