
/**
 * GuidedByALocal search object
 * @author Mattijs Hoitink <mattijs@voidwalkers.nl>
 */
function gblSearch(locations) {

    this.locations = locations;
    this.form = null;
    this.elementPrefix = '';
    this.elements = [];
    this.searchUrlBase = null;
    
}

/**
 * Sets the base url for searching.
 * @param urlBase The base url
 */
gblSearch.prototype.setSearchUrlBase = function(urlBase) {
    this.searchUrlBase = urlBase;
}

gblSearch.prototype.getSearchUrlBase = function() {
    return this.searchUrlBase;
}

gblSearch.prototype.getElement = function(elementBaseName) {
    var constructedElementId = this.elementPrefix + elementBaseName;

    for (elementId in this.elements) {
        if (elementId == constructedElementId) {
            return this.elements[elementId];
        }
    }

    return null;
}

/**
 * Returns the parameters for the search
 * @return list
 */
gblSearch.prototype.getParameters = function() {
    var parameters = [];

    for (elementIndex in this.elements) {
        var element = this.elements[elementIndex];
        parameters[element.id] = $(element).children('option:selected').html();
    }

    return parameters;
}

/**
 * Attach function
 * @param formId The id of the form
 */
gblSearch.prototype.attach = function(formId) {
    var search = this;

    search.form = $('#' + formId);
    $(search.form).submit(function() {
        // If there is a callback function, let the function handle the submit
        if (search.formSubmit) {
            search.formSubmit(search.form);
            return false;
        }

        // Else continue with a normal submit
        return true;
    });

    // Loop the select elements
    $('#' + formId + ' select').each(function(index, element) {
        // Store a reference
        search.elements[element.id] = element;

        // Add a callback
        $(element).change(function() {
            search.elementChanged(this);
        });

    });
}

/**
 * Checks for a callback function for the the changed element.
 * @param element The changed element
 */
gblSearch.prototype.elementChanged = function(element) {
    var updateFunction = eval('this.' + element.id + 'Changed');

    if(typeof updateFunction == 'function') {
        updateFunction(element);
    }
}

/**
 * Updates countryElement with countries from continentId
 * @param continentId The id of the continent to get the countries from
 * @param countryElement The country element to update
 */
gblSearch.prototype.updateCountries = function(continentId, countryElement) {
    if(null == continentId) {
        return;
    }
    
    if (null == countryElement) {
        // Try to get the default country element
        if(null != this.getElement('country')) {
            countryElement = this.getElement('country');
        } else {
            return;
        }
    }

    var countries = this.getCountries(continentId);
    $(countryElement).empty();
    $(countryElement).append(
        $('<option></option>')
         .attr('value', '0')
         .attr('label', 'Choose country')
         .html('Choose country')
    );
    for (var key in countries) {
        var country = countries[key];
        $(countryElement).append(
            $('<option></option>')
             .attr('value', country.id)
             .attr('label', country.name)
             .html(country.name)
        );
    }

    // Country element changed, send changed event
    this.elementChanged(countryElement);
}

/**
 * Updates the citiesElement with cities from a country, filtered by region.
 * @param countryId The id of the country to get the cities from
 * @param citiesElement The cities element to update
 * @param region The region to filter the cities on, defaults to 'all'
 * @return list
 */
gblSearch.prototype.updateCities = function(countryId, citiesElement, region) {
    // Check for countryId
    if(null == countryId) {
        return;
    }

    // Check if cities element is supplied, or default is set
    if(null == citiesElement) {
        if(null != this.getElement('city')) {
            citiesElement = this.getElement('city');
        } else {
            return;
        }
    }

    // Check for region, else set to all
    if(null == region) {
        region = 'all';
    }

    var cities = this.getCities(countryId, region);
    $(citiesElement).empty().append(
        $('<option></option>')
         .attr('value', 'all')
         .attr('label', 'All')
         .html('All')
    );

    for (var key in cities) {
        var city = cities[key];
        $(citiesElement).append(
            $('<option></option>')
             .attr('value', city.name.toLowerCase())
             .attr('label', city.name.toLowerCase())
             .html(city.name)
        );
    }

    // Cities changed, send changed event
    this.elementChanged(citiesElement);
}

/**
 * Updates the languages for a specific level. Levels
 * <ul>
 *   <li>default</li>
 *   <li>continent</li>
 *   <li>country</li>
 *   <li>city</li>
 * </ul>
 */
gblSearch.prototype.updateLanguages = function(level, languageElement, continentId, countryId, cityName) {
    
    var languages = [];
    
    switch(level) {
        case 'continent':
            if (null == continentId) { alert('Missing continentId for updating continent languages'); }
            
            if (null != this.locations[continentId]) {
                languages = this.locations[continentId].languages;
            }
            break;
        case 'country':
            if (null == continentId) { alert('Missing continentId for updating country languages'); }
            if (null == countryId) { alert('Missing countryId for updating country languages'); }
            
            var country = this.getCountry(countryId, continentId);
            if (null != country) {
                languages = country.languages;
            }
            
            break;
        case 'city':
            if (null == continentId) { alert('Missing continentId for updating city languages'); }
            if (null == countryId) { alert('Missing countryId for updating city languages'); }
            if (null == cityName) { alert('Missing cityName for updating city languages'); }

            var city = this.getCity(cityName, countryId, continentId);

            if (null != city) {
                languages = city.languages;
            }

            /*for (var continent in this.locations) {
                for (var country in this.locations[continent].countries) {
                    for (var cityId in this.locations[continent].countries[country].cities) {
                        city = this.locations[continent].countries[country].cities[cityId];
                        if (identifier.toLowerCase() == city.name.toLowerCase()) {
                            languages = city.languages;
                            break;
                        }
                    }
                }
            }*/
            break;
        case 'default':
        default:
            break;
    }

    $(languageElement).empty().append(
        $('<option></option>')
         .attr('value', 'any')
         .attr('label', 'Any')
         .html('Any')
    );

    for (var key in languages) {
        var language = languages[key];
        $(languageElement).append(
            $('<option></option>')
             .attr('value', language.name.toLowerCase())
             .attr('label', language.name)
             .html(language.name)
        );
    }
}

/**
 * Returns all countries for a continent.
 * @param continentId The id of the continent.
 * @return list
 */
gblSearch.prototype.getCountries = function(continentId) {
    return this.locations[continentId].countries;
}

/**
 * Returns a country from a continent
 * @param countryId The id of the country
 * @param continentId The id of the continent. If no continent is provided all continents will be searhed
 * @return list
 */
gblSearch.prototype.getCountry = function(countryId, continentId) {
    if (continentId != null && this.locations[continentId].countries[countryId] != null) {
        return this.locations[continentId].countries[countryId];
    }
    else {
        for (var continent in this.locations) {
            if(null != this.locations[continent].countries[countryId]) {
                return this.locations[continent].countries[countryId];
            }
        }
    }

    return null;
}

/**
 * Returns a country by ISO country code
 * @param countryCode A country code like NL
 * @object
 */
gblSearch.prototype.getCountryByCode = function(countryCode) {
    if (countryCode != 'unk' || countryCode != 'region'){
        for (var continent in this.locations) {
            for (var country in this.locations[continent].countries){
                if(countryCode == this.locations[continent].countries[country].code) {
                    return this.locations[continent].countries[country];
                }
            }
        }
    }

    return null;
}

/**
 * Returns all cities for a country, filtered by region
 * @param countryId The id for the country
 * @param region The region name, defaults to all
 * @return list
 */
gblSearch.prototype.getCities = function(countryId, region) {
    if(null == countryId) {
        return [];
    }

    if(null == region) {
        region = 'all';
    }

    var country = this.getCountry(countryId);
    if(null == country) {
        return [];
    }

    if (region.toLowerCase() != 'all') {
        var cities = {};
        for (var cityKey in country.cities) {
            var city = country.cities[cityKey];
            if (city.region != null && city.region.toLowerCase() == region.toLowerCase()) {
                cities[city.name.toLowerCase()] = {'name': city.name, 'id': city.id, 'region': city.region.toLowerCase()};
            }
        }
        
        return cities;
    }
    else {
        return country.cities;
    }
}

/**
 * Returns city data. Country and Continent id need to be provided to deal with
 * duplicate city names in different countries/continents.
 * @param cityName
 * @param countryId
 * @param continentID
 * @return object
 */
gblSearch.prototype.getCity = function(cityName, countryId, continentId) {
    var country = this.getCountry(countryId, continentId);

    if (null !== country) {
        for(var cityIndex in country.cities) {
            var city = country.cities[cityIndex];
            if (city.name.toLowerCase() == cityName.toLowerCase()) {
                return city;
            }
        }
    }

    return null;
}


gblSearch.prototype.showMessage = function(message) {
    var formId = $(this.form).attr('id');
    
    var messageContainer = $('#' + formId + ' > .form-messageContainer');
    if (undefined != messageContainer) {
        $(messageContainer).html(message);
    }
}

gblSearch.prototype.clearMessages = function() {
    var formId = $(this.form).attr('id');

    var messageContainer = $('#' + formId + '> .form-messageContainer');
    if (undefined != messageContainer) {
        $(messageContainer).empty();
    }
}

/**
 * Object to parse the querystring.
 * @author Mattijs Hoitink <mattijs@voidwalkers.nl>
 */
function Querystring(qs) { // optionally pass a querystring to parse
    this.params = {};

    // Add function
    this.add = function(key, value) {
        this.params[key] = value;
    }

    // Contains function
    this.contains = function(key) {
        var value = this.params[key];
        return (value != null);
    }

    // Get function
    this.get = function(key, default_) {
        var value = this.params[key];
        return (value != null) ? value : default_;
    }

    // Build function
    this.build = function() {
        var pairs = new Array();
        for (var key in this.params) {
            pairs.push(key + '=' + this.params[key]);
        }
        if(pairs.length == 0) {
            return '';
        }
        return '?' + pairs.join('&');
    }

    if (qs == null) qs = location.search.substring(1, location.search.length);
    if (qs.length == 0) return;

    // Turn <plus> back to <space>
    // See: http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4.1
    qs = qs.replace(/\+/g, ' ');
    var args = qs.split('&'); // parse out name/value pairs separated via &

    // split out each name=value pair
    for (var i = 0; i < args.length; i++) {
        var pair = args[i].split('=');
        var name = decodeURIComponent(pair[0]);

        var value = (pair.length==2) ? decodeURIComponent(pair[1]) : name;
        this.params[name] = value;
    }

}
