/******************** 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); 
}

/******************************************************************************/

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: 'tc_autosuggest', // Klassenname für container-div
            
            onSelect:   null,            // Callbackfunktion, wenn eine Option gewählt wurde
            
            singleChoiceAutocomplete: false,      // 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))
            this.container.hide();
        }.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();
        } 
    },
    
    /**
     * 
     */
    hide: function() { 
        if(this.container) 
        this.container.hide(); 
    },
    
    /**
     * 
     */
    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) {
                
                // Autofokus hat Nebeneffekt, erstmal aus
                //$(this.fields['ort']['id']).focus();
                return false;
            } 
               
            // 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;
            */
        }
        
        
        // Ort 
        if(this.inFocus == 'ort') {
            
            //console.log('Check ort');
            
            // Wenn PLZ gesetzt: Suche PLZ->Ort
            if(this.fields['plz']['lastValue'].length == 5) {
                return true;
            }
            
            // 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']);
            return;
        }
        
        if(response['focus'] == 'ort') {
            
            this.createSuggestbox(this.fields['ort']['id'], response['ort']);
            
            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 );}*/ 
            });
        }
    }
};
