//
// SEARCH.JS - contains all javascript functionality specific to search UI
//

// TODO: kill global vars, setup root application class, and rename this module
var currentHeaderElem = null;
var _debugTimer; // used for profiling
var _searchDlg = null;
var EAPID = "40422";
var TPID = "1";
var EXPEDIA_AFFILIATED_PRODUCT_ID = EAPID + "-" + TPID;
var REFERRER_ID = "-56698";
var ECONOMY_CLASS = 3;

// startup routines after window load
window.onload = function() {
  // fixup feedback link (spam defeat)
  $('feedback_link_anchor').writeAttribute('href', 'mail'+'to:exlefsfb'+'@'+'expedia.'+'com?subject='+escape("Experimental Flight Search Feedback"));
  
  var tripDisplayList =
    new PartialTripDisplayList(
      $('search_results_parent'),
      $('more_results')
    );
  var sortState = new SortState(TRIP_SORT_ORDERS);
  
  var doThisAfterLoadingSliderFilters = initCollapseButtons.curry({
    collapse_button_for_carrier_filter: 'carrier_filter',
    collapse_button_for_departure_time_filters: 'departure_time_filters',
    collapse_button_for_return_time_filters: 'return_time_filters'
  });
  var filters = loadFilters(doThisAfterLoadingSliderFilters);
  
  getSearchParamsFromCookie();
  
  initSearchQueryDialog(
    handleSuccess.curry(tripDisplayList, sortState, filters).bind(this),
    handleFailure.bind(this));
    
  // setup google analytics tracking
  $$('[trackable]').each(function(elm){ setupTracker(elm); });
};

var searchFieldNames = [
  'search_origin', 'search_destination',
  'search_depart_date', 'search_return_date',
  'search_adult', 'search_senior',
  'search_infant',
  'search_child', 'search_youth'
];


function getSearchParamsFromCookie() { // or default values
  var today = new Date();
  var default_depart = daysAfter(7, today);
  var default_return = daysAfter(7, default_depart);
  var minimum_depart = daysAfter(1, today);
  var minimum_return = daysAfter(2, minimum_depart);
  
  var defaults = {
    search_origin: 'SEA',
    search_destination: 'LAX',
    search_depart_date: dateToString(default_depart),
    search_return_date: dateToString(default_return),
    search_adult: '1',
    infant_radio: 'seat'
  };

  var minimum_dates = {
    search_depart_date: dateToString(minimum_depart),
    search_return_date: dateToString(minimum_return)
  };
  
  loadFormFromCookie(searchFieldNames, defaults, minimum_dates);
}

function saveFormToCookie(fields) {
  var cookieExp = { expires: new Date("August 8, 2025") };
  
  // normal fields
  fields.each(function(field) {
    field = $(field);
      YAHOO.util.Cookie.set(field.id, field.value, cookieExp );
  });
  
  // radio buttons
  YAHOO.util.Cookie.set("infant_radio", getRadioGroupVal("searchDlgForm", "infant_lap_or_seat"), cookieExp);
}

function loadFormFromCookie(fields, defaults, minimum_dates) {
  // normal fields
  fields.each(function(field) {
    field = $(field);
    var fieldCookie = YAHOO.util.Cookie.get(field.id);
    if (minimum_dates[field.id] != null && stringToDate(fieldCookie) < stringToDate(minimum_dates[field.id]))
      field.value = minimum_dates[field.id];
    else
      field.value = fieldCookie || defaults[field.id];
  });
  
  // radio buttons
  setRadioGroupVal( "searchDlgForm", "infant_lap_or_seat", YAHOO.util.Cookie.get("infant_radio") || defaults["infant_radio"]);
}


// Lots of inner functions here. TODO: give this its own class or refactor this to somewhere else
function initSearchQueryDialog(successHandler, failureHandler) {
  var flightSearchDS = new EXL.DS.FlightSearch();
  
  function handleSubmit() {
    saveFormToCookie(searchFieldNames);
    
    hideServerErrorMessage();
    if( _searchDlg.validate() )
    {
      _searchDlg.hide();

      _debugTimer = new Date();

      // map search form parameters to flightDS and make the query call
      var reqParams = {
        "FromAirport" : _searchDlg.origin_tla,
        "ToAirport" : _searchDlg.dest_tla,
        "FromDate" : _searchDlg.depart_date,
        "ToDate" : _searchDlg.return_date,
        "Class" : ECONOMY_CLASS,  // TODO: get selected class of service (economy, business, first)
        "NumAdult" : _searchDlg.num_adult,
        "NumSenior" : _searchDlg.num_senior,
        "NumYouth" : _searchDlg.num_youth,
        "NumChild" : _searchDlg.num_child,
        "NumInfant" : _searchDlg.num_infant,
        "InfantInSeat" : _searchDlg.infant_in_seat
      };
      
      // Track the event in GA
      trackEvent("searchDialog", "Submit");
      
      // Call the BFS Search
      flightSearchDS.query.bind(flightSearchDS)(reqParams, successHandler, failureHandler);
    }
  };

  function handleCancel() {
    trackEvent("searchDialog", "Cancel");  // Track the event in GA
    _searchDlg.hide();
  };
  
  function sortAirportsBySize(a,b) {
    aAirport = AIRPORTS.get(a[1].toUpperCase());
    if (aAirport == null) { 
      aAirport = METROCODES.get(a[1].toUpperCase());
    }
    bAirport = AIRPORTS.get(b[1].toUpperCase());
    if (bAirport == null) { 
      bAirport = METROCODES.get(b[1].toUpperCase());
    }

    return bAirport.size - aAirport.size;
  }
  
  function searchAirports(sQuery) {
    matchingAirports = [];
    
    if (sQuery && sQuery.length > 0 ) {
      var sQueryTestString = decodeURI(sQuery.toLowerCase());
      matchingTLAs = [];
      matchingCityNamesWordStart = [];
      matchingCityNamesFromStart = [];
      matchingAirportNamesFromStart = [];
      matchingStateAbbrevs = [];
      [METROCODES, AIRPORTS].each(function(tlray) {
        tlray.each(function(pair) {
          var cityNameWithTLA = pair.value.city_name + (pair.value.state?", " + pair.value.state:"") + " (" + pair.key + ")";
          if (pair.value.valid != false) {
            if (pair.key.toLowerCase().startsWith(sQueryTestString)) {        
              matchingTLAs.push([cityNameWithTLA, pair.key]);
            } else if (decodeURI(cityNameWithTLA.toLowerCase()).startsWith(sQueryTestString)) {        
              matchingCityNamesFromStart.push([cityNameWithTLA, pair.key]);
            } else if (pair.value.city_name.toLowerCase().include(" " + sQueryTestString) || pair.value.city_name.toLowerCase().include("/" + sQueryTestString) ) {        
              matchingCityNamesWordStart.push([cityNameWithTLA, pair.key]);
            } else if (sQueryTestString.startsWith(cityNameWithTLA.toLowerCase())) {        
              matchingCityNamesFromStart.push([cityNameWithTLA, pair.key]);
            } else if (pair.value.city_name.toLowerCase().startsWith(sQueryTestString)) {        
              matchingCityNamesFromStart.push([cityNameWithTLA, pair.key]);
            } else if (pair.value.airport_name && pair.value.airport_name.toLowerCase().startsWith(sQueryTestString)) {        
              matchingAirportNamesFromStart.push([cityNameWithTLA, pair.key]);
            } else if (pair.value.state && pair.value.state.toLowerCase().startsWith(sQueryTestString)) {        
              matchingStateAbbrevs.push([cityNameWithTLA, pair.key]);
            }
          }
        });
      });
      matchingAirports = matchingTLAs.sort(sortAirportsBySize).concat(matchingCityNamesFromStart.sort(sortAirportsBySize), matchingCityNamesWordStart.sort(sortAirportsBySize), matchingStateAbbrevs.sort(sortAirportsBySize), matchingAirportNamesFromStart.sort(sortAirportsBySize));
    }

    return matchingAirports;
  }

  function instantiateAutoComplete(oACDS, id, div) {
    this.oAutoComp = new YAHOO.widget.AutoComplete(id, div, oACDS); 
    this.oAutoComp.prehighlightClassName = "yui-ac-prehighlight"; 
    this.oAutoComp.animSpeed = 0;
    this.oAutoComp.queryDelay = 0;
    this.oAutoComp.maxResultsDisplayed = 4;
    this.oAutoComp.typeAhead = false;           // Keep false, otherwise greedy selection causes problems
    this.oAutoComp.allowBrowserAutocomplete = true;
  }

  function initAutoComplete() {
    logDuration(function() {
      YAHOO.widget.ACJSArray = new function() { 
        // Instantiate first JS Array DataSource 
        this.oACDS = new YAHOO.widget.DS_JSFunction(searchAirports); 
        // Instantiate AutoCompletes
        instantiateAutoComplete(this.oACDS, 'search_origin', 'origincontainer');
        instantiateAutoComplete(this.oACDS, 'search_destination', 'destcontainer');
      }; 
    }, "setting up autocomplete");
  };

  function initCalendars() {
    logDuration(function() {
      var retCal = new Calendar(
        'returnCal', 'returnCalContainer', 'search_return_date');
      var departCal = new Calendar('departCal', 'departCalContainer', 'search_depart_date',
        function(selDate) {
          if( selDate.compareTo(new Date($('search_return_date').value)) >= 0)  {  // move out return date if we need to
              retCal.setMinDate(selDate.addDays(1));
            	retCal.editCtrl.value = dateToString(selDate.addDays(1));
          } else {
              retCal.setMinDate(selDate.addDays(1));
          }
        });
      retCal.setMinDate(Date.today());
      retCal.setMaxDate(getMaxDate());
      departCal.setMinDate(Date.today());
      departCal.setMaxDate(getMaxDate());
    }, "setting up calendars");
  };
  
  function initTravelers() {
    logDuration(function() {
      if (YAHOO.util.Cookie.get("search_youth")*1 + YAHOO.util.Cookie.get("num_child")*1 + YAHOO.util.Cookie.get("num_infant")*1 > 0) {
        showChildren();
      }
    }, "setting up travelers");
  };

  function initButtonHandlers() {
    YAHOO.util.Event.addListener("show", "click",
      _searchDlg.show.curry(true), _searchDlg, true);
  };
  
  var dialogProperties = {
    width: "30em",
    visible: false, 
    modal: true,
    underlay: "none",
    zIndex: 15000,
    close: false,
    draggable: true
    
    // BUGBUG - leave out effects right now - callbacks that manipulate UI get hosed if effect is in transition
//    effect: { effect: YAHOO.widget.ContainerEffect.FADE, duration: 0.25 }
  };
  
  var buttonProperties = {
    submit: { text:"Search", handler: handleSubmit.bind(this), isDefault: true },
    cancel: { text:"Cancel", handler: handleCancel.bind(this) }
  };

  logDuration(function() {
    _searchDlg = new EXL.UI.SearchDialog("searchDialog", dialogProperties, buttonProperties);
    hideServerErrorMessage();
    initButtonHandlers();
  }, "setting up search dialog");
  
  // show search dialog
  $('searchDialog').style.display = 'block';
  _searchDlg.show.bind(_searchDlg, false).defer();
  
  initAutoComplete();
  initCalendars();
  initTravelers();
}

function showChildren()
{
  ['search_additional_travelers'].each(Element.show);
  return false;
}

function toggleChildren()
{
  ['search_additional_travelers'].each(Element.toggle);
  return false;
}

// This function waits until every filter in the list has fired a 'ready' callback. When all of the filters are ready, it calls fn.
function whenSliderFiltersAreReady(filters, fn) {
  var readyFilters = 0;
  function areWeThereYet() {
    readyFilters++;
    if(readyFilters >= filters.size()) fn();
  }
  filters.each(function(filter) { filter.ready = areWeThereYet; });
}

function loadFilters(filtersReadyCallback) {
  var sliderFilters = [
    new PriceFilter($('price_filter')),
    new DurationFilter($('duration_filter')),
    new DepartureTakeoffTimeFilter($('departure_takeoff_filter')),
    new DepartureLandingTimeFilter($('departure_landing_filter')),
    new ReturnTakeoffTimeFilter($('return_takeoff_filter')),
    new ReturnLandingTimeFilter($('return_landing_filter'))
  ];
  whenSliderFiltersAreReady(sliderFilters, filtersReadyCallback);
  
  var filters = [
    new CheckboxStopFilter($('stop_filter')),
    new CarrierFilter($('carrier_filter'))
  ].concat(sliderFilters);
  
  var group = new UndoCapableFilterGroup(filters);
  
  $('undo_button_id').observe('click', group.undo.bind(group));

  /* DEPRECATED
  $$('.redo_button').invoke('observe', 'click', group.redo.bind(group));
*/

  return group;
}

// Dialog callback for return of server request
// Note: "this" scope is automatically set to the dialog
var handleSuccess = function(tripDisplayList, sortState, filters, flightData) {
  var _nowTime = new Date();
  YAHOO.log("Server responded in " + (_nowTime - _debugTimer) + " milliseconds", "farescope", "farescope");
  
  if( flightData.trips.length <= 0)
  {
    handleEmptyResultset();
  }
    else
  {
    
    // change the interstitial UI
   $('no_results_message').style.display = "none";
   $('interstitial_message_num_results').update(flightData.trips.length);
   YAHOO.util.Dom.setStyle('interstitial_message_server', 'display', 'none');
   
   newResults(flightData.trips, tripDisplayList, sortState, filters);
   _nowTime = new Date();
  }
  
  YAHOO.log("UI results processed in " + (_nowTime - _debugTimer) + " milliseconds", "farescope", "farescope");
};

function handleEmptyResultset()
{
  // TODO: phase this out in favor of handleFailure
	$('no_results_message').style.display = "inline";
  startSearchQueryDialogOver();
}

function startSearchQueryDialogOver() {
  hideInterstitial();
  _searchDlg.cancel_buttons = false;
  _searchDlg.show(false);
};

// Dialog callback for server request failure
var handleFailure = function(serverResponse) {
  trackEvent("serverError", "NoResponse");  // Track the event in GA
  startSearchQueryDialogOver();
  showServerErrorMessage(serverResponse.errorMsg ? serverResponse.errorMsg : "Failed to receive a response from the proxy server. Please try again.");
};


// Introduces a new result set received from the back end.
function newResults(trips, tripDisplayList, sortState, filters) {  
	clearPerfCache();
	
  enableResultsDisplay(trips);
  tripDisplayList.setTrips(trips);
  sortState.reset();
  registerSortCallbacks(tripDisplayList, sortState);
  sortAndUpdateUI(tripDisplayList, sortState, 'price');
  
  filters.outputChange = withSpinner.curry(
    refreshResultsDisplayFromFilters.curry(tripDisplayList, filters),
    "Filtering results...");
  filters.setInputItems(trips, true);
}

function sortAndUpdateUI(tripDisplayList, sortState, sortSelector)
{
  var headerElement = 'sort_' + sortSelector;
  
  sortState.changeSortField(sortSelector);
  var sortDir = sortState.isSortReversed();

  // modify appropriate dom elements
  if( currentHeaderElem ) {
   Element.setStyle(currentHeaderElem + "_image", {visibility:"hidden"});
  }
  var headerElementImage = headerElement + "_image";
  Element.writeAttribute(headerElementImage, "src", "images/arrow-" + (sortDir ? "dn.gif" : "up.gif"));
  Element.setStyle(headerElementImage, { visibility: "visible" });
  currentHeaderElem = headerElement;

  // sort
  withSpinner(function(){
   var cmp = sortState.getSortComparator();
   tripDisplayList.setSortComparator(cmp, sortDir);
   tripDisplayList.highlightFirst();
   trackEvent("searchResults", "Sort", sortSelector);  // Track the event in GA
  }, "Sorting results...");
}

function registerSortCallbacks(tripDisplayList, sortState) {
  sortState.getSortFields().each(function(order) {
    var id = "sort_"+order;
    YAHOO.util.Event.purgeElement(id);
    YAHOO.util.Event.addListener(id, "click",
      sortAndUpdateUI.curry(tripDisplayList, sortState, order));
  });
}

function refreshResultsDisplayFromFilters(tripDisplayList, filters) {  
  var filteredResults =
    logDuration(filters.getFilteredItems.bind(filters), "filtering");

  updateOverconstraintDisplay(filteredResults);
  updateResultCount(filteredResults.length);

  tripDisplayList.showAndHide(filteredResults);
  tripDisplayList.highlightFirst();
}

function updateOverconstraintDisplay(filteredResults) {
  if(filteredResults.length == 0) {
    showOverconstraintDisplay();
  } else {
    hideOverconstraintDisplay();
  } 
}

/* DEPRECATED
function updateUndoButtons(filters) {
  showUndoButtonIf(filters.canUndo());
  showRedoButtonIf(filters.canRedo());
}
*/
