/**
 * TT Suggest Box (Tredix Dreamreader) implementation for IBEs.
 * Constructor of class TTSuggestBox
 * 
 * @class Represents the base class of the suggest box.
 * @author Michael Dreher <dreher@traveltainment.de>
 * @constructor
 */
function TTSuggestBox(KID, CID) {
    this.config            = {};
    this.callbacks         = {};
    this.topRegion         = null;
    this.KID               = KID;
    this.CID               = CID;
    this.source            = '';
	this.elem              = null;
    this.lastResult        = null;
    this.elementSelected   = false;
    this.isOpen            = false;
    this.defaultLabel      = '';
    this.additionalResults = [];
    this.resultFilter      = [];
    this.suggestTypeSort   = {
        'Region':      0,
        'Destination': 1,
        'Village':     2,
        'POI':         3,
        'Hotel':       4
    };

    var THIS = this;

    // Collect instances in static array
    TTSuggestBox.instances.push(this);

    // Default
    this.setConfig({
        url:                 'http://80.87.174.125/Tredix/IFS?Context=Tredix/IFS/TravelTainment/Suggest/V3',
        source:              '',  // deprecated
        language:            'DE',
        numberOfSuggests:    '25',
        didYouMean:          '1',
        label:               '',
        inputName:           '',
        highlight:           '0',
        suggestVillages:     '0',
        suggestAirports:     '0',
        suggestPOIs:         '0',
        cityIbe:             '0',
        poiTypes:            '',
        filterPoisAccom:     true,
        port:                '653',
        categorize:          false,
        categorySortDynamic: false,
        charset:             null,
        selectFirstOnClose:  false,
        blockedRegions:      [],
        categoryLabels: {
            'Village':     'Orte',
            'POI':         'Sehensw&uuml;rdigkeiten',
            'Destination': 'Regionen',
            'Region':      'Reiseziele',
            'Hotel':       'Hotels'
        },
        anyElement: {
            show:             true,
            label:            'beliebig',
            fireOnEmptyInput: true, // fire "any option" if enter is pressed and the input is empty
            fireOnEmptyClick: true,
            showOnNoResult:   true
        },
        noResultElement: {
            show:       false,
            label:      'keine Vorschl\xE4ge vorhanden',
            value:      'beliebig',
            selectable: true
            // to manipulate action on select use 'setIbeDataCallback'
        }
    });

    /**
     * Compare-Funktion für Ergebnisse von Tredix
     * @param {Object} a
     * @param {Object} b
     */
    this.resultCompare = function(a, b) {
        if (a.type == b.type) {
            return -1;
        }
        return THIS.suggestTypeSort[a.type] < THIS.suggestTypeSort[b.type] ? -1 : 1;
    };
    this.resultCompareSort = false;
    
    if (typeof port != 'undefined') {
        this.config.port = port;
    }
    this.refreshPort();
}

/**
 * Constant for hotel-only source
 * @static
 */
TTSuggestBox.SRCHOTELONLY = 'HotelOnly';
/**
 * Constant for lapa source
 * @static
 */
TTSuggestBox.SRCLAPA      = 'LamiPauschal';
/**
 * Constant for fewo source
 * @static
 */
TTSuggestBox.SRCFEWO      = 'HolidayHome';
/**
 * SuggestBox Instances collector
 */
TTSuggestBox.instances = [];
/**
 * Static function to close all open suggest box instances from any script.
 */
TTSuggestBox.closeAll = function() {
    for (var i = 0; i < TTSuggestBox.instances.length; i++) {
        if (TTSuggestBox.instances[i].isOpen === true) {
            jQuery(TTSuggestBox.instances[i].elem).catcomplete('close');
        }
    }
};

/**
 * Set Configuration options.
 * @param {Object} oConfig
 */
TTSuggestBox.prototype.setConfig = function(oConfig) {
    if (typeof oConfig == 'undefined') {
        return;
    }
    for (var k in oConfig) {
        if (oConfig.hasOwnProperty(k)) {
            if (typeof oConfig[k] == 'object' && typeof this.config[k] != 'undefined') {
                for (var l in oConfig[k]) {
                    if (oConfig[k].hasOwnProperty(l)) {
                        this.config[k][l] = oConfig[k][l];
                    }
                }
            } else {
                this.config[k] = oConfig[k];
            }
        }
    }
    // Save default label for label reset
    if (typeof oConfig.label != 'undefined') {
        this.defaultLabel = oConfig.label;
    }
};

/**
 * Sets a callback-function for the dreamreader.
 * @param {Object} name 
 * @param {Object} func
 */
TTSuggestBox.prototype.setCallback = function(name, func) {
    this.callbacks[name] = func;
};

/**
 * 
 * @param {Object} func
 */
TTSuggestBox.prototype.setResultCompare = function(func) {
    if (typeof func == 'function') {
        this.resultCompareSort = true;
        this.resultCompare     = func;
    }
};

TTSuggestBox.prototype.addResultFilter = function(func) {
    if (typeof func == 'function') {
        this.resultFilter.push(func);
    }
};

/**
 * Activates filtering the result with the given region code.
 * @param {Object} topRegion
 */
TTSuggestBox.prototype.setRegionFilter = function(topRegion) {
    this.topRegion = topRegion;
};

/**
 * Callback method that is called whenever an item is selected.
 * @param {Object} elem
 */
TTSuggestBox.prototype.setIbeData = function(elem) {
    if (typeof this.callbacks.setIbeDataCallback == 'function') {
        this.callbacks.setIbeDataCallback(elem);
    }        
};

/**
 * Initializes the any element.
 */
TTSuggestBox.prototype.setAnyElement = function() {
	this.anyElement = {
        label:     this.config.anyElement.label,
        value:     this.config.anyElement.label,
        topRegion: '',
        region:    '',
        iff:       '',
        iffOrt:    '',
        type:      'any',
        category:  this.config.categorize ? 'Alle Reiseziele' : '',
        crs:       '',
        tva:       '',
        latitude:  '',
        longitude: ''
    };
};

/**
 * Selects the any element and submits the search form.
 */
TTSuggestBox.prototype.fireAnyElement = function(submitForm) {
    this.setIbeData(this.anyElement);
    if (typeof this.callbacks.select == 'function') {
        this.callbacks.select(this.anyElement);
    }
    if (typeof submitForm == 'undefined' || submitForm) {
        document.bengine.submit();
    }
};

/**
 * Initializes the element for no results.
 */
TTSuggestBox.prototype.setNoResultElement = function() {
    this.noResultElement = {
        label:     this.config.noResultElement.label,
        value:     this.config.noResultElement.value,
        topRegion: '',
        region:    '',
        iff:       '',
        iffOrt:    '',
        type:      'noresult',
        category:  '',
        crs:       '',
        tva:       '',
        latitude:  '',
        longitude: ''
    };
};

/**
 * Adds an array of items to the additional results that are appended to the original search results.
 * @param {Object} results
 */
TTSuggestBox.prototype.addToAdditionalResults = function(results) {
    for (var i = 0; i < results.length; i++) {
        this.additionalResults[this.additionalResults.length] = {
            label:     results[i].name,
            value:     results[i].name,
            topRegion: results[i].topRegion ? results[i].topRegion : null,
            region:    results[i].id,
            iff:       null,
            iffOrt:    null,
            type:      results[i].category,
            category:  this.config.categorize ? this.getCategoryLabel(results[i].category) : '',
            crs:       '',       // TODO
            tva:       '',       // TODO
            latitude:  null,
            longitude: null
        };
    }
};

/**
 * Returns the matching additional results with optional highlighting.
 * @param {Object} results
 * @param {Object} data
 */
TTSuggestBox.prototype.getMatchingAdditionalResults = function(results, data) {
    var query = data.SuggestResult.MetaData.RequestParameter.Query.replace(/ /, '|');
    
    for (var i = 0; i < this.additionalResults.length; i++) {
        if (this.additionalResults[i].value.match(new RegExp(query, 'i'))) {
            results[results.length] = this.highlightMatches(this.cloneObject(this.additionalResults[i]), query); 
        }
    }
    return results;
};

/**
 * Highlights the query in the result.label.
 * @param {Object} result
 * @param {Object} query
 */
TTSuggestBox.prototype.highlightMatches = function(result, query) {
    if (this.config.highlight) {
        result.label = result.label.replace(new RegExp(query, 'ig'), '<span id="highlight">$&</span>');
    }
    return result;
};

TTSuggestBox.prototype.refreshPort = function() {
    if (this.config.port == 655) {
        this.config.categoryLabels.Hotel = 'Objekte';
    }
    
    // Suggest-Source
    if (this.config.port == 655) {
        this.source = TTSuggestBox.SRCFEWO;
    } else if (this.config.port == 653) {
        this.source = TTSuggestBox.SRCHOTELONLY;
    } else {
        this.source = TTSuggestBox.SRCLAPA;
    }
};

/**
 * Clones an object.
 * @param {Object} obj
 */
TTSuggestBox.prototype.cloneObject = function(obj) {
    if (typeof Object.create == 'function') {
        return Object.create(obj);
    } else {
        return jQuery.extend(true, {}, obj);
    }
};

/**
 * Appends the Dreamreader input element to the given DOM node.
 * @param {Object} node HTML-element to add the dreamreader to.
 */
TTSuggestBox.prototype.appendTo = function(node) {
    if (typeof jQuery != 'function' || typeof jQuery.ui.autocomplete != 'function') {
        throw new Error('TTSuggestBox: jQuery with jQuery.UI.catcomplete must be included first!');
    }
    this.setLabel();
	this.setAnyElement();
    this.setNoResultElement();
    
    // create new input node
    var elem   = document.createElement('input');
    elem.type  = 'text';
    elem.value = this.config.label;
    if (this.config.inputName != '') {
        elem.name = this.config.inputName;
    }

    var THIS = this;

    // events
    elem.onclick = function() {
        if (this.value == THIS.config.label || this.value == THIS.defaultLabel) {
            elem.value = '';
            if (THIS.config.anyElement.fireOnEmptyClick == true) {
                THIS.fireAnyElement(false);
            }
            if (typeof THIS.callbacks.removeDefaultLabelCallback == 'function') {
                THIS.callbacks.removeDefaultLabelCallback();
            }
        }
    };
    elem.onblur = function() {
        if (this.value == '') {
            elem.value = THIS.config.anyElement.fireOnEmptyClick == true ? THIS.defaultLabel : THIS.config.label;
            if (typeof THIS.callbacks.restoreDefaultLabelCallback == 'function') {
                THIS.callbacks.restoreDefaultLabelCallback();
            }
        }
    };
    
    // Add to DOM
    if (typeof this.callbacks.beforeDomAppend == 'function') {
        this.callbacks.beforeDomAppend(elem);
    }
    jQuery(node).html('').append(elem);
    this.elem = elem;
	
    // Create Autocomplete
    jQuery(elem).catcomplete({
        source: function(request, response) {
            // filterRegion?
            var optionalItems = '';
            if (typeof THIS.callbacks.getRegionFilter == 'function') {
                THIS.setRegionFilter(THIS.callbacks.getRegionFilter()); 
            }
            if (THIS.topRegion != null) {
                // Regions: flugdauer, Destinations: topregion
                if (THIS.topRegion >= 10000) {
                    optionalItems += '&Regions=' + THIS.topRegion;
                } else {
                    optionalItems += '&Destinations=' + THIS.topRegion;
                }
            }
            if (THIS.config.charset != null) {
                optionalItems += '&Charset=' + THIS.config.charset;
            }
            
            // Parameter ResponseFilter zusammensetzen
            var responseFilter = [];
            if (THIS.config.filterPoisAccom) {
                responseFilter.push('POINearbyAccommodation');
            }
            if (THIS.config.cityIbe == '1') {
                responseFilter.push('CityIBE');
            }
            
            // Blockierte Regionsgruppen (>= 10000) herausfiltern
            if (typeof THIS.config.blockedRegions != 'undefined' && THIS.config.blockedRegions.length > 0) {
                var blockedGroups = [];
                for (var i = 0, c = THIS.config.blockedRegions.length; i < c; i++) {
                    if (parseInt(THIS.config.blockedRegions[i]) >= 10000) {
                        blockedGroups.push(parseInt(THIS.config.blockedRegions[i]));
                    }
                }
                if (blockedGroups.length > 0) {
                    optionalItems += '&ExcludeRegions=' + blockedGroups.join(',');
                }
            }
            
            var tmpURL = THIS.config.url
                       + "&Query="+ encodeURIComponent(request.term)
                       + "&NumberOfSuggests=200" // + THIS.config.numberOfSuggests
                       + "&Language=" + THIS.config.language
                       + "&ResponseDetail=DLC,IFF,TYPE,REGIONCODE,IFFREGIONCODE,IFFVILLAGECODE,GEOLOCATION"
                       + "&Highlighting=" + THIS.config.highlight
                       + "&DidYouMean=" + THIS.config.didYouMean
                       + "&SuggestAirports=" + THIS.config.suggestAirports
                       + "&SuggestDestinations=" + THIS.config.suggestDestinations
                       + "&SuggestHotels=" + THIS.config.suggestHotels
                       + "&SuggestPOIs=" + THIS.config.suggestPOIs
                       + "&SuggestRegions=" + THIS.config.suggestRegions
                       + "&SuggestVillages=" + THIS.config.suggestVillages
                       + "&Customer=" + THIS.KID + (typeof THIS.CID == 'undefined' ? '' : THIS.CID)
                       + "&Source=" + THIS.source
                       + "&POITypes=" + THIS.config.poiTypes
                       + "&ResponseFilter=" + responseFilter.join(',')
                       + optionalItems;

            if (typeof THIS.config.consoleDebug != 'undefined' && THIS.config.consoleDebug == '1' && typeof console != 'undefined') {
                console.log(tmpURL);                    
            }
            
            // Request Tredix
            jQuery.ajax({
                url:      tmpURL,
                dataType: "jsonp",
                jsonp:    "jsoncallback",
                data:     {},
                success: function(data) {
                    tmpData = data;
                    if (!data.TredixIFSError) {
                        if (THIS.config.categorySortDynamic) {
                            var suggestTypeSortCount = 0;
                            THIS.suggestTypeSort = {};
                        }
                        
                        // Valid result
                        var result = jQuery.map(data.SuggestResult.Suggests, function(item) {
                            //console.debug(item);
                            // POIs ohne IFFOrt herausfiltern
                            if (item.Type == 'POI' && (typeof item.IFFVillageCode == 'undefined' || item.IFFVillageCode == '')) {
                                return null;
                            }
                            // Nicht vorhandene City-Ziele herausfiltern
                            if (typeof THIS.isCityLight != 'undefined' && THIS.isCityLight && typeof THIS.iffOrteCity != 'undefined') {
                                if (typeof item.IFFVillageCode == 'undefined' || item.IFFVillageCode == '' || typeof THIS.iffOrteCity[parseInt(item.IFFVillageCode)] == 'undefined') {
                                    return null;
                                }
                            }
                            // Geblockte Regionen (< 10000) herausfiltern
                            if (typeof THIS.config.blockedRegions != 'undefined' && THIS.config.blockedRegions.length > 0) {
                                var topreg = item.Type == 'POI' ? item.RegionCode : item.IFFRegionCode;
                                if (topreg && parseInt(topreg) < 10000 && TTSuggestBox.containsItem(parseInt(topreg), THIS.config.blockedRegions)) {
                                    return null;
                                }
                            }
                            
                            var valueRes = item.Suggestion;
                            if (THIS.config.highlight == 1) {
                                valueRes = valueRes.replace(/<\/?span[^>]*>/g, '');
                            }
                            
                            if (THIS.config.categorySortDynamic) {
                                if (typeof THIS.suggestTypeSort[item.Type] == 'undefined') {
                                    THIS.suggestTypeSort[item.Type] = suggestTypeSortCount++;
                                }
                            }
                            
                            return {
                                label:     item.Suggestion,
                                value:     valueRes,
                                topRegion: item.IFFRegionCode,
                                region:    item.RegionCode,
                                iff:       item.IFF,
                                iffOrt:    item.IFFVillageCode,
                                type:      item.Type,
                                category:  THIS.config.categorize ? THIS.getCategoryLabel(item.Type) : '',
                                crs:       item.crs,
                                tva:       item.tva,
                                latitude:  item.Latitude,
                                longitude: item.Longitude
                            };
                        });
                        
                        // Zusätzliche eigene Ergebnisse hinzufügen (UDF-Gruppen)
                        if (THIS.additionalResults.length > 0) {
                            result = THIS.getMatchingAdditionalResults(result, tmpData);
                        }
                        
                        // Ergebnisse filtern
                        if (THIS.resultFilter.length > 0) {
                            for (var i = 0; i < THIS.resultFilter.length; i++) {
                                result = jQuery.map(result, THIS.resultFilter[i]);
                            }
                        }
                        
                        // Liste auf vorgegebene Länge kürzen
                        while(result.length > THIS.config.numberOfSuggests) {
                            result.pop();
                        }
                        
                        // Ergebnisse sortieren
                        if (THIS.resultCompare != null && (THIS.resultCompareSort || THIS.config.categorize)) {
                            result = result.sort(THIS.resultCompare);
                        }

                        // NoResult-Element hinzufügen
                        var noResult = result.length == 0;
                        if (THIS.config.noResultElement.show && noResult) {
                            result.push(THIS.noResultElement);
                        }
                        
                        // shotel-Element hinzufügen
                        if (typeof THIS.config.textElement != 'undefined' && THIS.config.textElement.show) {
                            if (typeof THIS.config.textElement.position != 'undefined' && THIS.config.textElement.position == 'first') {
                                // Als erstes Element
                                result.unshift(THIS.getTextElement(request.term));
                            } else {
                                // Als (vor)letztes Element
                                result.push(THIS.getTextElement(request.term));
                            }
                        }
                        
                        // Beliebig-Element hinzufügen
                        if (THIS.config.anyElement.show && (THIS.config.anyElement.showOnNoResult && noResult || !noResult)) {
                            result.push(THIS.anyElement);
                        }

                        //console.debug(result);
                        THIS.lastResult = result;
                        response(result);
                    } else { 
                        // IFS Error
                        alert(data.TredixIFSError.Message);
                    }
                }
            });
        },
        minLength: 1,
        delay: 0,
        html: true,
        focus: function(event, ui) {
            if (typeof ui.item != 'undefined' && ui.item != null && ui.item.type == 'noresult' && !THIS.config.noResultElement.selectable) {
                return false;
            }
            if (typeof THIS.callbacks.focus == 'function') {
                THIS.callbacks.focus(ui.item);
            }
        },
        select: function(event, ui) {
            if (ui.item.type == 'noresult' && !THIS.config.noResultElement.selectable) {
                return false;
            }
            THIS.elementSelected = true;
            THIS.setIbeData(ui.item);
            if (typeof THIS.callbacks.select == 'function') {
                THIS.callbacks.select(ui.item);
            }
        },
        open: function() {
            THIS.elementSelected = false;
            THIS.isOpen          = true;
            
            if (typeof THIS.callbacks.open == 'function') {
                THIS.callbacks.open();
            }
        },
        close: function() {
            // Texteingabe ohne Element auszuwählen soll auch funktionieren,
            // wenn es den eingegebenen Text genau so im letzten Ergebnis gibt
            if (!THIS.elementSelected && THIS.lastResult) {
                var foundItem = false;
                for (var i = 0; i < THIS.lastResult.length; i++) {
                    if (THIS.lastResult[i].value.toString().toLowerCase() == THIS.elem.value.toString().toLowerCase()) {
                        THIS.elementSelected = true;
                        THIS.setIbeData(THIS.lastResult[i]);
                        jQuery(THIS.elem).val(THIS.lastResult[i].value);
                        foundItem = true;
                        break;
                    }
                }
                // Wenn kein exakter Treffer gefunden wurde, aber Ergebnisse, dann erstes Ergebnis auswählen
                if (THIS.config.selectFirstOnClose && !foundItem && THIS.elem.value.toString() != '') {
                    if (THIS.lastResult.length > 0) {
                        THIS.elementSelected = true;
                        THIS.setIbeData(THIS.lastResult[0]);
                        jQuery(THIS.elem).val(THIS.lastResult[0].value);
                    } else {
                        THIS.reset(); // klappt noch nicht mit weg.de lösung
                    }
                }                
            }
            THIS.isOpen = false;

            if (typeof THIS.callbacks.close == 'function') {
                THIS.callbacks.close();
            }
        }
    }).keydown(function(event) {
        // Leeres Input-Element -> beliebig abfeuern bei ENTER?
        if (THIS.config.anyElement.fireOnEmptyInput && event.keyCode == '13' && this.value == '') {
            THIS.fireAnyElement();
        // Text eingetippt und Enter -> autocomplete schliessen
        } else if (event.keyCode == '13' && this.value != '') {
            jQuery(THIS.elem).catcomplete('close');
        }
    });
};

/**
 * Returns the category label for a given return type.
 */
TTSuggestBox.prototype.getCategoryLabel = function(value) {
    return this.config.categoryLabels[value];
};

TTSuggestBox.prototype.reset = function() {
	this.elem.value = this.config.label;
};

TTSuggestBox.prototype.getElement = function() {
    return this.elem;
};

/**
 * Erlaubt das nachträgliche Ändern des angezeigten Textes.
 * @param {Object} value
 */
TTSuggestBox.prototype.setLabelText = function(value) {
    this.config.label = value;
    this.elem.value = this.config.label;
    
    if (typeof this.callbacks.setLabelCallback == 'function') {
        this.config.label = this.callbacks.setLabelCallback(this.config.label, this.elem);
    }
};

/**
* Search Element in Array.
* @param {Object} needle
*/
TTSuggestBox.containsItem = function(needle, haystack) {
    for (var i = 0; i < haystack.length; i++) {
        if (haystack[i] == needle) return true;
    }
    return false;
};

/* -------------------------------------------------------------------------- */
// Simulate Classic Inheritance 
if (typeof TTOOP == 'undefined' || typeof TTOOP.extend != 'function') {
    var TTOOP = {}; 

    /**
     * Extend superClass with subClass
     * @param {Object} subClass
     * @param {Object} superClass
     */
    TTOOP.extend = function (subClass, superClass) {
        var F = function() {};
        F.prototype = superClass.prototype;
        subClass.prototype = new F();
        subClass.prototype.constructor = subClass;
        
        subClass.parent = superClass.prototype;
        if (superClass.prototype.constructor == Object.prototype.constructor) {
            superClass.prototype.constructor = superClass;
        }
    };
}

/**
 * Destination SB extending TTSuggestBox
 * @class Represents the destination suggest box.
 * @author Michael Dreher <dreher@traveltainment.de>
 * @constructor
 * @extends TTSuggestBox
 * @param {Object} oConfig Configuration object.
 */
function TTSuggestBoxDestination(oConfig, KID, CID) {
    TTSuggestBoxDestination.parent.constructor.call(this, KID, CID);

    this.refDR         = null;
    this.inpZiel       = null;
    this.inpRegion     = null;
    this.topRegIndex   = {};
    this.zgkIndex      = {};
    this.udfGroupIndex = {};
    this.iffOrteCity   = {};
    this.isUdfRegions  = false;
    this.isCityLight   = typeof IBE != 'undefined' && typeof IBE.req.ibeType != 'undefined' && IBE.req.ibeType == 'city';

    this.setConfig({
        suggestDestinations:    this.isCityLight ? 0 : 1,
        suggestRegions:         this.isCityLight ? 0 : 1,
		suggestPOIs:			1,
		suggestVillages:		1,
        suggestHotels:          0,
        loadCache:              true,
        inputName:              'suggestDestination',
        zielSelectName:         '',
        regionSelectName:       '',
		destinationJumpToHotel: false,
        anyElement: {
            label: 'Alle Reiseziele'
        }
    });
    this.setConfig(oConfig);

    if (this.config.loadCache) {
        this.initRegions();
    }
    if (this.isCityLight) {
        this.addResultFilter(this.cityIffOrtFilter);
    }
}
TTOOP.extend(TTSuggestBoxDestination, TTSuggestBox);

/**
 * Initialize Regions from cachefile.
 */
TTSuggestBoxDestination.prototype.initRegions = function() {
    if (typeof window['ttUDF_' + this.config.port] != 'undefined') {
        // UDF-Regionen Gruppen als Reiseziele verwenden.
        this.isUdfRegions = true;
        var udfGroups     = [];
        
        // TopRegion-Index anlegen
        for (var i = 0; i < window['ttUDF_' + this.config.port].length; i++) {
            var group      = window['ttUDF_' + this.config.port][i];
            var topRegions = [];

            for (var j = 0; j < group.regions.length; j++) {
                var region = group.regions[j];
                this.topRegIndex[region.topRegion] = {
                    name: region.titleJS,
                    zgk:  region.zk,
                    reg:  region.id
                };
                this.zgkIndex[region.zk] = {
                    name: region.titleJS,
                    topR: region.topRegion,
                    reg:  region.id
                };
                if (this.isCityLight) {
                    this.iffOrteCity[region.IFFOrt] = 1;
                }
                topRegions.push(region.topRegion);
            }

            udfGroups.push({
                id:        group.id,
                name:      group.titleJS,
                category:  'Region',
                topRegion: topRegions.join(',')
            });
            this.udfGroupIndex[group.id] = {
                name: group.titleJS
            };
        }
        
        // UDF Gruppen aus Cachefiles zu den additionalResults der SuggestBox hinzufügen
        if (this.config.suggestRegions == 1) {
            this.config.suggestRegions = 0; // dont request regions from tredix
            this.addToAdditionalResults(udfGroups);
        }
        
        // Formularfeldnamen
        var zielSelectName   = this.config.zielSelectName == '' ? 'udf' : this.config.zielSelectName;
        var regionSelectName = this.config.regionSelectName == '' ? 'udfRegion' : this.config.regionSelectName;
    } else {
        // TopRegion-Index anlegen
        for (var i = 0; i < window['TTREGS_' + this.config.port].length; i++) {
            var tmp = window['TTREGS_' + this.config.port][i].split('|');
            this.topRegIndex[tmp[3]] = {
                name: tmp[0],
                zgk:  tmp[1],
                reg:  tmp[2]
            };
            this.zgkIndex[tmp[1]] = {
                name: tmp[0],
                topR: tmp[3],
                reg:  tmp[2]
            };
        }
    
        // Formularfeldnamen
        var zielSelectName   = this.config.zielSelectName == '' ? 'ziel' : this.config.zielSelectName;
        var regionSelectName = this.config.regionSelectName == '' ? 'region' : this.config.regionSelectName;
    }

    // Formularfeld ziel
    if (jQuery('select[name="' + zielSelectName + '"]').length == 1) {
        this.inpZiel = jQuery('select[name="' + zielSelectName + '"]');
    } else if (jQuery('#' + zielSelectName).length == 1) {
        this.inpZiel = jQuery('#' + zielSelectName);
    }

    // Formularfeld region
    if (jQuery('select[name="' + regionSelectName + '"]').length == 1) {
        this.inpRegion = jQuery('select[name="' + regionSelectName + '"]');
    } else if (jQuery('#' + regionSelectName).length == 1) {
        this.inpRegion = jQuery('#' + regionSelectName);
    }
    //console.debug(this.topRegIndex);
};

/**
 * Sets the label of the SB.
 */
TTSuggestBoxDestination.prototype.setLabel = function() {
    var inpUdfRegionen = jQuery('#udfregionen,input[name="udfregionen"],select[name="udfregionen"]');
    if (typeof this.callbacks.setLabel == 'function') {
        this.config.label = this.callbacks.setLabel();
    } else if (typeof IBE != 'undefined' && typeof IBE.req[this.config.inputName] != 'undefined' && IBE.req[this.config.inputName] != '' && IBE.req[this.config.inputName] != this.config.label && IBE.req.detail != '' && IBE.req.detail != 'zielgebiet') {
        this.config.label = IBE.req[this.config.inputName];
    } else if (typeof suggestBoxDestinationLabel != 'undefined' && suggestBoxDestinationLabel != '') {
        this.config.label = suggestBoxDestinationLabel;
    } else if (typeof IBE != 'undefined' && typeof IBE.req.topRegion != 'undefined' && parseInt(IBE.req.topRegion) > 0 && typeof this.topRegIndex[IBE.req.topRegion] != 'undefined') {
        this.config.label = this.topRegIndex[IBE.req.topRegion].name;
    } else if (typeof IBE != 'undefined' && typeof IBE.req.zielgebiets_kenner != 'undefined' && parseInt(IBE.req.zielgebiets_kenner) > 0 && typeof this.zgkIndex[IBE.req.zielgebiets_kenner] != 'undefined') {
        this.config.label = this.zgkIndex[IBE.req.zielgebiets_kenner].name;
    } else if (typeof IBE != 'undefined' && typeof IBE.req.flugdauer != 'undefined' && parseInt(IBE.req.flugdauer) >= 10000 && typeof this.topRegIndex[IBE.req.flugdauer] != 'undefined') {
        this.config.label = this.topRegIndex[IBE.req.flugdauer].name;
    } else if (typeof IBE != 'undefined' && typeof IBE.req.udf != 'undefined' && typeof this.udfGroupIndex[IBE.req.udf] != 'undefined') {
        this.config.label = this.udfGroupIndex[IBE.req.udf].name;
    } else if (inpUdfRegionen.length > 0 && inpUdfRegionen.val() != '' && inpUdfRegionen.val() != '-1' && inpUdfRegionen.val().split('|')[0] != 'a') {
        this.config.label = inpUdfRegionen.val().split('|')[0];
    } else if (detail == 'hotel'&& typeof document.bengine.IFFOrt != 'undefined' && document.bengine.IFFOrt.value != '' && typeof document.bengine.IFFOrt.selectedIndex != 'undefined' && (typeof showCompare == 'undefined' || showCompare == false)) {
        this.config.label = document.bengine.IFFOrt.options[document.bengine.IFFOrt.selectedIndex].text;
    }
    
    if (typeof this.callbacks.setLabelCallback == 'function') {
        this.config.label = this.callbacks.setLabelCallback(this.config.label);
    }
};

/**
 * Connect SB to given Hotel SB (hotels will be restricted to selected region)
 * @param {Object} dr
 */
TTSuggestBoxDestination.prototype.linkToHotelBox = function(dr) {
    if (typeof dr == 'object' && typeof dr.setRegionFilter == 'function') {
        this.refDR = dr;

        // Init Hotel DR with selected region
        if (typeof IBE.req != 'undefined') {
            var topReg = null;
            if (typeof IBE.req.topRegion != 'undefined' && parseInt(IBE.req.topRegion) > 0) {
                topReg = IBE.req.topRegion;
            } else if (typeof IBE.req.zielgebiets_kenner != 'undefined' && parseInt(IBE.req.zielgebiets_kenner) > 0 && typeof this.zgkIndex[IBE.req.zielgebiets_kenner] != 'undefined') {
                topReg = this.zgkIndex[IBE.req.zielgebiets_kenner].topR;
            } else if (typeof IBE.req.flugdauer != 'undefined' && parseInt(IBE.req.flugdauer) >= 10000) {
                topReg = IBE.req.flugdauer;
            }
            this.refDR.setRegionFilter(topReg);
        }
    }
};

/**
 * Updates the IBE form with the selected elements data.
 */
TTSuggestBoxDestination.prototype.setIbeData = function(elem) {
    // Update Hotel DR with selected region
    if (this.refDR != null) {
        this.refDR.setRegionFilter(typeof elem.topRegion == 'undefined' ? elem.region : elem.topRegion);
    }
    
    // Destination-Selects update
    this.setDestinationFormFields(elem);

    if (typeof this.callbacks.setIbeParams == 'function') {
        this.callbacks.setIbeParams(elem);
    } else {
		//console.debug(elem);
        if (elem.type == 'any' || elem.type == 'noresult') {
            jQuery('#detail,input[name="detail"]').val('zielgebiet');
            if (this.isUdfRegions) {
                jQuery('#udf,input[name="udf"]').val('');
            } else {
                jQuery('#flugdauer,input[name="flugdauer"]').val('');
            }
        } else {
            if (elem.type == 'Region') {
                if (this.config.destinationJumpToHotel && !this.isUdfRegions) {
                    jQuery('#detail,input[name="detail"]').val('hotel');
                    jQuery('#topRegion,input[name="topRegion"]').val(elem.region);
                } else {
                    jQuery('#detail,input[name="detail"]').val('zielgebiet');
                    if (this.isUdfRegions) {
                        jQuery('#udf,input[name="udf"]').val(elem.region);
                    } else {
                        jQuery('#flugdauer,input[name="flugdauer"]').val(elem.region);
                    }
                }
            } else {
                jQuery('#detail,input[name="detail"]').val('hotel');
                jQuery('#topRegion,input[name="topRegion"]').val((typeof elem.topRegion == 'undefined' || elem.topRegion == null) && elem.region ? elem.region : elem.topRegion);
            }
        }
        if (parseInt(elem.iffOrt) > 0) {
            jQuery('#IFFOrt,input[name="IFFOrt"]').val(elem.iffOrt);
            jQuery('#IFFOrtSuggest,input[name="IFFOrtSuggest"]').val(elem.iffOrt);
            jQuery('#IFFVillage,input[name="IFFVillage"]').val(elem.iffOrt);
            jQuery('#zielOrt,select[name="zielOrt"]').val(elem.region + ' ' + elem.iffOrt); // formular == 5
        } else {
            jQuery('#IFFOrt,input[name="IFFOrt"]').val('');
            jQuery('#IFFOrtSuggest,input[name="IFFOrtSuggest"]').val(-1);
            jQuery('#IFFVillage,input[name="IFFVillage"]').val(-1);
        }
        jQuery('#IFFFilter,input[name="IFFFilter"]').val('');
        jQuery('#IFF,input[name="IFF"]').val('');
	    jQuery('#zielgebiets_kenner,input[name="zielgebiets_kenner"]').val('-1');
        jQuery('#udfregionen,input[name="udfregionen"]').val('');
        jQuery('#shotel,input[name="shotel"]').val('');
		//jQuery('#udfregionen,input[name="udfregionen"]').val(elem.value + '|' + topReg);
    }

    TTSuggestBoxDestination.parent.setIbeData.call(this, elem);
};

/**
 * @private
 */
TTSuggestBoxDestination.prototype.setDestinationFormFields = function(elem) {
    var topReg          = elem.topRegion;
    var region          = elem.region;
    var cityLightSuffix = '';
    if (this.isCityLight && typeof elem.iffOrt != 'undefined') {
        cityLightSuffix = '|' + elem.iffOrt;
    }

    if (this.isUdfRegions) {
        if ((typeof topReg == 'undefined' || topReg == null) && elem.type != 'Region') {
            // POI ohne topRegion, aber nicht für UDF-Gruppen
            topReg = region;
        }
        
        if (this.inpZiel != null && this.inpRegion == null) {
            // nur ein Select für ZIEL
            if (typeof topReg == 'undefined' || topReg == null) {
                // UDF-Gruppe ausgewählt
                jQuery(this.inpZiel).val(region).change();
            } else if (typeof this.topRegIndex[topReg] != 'undefined') {
                jQuery(this.inpZiel).val(this.topRegIndex[topReg].reg + '|' + topReg + cityLightSuffix).change();
            } else if (topReg == '') {
                jQuery(this.inpZiel).val('-1').val('-1|-1').val('-1|-1').change();
            }
        } else if (this.inpZiel != null && this.inpRegion != null) {
            // zwei Selects
            if (typeof topReg == 'undefined' || topReg == null) {
                // UDF-Gruppe ausgewählt
                jQuery(this.inpZiel).val(region).change();                
            } else if (typeof this.topRegIndex[topReg] != 'undefined') {
                jQuery(this.inpZiel).val(this.topRegIndex[topReg].reg).change();
                jQuery(this.inpRegion).val(this.topRegIndex[topReg].reg + '|' + topReg + cityLightSuffix).change();
            } else if (topReg == '') {
                jQuery(this.inpZiel).val('-1').val('-1|-1').change();
                jQuery(this.inpRegion).val('-1').val('-1|-1').change();
            }
        }
    } else {
        if (typeof topReg == 'undefined' || topReg == null) {
            topReg = region;
        }
        if (this.inpZiel != null) {
            if (typeof this.topRegIndex[topReg] != 'undefined') {
                jQuery(this.inpZiel).val(this.topRegIndex[topReg].reg).change();
            } else if (topReg == '') {
                jQuery(this.inpZiel).val('-1');
            }
        } 
        if (this.inpRegion != null) {
            if (typeof this.topRegIndex[topReg] != 'undefined') {
                if (topReg < 10000) {
                    jQuery(this.inpRegion).val(this.topRegIndex[topReg].zgk + '|' + this.topRegIndex[topReg].reg).change();
                } else {
                    jQuery(this.inpRegion).val('-1');
                }
            } else if (topReg == '') {
                jQuery(this.inpRegion).val('-1');
            }
        }
    }
};

TTSuggestBoxDestination.prototype.reset = function() {
	TTSuggestBoxDestination.parent.reset.call(this);
};

TTSuggestBoxDestination.prototype.observeRegionSelects = function() {
    // Add focus selector to jQuery if not present (1.5 or lower)
    (function ( $ ) {
        var filters = $.expr[":"];
        if ( !filters.focus ) { 
            filters.focus = function( elem ) {
               return elem === document.activeElement && ( elem.type || elem.href );
            };
        }
    })( jQuery );

    // Formularfeldnamen
    if (this.isUdfRegions) {
        var zielSelectName   = this.config.zielSelectName == '' ? 'udf' : this.config.zielSelectName;
        var regionSelectName = this.config.regionSelectName == '' ? 'udfRegion' : this.config.regionSelectName;
    } else {
        var zielSelectName   = this.config.zielSelectName == '' ? 'ziel' : this.config.zielSelectName;
        var regionSelectName = this.config.regionSelectName == '' ? 'region' : this.config.regionSelectName;
    }

    var THIS              = this;
    var initializedZiel   = false;
    var initializedRegion = false;

    // Formularfeld ziel
    jQuery('#' + zielSelectName + ',select[name="' + zielSelectName + '"]').change(function(event) {
        if (initializedZiel || jQuery(this).is(':focus')) {
            THIS.setLabelText(jQuery(this).val() == '-1' ? THIS.config.anyElement.label : jQuery(this).find(':selected').text());
        }
        initializedZiel = true;
    });
    
    // Formularfeld region
    jQuery('#' + regionSelectName + ',select[name="' + regionSelectName + '"]').change(function(event) {
        if (initializedRegion || jQuery(this).is(':focus')) {
            if (jQuery(this).val() == '-1') {
                initializedZiel = true;
                jQuery('#' + zielSelectName + ',select[name="' + zielSelectName + '"]').change();
            } else {
                THIS.setLabelText(jQuery(this).find(':selected').text());
            }
        }
        initializedRegion = true;
    });
};

/**
 * Hotel SB extending TTSuggestBox
 * @class Represents the hotel suggest box.
 * @author Michael Dreher <dreher@traveltainment.de>
 * @constructor
 * @extends TTSuggestBox
 * @param {Object} oConfig Configuration object.
 */
function TTSuggestBoxHotel(oConfig, KID, CID) {
    TTSuggestBoxHotel.parent.constructor.call(this, KID, CID);
    
    this.inpZiel         = null;
    this.inpRegion       = null;
    this.isUdfRegions    = false;
    this.zgkIndex        = {};
    this.udfGroupRegions = {};
    
    this.setConfig({
        suggestDestinations: 0,
        suggestRegions:      0,
        suggestHotels:       1,
        inputName:           'suggestHotel',
        zielSelectName:      '',
        regionSelectName:    '',
        anyElement: {
            label: 'Alle Hotels'
        },
        textElement: {
            show:     false,
            position: 'last', // last/first
            label:    'Alle ' + (this.config.port == 655 ? 'Objekte' : 'Hotels') + ' mit &quot;<b>##</b>&quot; finden'
        }
    });
    this.setConfig(oConfig);
}
TTOOP.extend(TTSuggestBoxHotel, TTSuggestBox);

/**
 * 
 */
TTSuggestBoxHotel.prototype.setIbeData = function(elem) {
    //console.debug(elem);
    if (typeof this.callbacks.setIbeParams == 'function') {
        this.callbacks.setIbeParams(elem);
    } else {
        var udfTmp = elem.topRegion ? 'a|' + elem.topRegion : '';
        
        jQuery('#flugdauer,input[name="flugdauer"]').val('');
        jQuery('#zielgebiets_kenner,input[name="zielgebiets_kenner"]').val('-1');
        jQuery('#udfregionen,input[name="udfregionen"]').val(udfTmp);

        if (elem.topRegion) {
            jQuery('#topRegion,input[name="topRegion"]').val(elem.topRegion);
        }
        if (elem.iffOrt) {
            jQuery('#IFFOrt,input[name="IFFOrt"]').val(elem.iffOrt);
            jQuery('#IFFOrtSuggest,input[name="IFFOrtSuggest"]').val(elem.iffOrt);
            jQuery('#IFFVillage,input[name="IFFVillage"]').val(elem.iffOrt);
        }
        
        // Fewo: Veranstalter, Leistungscodierung setzen
        if (this.config.port == 655 && typeof elem.crs != 'undefined' && typeof elem.tva != 'undefined') {
            //jQuery('#IFF,input[name="IFF"]').val(elem.iff);
            jQuery('#IFF,input[name="IFF"]').val(elem.tva + ';' + elem.crs);
            jQuery('#CRS,input[name="CRS"]').val(elem.crs);
            jQuery('#TVA,input[name="TVA"]').val(elem.tva);
        } else {
            jQuery('#IFF,input[name="IFF"]').val(elem.iff);
        }
        
        // IFFFilter nur bei FEWO, weil hier mit IFF auf Hotelseite gesprungen wird
        if (this.config.port == 655) {
            jQuery('#IFFFilter,input[name="IFFFilter"]').val(elem.iff);
        }

        if (elem.type == 'freetext') {
            jQuery('#detail,input[name="detail"]').val('zielgebiet');
            jQuery('#shotel,input[name="shotel"]').val(elem.value);
        } else {
            if (elem.type != 'any') {
                jQuery('#detail,input[name="detail"]').val(this.config.port == 655 ? 'hotel' : 'termine');
            } else {
                jQuery('#detail,input[name="detail"]').val('hotel');
                jQuery('#IFF,input[name="IFF"]').val('');
            }
            jQuery('#shotel,input[name="shotel"]').val('');
        }
    }

    TTSuggestBoxHotel.parent.setIbeData.call(this, elem);
};

/**
 * 
 */
TTSuggestBoxHotel.prototype.setLabel = function() {
    if (typeof this.callbacks.setLabel == 'function') {
        this.config.label = this.callbacks.setLabel();
    } else if (
        typeof IBE != 'undefined' && typeof IBE.req[this.config.inputName] != 'undefined' && IBE.req[this.config.inputName] != '' && IBE.req[this.config.inputName] != this.config.label 
        && (IBE.req.detail == 'termine' || this.config.port == 655 && parseInt(jQuery('#IFFFilter,input[name="IFFFilter"]').val()) > 0 && IBE.req.detail == 'hotel')
    ) {
        this.config.label = IBE.req[this.config.inputName];
    } else if (typeof suggestBoxHotelLabel != 'undefined' && suggestBoxHotelLabel != '') {
        this.config.label = suggestBoxHotelLabel;
    }
    if (typeof this.callbacks.setLabelCallback == 'function') {
        this.config.label = this.callbacks.setLabelCallback(this.config.label);
    }
};

TTSuggestBoxHotel.prototype.reset = function() {
	TTSuggestBoxHotel.parent.reset.call(this);
};

TTSuggestBoxHotel.prototype.observeRegionSelects = function() {
    // Formularfeldnamen
    if (typeof window['ttUDF_' + this.config.port] != 'undefined') {
        this.isUdfRegions = true;
        var zielSelectName   = this.config.zielSelectName == '' ? 'udf' : this.config.zielSelectName;
        var regionSelectName = this.config.regionSelectName == '' ? 'udfRegion' : this.config.regionSelectName;

        var udfGroups     = [];
        
        // TopRegionen für UDF-Gruppen auslesen
        for (var i = 0; i < window['ttUDF_' + this.config.port].length; i++) {
            var group      = window['ttUDF_' + this.config.port][i];
            var topRegions = [];

            for (var j = 0; j < group.regions.length; j++) {
                topRegions.push(group.regions[j].topRegion);
            }
            this.udfGroupRegions[group.id] = [];
            this.udfGroupRegions[group.id].push(topRegions.join(','));
        }
    } else {
        this.isUdfRegions = false;
        var zielSelectName   = this.config.zielSelectName == '' ? 'ziel' : this.config.zielSelectName;
        var regionSelectName = this.config.regionSelectName == '' ? 'region' : this.config.regionSelectName;

        // ZGK-Index anlegen / aus SuggestBoxDestination verwenden
        for (var i = 0; i < window['TTREGS_' + this.config.port].length; i++) {
            var tmp = window['TTREGS_' + this.config.port][i].split('|');
            this.zgkIndex[tmp[1]] = {
                name: tmp[0],
                topR: tmp[3],
                reg:  tmp[2]
            };
        }
    }

    var THIS = this;
    var hasExtraRegionSelect = jQuery('#' + regionSelectName + ',select[name="' + regionSelectName + '"]').length == 1;

    // Formularfeld ziel
    var zielOnChange = function() {
        var zielVal = THIS.parseZielSelectValue(jQuery(this).val());
        if (zielVal == '-1') {
            THIS.setRegionFilter('');
        } else {
            if (THIS.isUdfRegions) {
                if (hasExtraRegionSelect) {
                    THIS.setRegionFilter(THIS.udfGroupRegions[zielVal]);
                } else {
                    zielVal = zielVal.split('|');
                    THIS.setRegionFilter(zielVal.length == 1 ? THIS.udfGroupRegions[zielVal[0]] : zielVal[1]);
                }
            } else {
                THIS.setRegionFilter(zielVal);
            }
        }
    };
    jQuery('#' + zielSelectName + ',select[name="' + zielSelectName + '"]').change(zielOnChange);
    
    // Formularfeld region
    var regionOnChange = function() {
        var regVal = THIS.parseRegionSelectValue(jQuery(this).val());
        if (regVal == '-1') {
            zielOnChange();
        } else {
            regVal = regVal.split('|');
            if (THIS.isUdfRegions) {
                THIS.setRegionFilter(regVal[1]);
            } else {
                THIS.setRegionFilter(THIS.zgkIndex[regVal[0]] ? THIS.zgkIndex[regVal[0]].topR : '');
            }
        }
    };
    jQuery('#' + regionSelectName + ',select[name="' + regionSelectName + '"]').change(regionOnChange);
    
    if (hasExtraRegionSelect) {
        regionOnChange();
    } else {
        zielOnChange();
    }
};

/**
 * Generates the element for text search with shotel.
 */
TTSuggestBoxHotel.prototype.getTextElement = function(search) {
    return {
        label:     this.config.textElement.label.replace(/\#\#/, search),
        value:     search,
        topRegion: '',
        region:    '',
        iff:       '',
        iffOrt:    '',
        type:      'freetext',
        category:  '',
        crs:       '',
        tva:       '',
        latitude:  '',
        longitude: ''
    };
};

TTSuggestBoxHotel.prototype.parseZielSelectValue = function(value) {
    if (typeof this.callbacks.parseZielSelectValue == 'function') {
        return this.callbacks.parseZielSelectValue(value);
    }
    return value;
};

TTSuggestBoxHotel.prototype.parseRegionSelectValue = function(value) {
    if (typeof this.callbacks.parseRegionSelectValue == 'function') {
        return this.callbacks.parseRegionSelectValue(value);
    }
    return value;
};

