com.bezurk.namespace('com.bezurk.hotels');

com.bezurk.hotels.LocationsAutoComplete = function() {

  function highlightNeedle(str, needle, startHighlight, endHighlight) {
    var regex = new RegExp('('+needle+')', 'gi');
    return str.replace(regex, startHighlight + '$1' + endHighlight);
  }

  /** Public methods and properties. **/
  return {
    maxResultsDisplayed : 30,
    queryDelay : 0.2,

    init : function(input, container) {
      var limit = 20; // Max. no. of results to retrieve.

      var dataSource = new YAHOO.widget.DS_JSFunction(this.dataSourceFunction);
      var autoComplete = new YAHOO.widget.AutoComplete(input, container, dataSource);

      // Configure the autoComplete widget.
      autoComplete.maxResultsDisplayed = this.maxResultsDisplayed;
      autoComplete.animVert = false;
      autoComplete.queryDelay = this.queryDelay;
      autoComplete.useShadow = false;
      autoComplete.useIFrame = true;
      autoComplete.allowBrowserAutocomplete = false;
      autoComplete.maxCacheEntries = 0;
      autoComplete.forceSelection = true;
      autoComplete.formatResult = function(result, query) {
        return com.bezurk.hotels.LocationsAutoComplete.constructLocationDisplay(result, query, true);
      };
      autoComplete.itemSelectEvent.subscribe(this.onItemSelect);
      autoComplete.dataReturnEvent.subscribe(this.onDataReturn);
      autoComplete.textboxBlurEvent.subscribe(this.onTextboxBlur);
    },

    onTextboxBlur : function(type, args) {
      var autoComplete = args[0];
      if (!autoComplete._bItemSelected) {
        var listItems = autoComplete.getListItems();
        if (listItems && listItems[0] && autoComplete.getListItemData(listItems[0])) {
          autoComplete._selectItem(listItems[0]);
        }
      }
    },

    onDataReturn : function(type, args) {
      // args[0] The auto complete object instance
      // args[1] The query string
      // args[2] Results array
      var results = args[2];
      var content = args[0]._oContainer._oContent;

      // Hack to enforce max-height in IE.
      if (results.length > 10) {
        var listElements = content.getElementsByTagName('li');
        var first = listElements[0];
        content.style.height = (10 * first.offsetHeight) + 'px';
      }

      if (results.length == 0) {
        args[0].setBody('<div class="error">No matching locations</div>');
      }
    },

    constructLocationDisplay : function(result, query, withMarkup) {
	var ValidChars = "0123456789";
	
      if (withMarkup) {

        var display = ['<div class="location-result">', highlightNeedle(result.locationName, query, '<strong>', '</strong>')];
        if (result.stateCode) {
          display = display.concat([', ', result.stateCode]);
        }
        display = display.concat([', ', highlightNeedle(result.countryName, query, '<strong>', '</strong>')]);
        if (ValidChars.indexOf(result.locationCode.charAt(0)) == -1) display = display.concat([' <span class="iata-code">(', highlightNeedle(result.locationCode, query, '<strong>', '</strong>'), ')</span>']);

        return display.join('');
      } else {

        var display = [result.locationName];
        if (result.stateCode) {
          display = display.concat([', ', result.stateCode]);
        }
  
        display = display.concat([', ', result.countryName]);
       
        if (ValidChars.indexOf(result.locationCode.charAt(0)) == -1)  display = display.concat([' (', result.locationCode, ')']);

        return display.join('');
      }
    },

    onItemSelect : function(type, args) {
      // args[0] The auto complete object instance
      // args[1] The selected <li> element item
      // args[2] The data returned for the item, either as an object,
      //         or mapped from the schema into an array
      var result = args[2];

      var textbox = args[0]._oTextbox;
      var query = textbox.value;

      textbox.value = com.bezurk.hotels.LocationsAutoComplete.constructLocationDisplay(result, query, false);
      textbox.locationName = result.locationName;
      textbox.stateCode = result.stateCode;
      textbox.countryName = result.countryName;
    },

    dataSourceFunction : function(query) {

      if (!query || query.length == 0) return [];
      var locations = [];
      var queryUpper = query.toUpperCase();
      var queryFirstLower = query.charAt(0).toLowerCase();
      var possibleLocations = com.bezurk.hotels.LocationsDataSource.getPossibleLocationsFor(query);
      if (!possibleLocations) { possibleLocations = new Array(); }
      var possibleLocationsLen = possibleLocations.length;

      for (var i = 0; i < possibleLocationsLen; i++) {
        var currLocation = possibleLocations[i];
        var currLocationName = encodeURI(currLocation.locationName).toUpperCase();
        var currCountryName = encodeURI(currLocation.countryName).toUpperCase();

        var alreadyAdded = false;

        // 1. location code (exact match)
        if (query.length == 3) {
          if (currLocation.locationCode.toUpperCase() == queryUpper) {
            locations.push(currLocation);
            alreadyAdded = true;
          }
        }

        // 2. location name (from the front)
        if (!alreadyAdded && currLocationName.indexOf(queryUpper) == 0) {
          locations.push(currLocation);
          alreadyAdded = true;
        }

        // 3. country code matches (exact match)
        if (query.length == 2) {
          if (!alreadyAdded && currLocation.countryCode.toUpperCase() == queryUpper) {
            locations.push(currLocation);
            alreadyAdded = true;
          }
        }

        // 4. country name matches (from the front)
        if (!alreadyAdded && currCountryName.toUpperCase().indexOf(queryUpper) == 0) {
          locations.push(currLocation);
          alreadyAdded = true;
        }

        // 5. state name matches (from the front)
        if (!alreadyAdded && currLocation.stateName && currLocation.stateName.toUpperCase().indexOf(queryUpper) == 0) {
          locations.push(currLocation);
          alreadyAdded = true;
        }
      }

      // Sort the locations.
      locations = locations.sort(function(x,y) {

        // Exact location code matches.
        xLocationCodeMatch = x.locationCode.toUpperCase() == queryUpper;
        yLocationCodeMatch = y.locationCode.toUpperCase() == queryUpper;
        if (xLocationCodeMatch && !yLocationCodeMatch) return -1;
        if (!xLocationCodeMatch && yLocationCodeMatch) return 1;

        // Location name matches (from the front).
        xLocationNameStartsWithToken = x.locationName.toUpperCase().indexOf(queryUpper) == 0;
        yLocationNameStartsWithToken = y.locationName.toUpperCase().indexOf(queryUpper) == 0;
        if (xLocationNameStartsWithToken && !yLocationNameStartsWithToken) return -1;
        if (!xLocationNameStartsWithToken && yLocationNameStartsWithToken) return 1;

        // Country name matches (from the front).
        xCountryNameStartsWithToken = x.countryName.toUpperCase().indexOf(queryUpper) == 0;
        yCountryNameStartsWithToken = y.countryName.toUpperCase().indexOf(queryUpper) == 0;
        if (xCountryNameStartsWithToken && !yCountryNameStartsWithToken) return -1;
        if (!xCountryNameStartsWithToken && yCountryNameStartsWithToken) return 1;

        // Group by state, country.
        if (x.countryCode > y.countryCode) return -1;
        if (x.countryCode < y.countryCode) return 1;
        if (x.stateCode && y.stateCode) {
          if (x.stateCode > y.stateCode) return -1;
          if (x.stateCode < y.stateCode) return 1;
        }
        
        // If all else fails, compare location name.
        return ((x.locationName < y.locationName) ? -1 : ((x.locationName > y.locationName) ? 1 : 0));
      });
      
      // Here implement maximum number of locations to return (hard set to 7 - evil hack but necessary, the other limits appear to be ignored)
     
      return locations.slice(0,7);
    }
  }
}();