/**
 * Class geoname completer
 *
 */
GeonameCompleter = Class.create();
Object.extend(GeonameCompleter.prototype, {

    /**
     * Initializes GeonameCompleter
     */    
    initialize: function(containerNotSet, containerSet, containerResults, options) {
        // save basic elements        
        this.containerNotSet = $(containerNotSet); 
        this.containerSet = $(containerSet); 
        this.containerResults = $(containerResults); 
        this.searchActivator = this.containerSet.down('a');
        this.searchTrigger = this.containerNotSet.down('.trigger');
        this.searchInput = this.containerNotSet.down('input');
        this.searchIndicator = this.containerNotSet.down('.indicator');

        // specifies the ajax url to call
        this.url = HTML_ROOT + 'ajax/ajaxZoo.php?mode=geonames';

        // save options        
        if( this.setOptions ){
            this.setOptions(options);
        } else {
            this.options = options || {};
        }
        
        // Callback function. Called when a location has been chosen
        this.options.onLocationChosen = (this.options.onLocationChosen) ? this.options.onLocationChosen : null;
        
        // isset=true is used to start by showing a set location
        this.options.isset         = this.options.isset || false;

        this.options.asynchronous  = true;
        this.options.onComplete    = this.onComplete.bind(this);
        
        // Automatically save the selected geoname
        this.options.autoSave	   = this.options.autoSave || false;
        this.options.id			   = this.options.id || 'empty';

        // avoid autocompletion by browser
        this.searchInput.setAttribute('autocomplete','off');

        // hide update area
        Element.hide(this.containerResults);
        Element.hide(this.searchIndicator);
        (this.options.isset) ? Element.hide(this.containerNotSet) : Element.hide(this.containerSet);
        

        // observe
        this.onBlurObserver = this.onTrigger.bindAsEventListener(this);
        Event.observe(this.searchInput, 'blur', this.onBlurObserver);
        Event.observe(this.searchInput, 'keypress', this.onKeyPress.bindAsEventListener(this));
        Event.observe(this.searchTrigger, 'click', this.onTrigger.bindAsEventListener(this));
        Event.observe(this.searchActivator, 'click', this.onActivate.bindAsEventListener(this));
    },
        
    /**
     * Shows search results
     */    
    show: function() {
        Event.stopObserving(this.searchInput, 'blur', this.onBlurObserver);
        if(Element.getStyle(this.containerResults, 'display')=='none') {
            Effect.SlideDown(this.containerResults);
        }
    },

    /**
     * Hides search results
     */    
    hide: function() {
        this.stopIndicator();
        if(Element.getStyle(this.containerResults, 'display')!='none') {
            Element.hide(this.containerResults);
        }
    },

    /**
     * Shows indicator and hides trigger
     */
    startIndicator: function() {
        Element.show(this.searchIndicator); 
        Element.hide(this.searchTrigger);
    },

    /**
     * Hides indicator and shows trigger
     */
    stopIndicator: function() {
        Element.hide(this.searchIndicator);
        Element.show(this.searchTrigger);
    },

    /**
     * When TAB or RETURN has been pressed this will trigger the search
     */
    onKeyPress: function(event){
        switch(event.keyCode) {
            case Event.KEY_RETURN:
            case Event.KEY_TAB:
                this.onTrigger(event);
                Event.stop(event);
        }
    },

    /**
     * Activates controls for the search
     */
    onActivate: function(event){
        Event.observe(this.searchInput, 'blur', this.onBlurObserver);
        Element.show(this.containerNotSet);
        Element.hide(this.containerSet);
        this.searchInput.focus();
        this.searchInput.select();
        Event.stop(event);
    },

    /**
     * Triggers the search
     */
    onTrigger: function(event) {
        Event.stop(event);

        // get the desired loc from input and clean it up first
        var loc = $F(this.searchInput);
        loc = loc.strip();
        loc = loc.sub(/%/,'');
        
        // check location
        if( this.searchInput.defaultValue == loc ||
            2 > loc.length 
            // in approx 83.000 datasets there is only one place, that has a one 
            // letter name (geonameid: 3163608; name: Å; asciiname: A; population: 1145)
        ){
            // reset fields and return
            this.searchInput.value = this.searchInput.defaultValue;               
            return;
        }
        
        // set cleaned location to input
        this.searchInput.value = loc;
        
        // start ajax search
        this.hide();        
        this.startIndicator();
        this.getUpdatedChoices();
    },

    /**
     * Handles click events on li elements in the result list
     */
    onClick: function(event) {
        var element = Event.findElement(event, 'LI');
        this.updateElement(this.getEntry(element.autocompleteIndex));
        
        if( this.options.onLocationChosen ){
            this.options.onLocationChosen(element.geocode);
        }
        if( this.options.autoSave ){
        	var params = '&id=' + this.options.id  + '&action=saveUserLocation&geonameid=' + ((element.geocode.isCountry == '0') ? '' : 'c') + element.geocode.geonameid;
        	var url = HTML_ROOT + 'ajax/ajaxZoo.php?mode=geonames' + params
        	new Ajax.Request(url);  
        }
        
        this.hide();
        Element.hide(this.containerNotSet);
        Element.show(this.containerSet);
        new Effect.Highlight(this.containerSet);
        
        Event.stop(event);
    },

    /**
     * Gets an entry by index
     */
    getEntry: function(index) {
        return this.containerResults.down('ul').childNodes[index];
    },

    /**
     * Called on click of an li in the result list
     */
    updateElement: function(selectedElement) {
        // output in containerSet
        this.containerSet.down('span').innerHTML = selectedElement.innerHTML;
        this.containerSet.down('input').value = ((selectedElement.geocode.isCountry == '0') ? '' : 'c') + selectedElement.id;
    },

    /**
     * Performs ajax request
     */
    getUpdatedChoices: function() {
        entry = 'loc=' + encodeURIComponent(this.searchInput.value);

        this.options.parameters = this.options.callback ?
            this.options.callback(this.element, entry) : entry;

        if(this.options.defaultParams) 
            this.options.parameters += '&' + this.options.defaultParams;        
        
        new Ajax.Request(this.url, this.options);
    },


    /**
     * Gets data form the node 
     *
     * @param XMLNode res Result node
     * @param String fieldname Name of the node
     * @return mixed
     */    
    getValue: function(res, fieldname){
        var child = res.getElementsByTagName(fieldname)[0].firstChild;
        return  child ? child.data : '';
    },
    

    /**
     * Called when ajax request is complete
     *
     * @param XMLHttpRequest request
     */
    onComplete: function(request) {
        var results = request.responseXML.documentElement.childNodes;

    	if( results.length == 1 ){
    	    // directly set properties
            var res = results[0];
            var geonameid = this.getValue(res,'geonameid');
            var asciiname = this.getValue(res,'name');
            var area = this.getValue(res,'area');
            var country = this.getValue(res,'country');
            var latitude = this.getValue(res,'latitude');
            var longitude = this.getValue(res,'longitude');
            var isCountry = this.getValue(res,'isCountry');
            if (isCountry == '0')
	            var nodeValue = asciiname + ', ' + area + ', ' + country;
	        else
	        	var nodeValue = country;

            if( this.options.onLocationChosen ){
                this.options.onLocationChosen({'latitude':latitude, 'longitude':longitude});
            }
            
            if( this.options.autoSave ){
	        	var params = '&id=' + this.options.id  + '&action=saveUserLocation&geonameid='  + ((isCountry == '0') ? '' : 'c') + geonameid;
	        	var url = HTML_ROOT + 'ajax/ajaxZoo.php?mode=geonames' + params
	        	new Ajax.Request(url);  
	        }
	        

            this.containerSet.down('span').innerHTML = nodeValue;
            this.containerSet.down('input').value = ((isCountry == '0') ? '' : 'c') + geonameid;
    	    
    	    this.stopIndicator();
    	    Event.stopObserving(this.searchInput, 'blur', this.onBlurObserver);
            Element.hide(this.containerNotSet);
            Element.show(this.containerSet);
            new Effect.Highlight(this.containerSet);
            
    	} else if( results.length == 0 ){
    	    
    	    // show help text
    	    var txt = 
    	        '<div><p><strong>Es wurde leider kein Ort oder Land mit dieser Bezeichnung gefunden.</strong></p>'+
    	        '<p>Bitte überprüfe die Rechtschreibung. Beachte, dass sehr kleine Orte nicht gefunden werden können. '+
    	        'Wähle die nächstgrößere Stadt in der Umgebung. <a href="#">Abbrechen</a> | <a href="../popup/hilfe-i24.php" onclick="new_window2(\'target\',\'480\',\'520\')" target="target" rel="nofollow">Hilfe</a></p></div>';
            this.containerResults.innerHTML = txt;
            
            var cancle = this.containerResults.down('A');
            Event.observe(cancle, 'click', this.reset.bindAsEventListener(this));
            
            this.searchInput.focus();
            this.searchInput.select();
            this.stopIndicator();
            this.show();
            
    	} else {

    	    // show choice list
    	    this.containerResults.innerHTML = '';
    	    var geocodes = [];
    	    var ul = Builder.node('ul', {'id':'choices'});
            for( i=0; i<results.length; i++ ){
                var res = results[i];
                var geonameid = this.getValue(res,'geonameid');
                var asciiname = this.getValue(res,'name');
                var area = this.getValue(res,'area');
                var country = this.getValue(res,'country');
                var latitude = this.getValue(res,'latitude');
                var longitude = this.getValue(res,'longitude');
                var isCountry = this.getValue(res,'isCountry');
	            if (isCountry == '1')
		            var nodeValue = country;
		        else
		        	var nodeValue = asciiname + (''!=area?', '+area:'') + ', ' + country;
                
                var li = Builder.node('li', {'id':geonameid}, nodeValue);
                geocodes.push({'latitude':latitude, 'longitude':longitude, 'geonameid':geonameid, 'isCountry':isCountry});
                ul.appendChild(li);
            }
            this.containerResults.appendChild(ul);
    	    
    	    var txt_top = '<div><p>Es wurden mehrere Treffer gefunden. Bitte w&auml;hlen:</p>';
    	    var txt_end = (results.length > 5 ? '<a href="#" class="showmore">mehr..</a>':'')+
    	        '<p class="notfound"><strong>Dein Ort nicht dabei?</strong> Entweder ein Tippfehler, '+
    	        'oder der eingegebene Ort ist zu klein. W&auml;hle die n&auml;chstgr&ouml;&szlig;ere Stadt '+
    	        'in der Umgebung.<br/><a href="#" class="cancel">Abbrechen</a> | <a href="../popup/hilfe-i24.php" onclick="new_window2(\'target\',\'480\',\'520\')" target="target" rel="nofollow">Hilfe</a>'+
    	        '</p></div>';
            this.containerResults.innerHTML = txt_top + this.containerResults.innerHTML + txt_end;
			
            var cancle = this.containerResults.down('A.cancel');
            Event.observe(cancle, 'click', this.reset.bindAsEventListener(this));

            // register event observers on each li
            var items = $A(this.containerResults.down('ul').childNodes);
            for( var i = 0; i<items.length; i++ ){
                var entry = $(items[i]);
                entry.autocompleteIndex = i;
                entry.geocode = geocodes[i];
                Event.observe(entry, "click", this.onClick.bindAsEventListener(this));
                
                // only 5 results show up first
                if( i>=5 ){
                    Element.addClassName(entry, 'more');
                    Element.hide(entry);
                } else {
                    Element.addClassName(entry, 'less');
                }
            }
            
            // register a onclick on the a#showmore:
            // a click will hide a#showmore and show all li.more and the p.notfound
            if( results.length > 5 ){
                var notfount_para = this.containerResults.down('p.notfound');
                var showmore_link = this.containerResults.down('a.showmore');
                Element.hide(notfount_para);
                Event.observe(showmore_link, 'click', function(event){
                    Event.stop(event);
                    $$('ul li.more').each( function(element){
                        Element.show(element);
                    });
                    $$('ul li.less').each( function(element){
                        Element.hide(element);
                    });
                    Element.show(notfount_para);
                    Element.hide(showmore_link);
                });
            }
            
            this.stopIndicator();
            this.show();

    	}
    },

    /**
     * Resets the elements
     *
     */    
    reset: function(event){
        if(event){ Event.stop(event); }
        
        // reset input to defaultvalue
        this.searchInput.value = this.searchInput.defaultValue;
        
        // show/hide containers
        Element.show(this.containerNotSet);
        Element.hide(this.containerSet);
        this.hide();
        
        // clear geonameid and text representation of geo location
        this.containerSet.down('span').innerHTML = '';
        this.containerSet.down('input').value = '';
    }
});

