/**
 * Common vehicle filtering code.
 * 
 * Expects the including page to contain:
 * - A form called "filtersForm"
 * - A div called vehicleDiv
 * - A div called vehicleCountDiv
 * - A div called statusDiv
 *
 * @author Neil Thier (c) Versata 2006
 */

// The set of vehicle fields to use in the filters
var arrFilterFields;

// The current list start index
var intListIndex = 0;
// The "original/full" set of vehicles, pre-filtering
var arrVehicles;
// The currently filtered set of vehicles
var arrVehiclesFiltered;

// Default number of vehicles to show per page
var intPageSize = 10;

// Define which direction we're sorting.  1 == default, -1 == reverse
var sortOrder = 1;

// A function pointer that can be used for post-processing of 
// a returned vehicle list.  Null by default.
var postProcessVehicleList = null;

/**
 * Object representing a filter in the UI.
 * @param strName both the name of the field on the vehicle and the name of the form field
 *  in the UI
 * @param boolPrepopulated whether this field was populated manually.  If true,
 *  nothing is done to it.  Otherwise it will be filled with all distinct values 
 *  found on the vehicles.
 * @param objMatcher a function pointer to the type of matcher to use to determine if a 
 *  vehicle satisfied the filter.
 */
function FilterField(strName, boolPrepopulated, objMatcher) {
  this.name = strName;
  this.prepopulated = boolPrepopulated;
  this.matcher = objMatcher;
}

/**
 * Return true iff the vehicle satisfies the filter
 */
function propertyMatcher(objFilterField, objVehicle) {
  var select = document.forms['filtersForm'].elements[objFilterField.name];
  // Check for it being the All selection
  if (select.selectedIndex == 0) {
    return true;
  }
  var selected = select.options[select.selectedIndex].value;
  
  if (objVehicle[objFilterField.name] == selected) {
    return true;
  }
  return false;
}

/**
 * Return true iff the vehicle satisfies the filter
 */
function numericalRangeMatcher(objFilterField, objVehicle) {
  var select = document.forms['filtersForm'].elements[objFilterField.name];
  // Check for it being the All selection
  if (select.selectedIndex == 0) {
    return true;
  }
  var selected = select.options[select.selectedIndex].value;
  
  // Get the min/max of the range.  Assume it's split by a -
  if (selected.indexOf("-") < 1) {
    // No range here
    return true;
  }

  // Get the numbers for comparison
  var arrMinMax = selected.split("-");
  var intMin = parseInt(arrMinMax[0]);
  var intMax = parseInt(arrMinMax[1]);
  var intValue = parseInt(objVehicle[objFilterField.name]);

  // Do a simple error check, some vehicle don't have prices, so exclude
  // those on any comparisons
  if (isNaN(intValue)) {
    return false;
  }

  if (intValue >= intMin && intValue <= intMax) {
    return true;
  }
  return false;
}

/**
 * Return true iff the vehicle satisfies the filter
 */
function checkboxMatcher(objFilterField, objVehicle) {
  var input = document.forms['filtersForm'].elements[objFilterField.name];
  // If it's not checked, treat it as a non-filter
  if (input.checked == false) {
    return true;
  }

  // Check for various styles of "true" values in the vehicle properties
  var strVal = objVehicle[objFilterField.name];
  
  // TODO: The true values assumed (Y,true,1,5) are usage dependent. The checkboxMatcher function 
  // Should have a set of true values passed to it when it is instantiated, which tells us that we
  // should be using OO JS for filtering
  if (input.checked && (strVal == "Y" || strVal == "true" || strVal == "1" || strVal == "5")) {
    return true;
  }
  return false;
}

/**
 * Return true iff the vehicle satisfies the filter
 */
function inputMatcher(objFilterField, objVehicle) {
  var input = document.forms['filtersForm'].elements[objFilterField.name];
  // Check for it having no data in the field
  if (input.value.length == 0) {
    return true;
  }

  if (objVehicle[objFilterField.name] == input.value) {
    return true;
  }
  return false;
}

/**
 * Sort function for the vehicles list.
 */
function sortVehicles(objVehA, objVehB) {
  // Find out what criteria we're searching on
  var select = document.forms['sortForm'].elements["sortSelect"];
  // Get the name of the vehicle property field to sort on
  var selected = select.options[select.selectedIndex].value;

  if (objVehA[selected] == objVehB[selected]) {
      return 0;
  }
  // Check if it's a number, if so we'll need to parse it.
  if (isNaN(objVehA[selected]) || isNaN(objVehB[selected]) || objVehA[selected] == "" || objVehB[selected] == "") {
    if (objVehA[selected] > objVehB[selected]) {
      return 1 * sortOrder;
    } else if (objVehA[selected] < objVehB[selected]) {
      return -1 * sortOrder;
    }
  } else {
    // Parse the numbers
    var intA = parseInt(objVehA[selected]);
    var intB = parseInt(objVehB[selected]);
    if (intA > intB) {
      return 1 * sortOrder;
    } else if (intA < intB) {
      return -1 * sortOrder;
    }
  }
  return 0;
}

/**
 * Rebuild the non-prepopulated filter dropdowns from the list of
 * vehicles provided.
 */
function rebuildFilters(arrVehicleList) {
  var arrFieldValues = new Array();

  for (var i = 0; i < arrFilterFields.length; i++) {
    arrFieldValues[i] = new Array();
  }

  // We'll step through the list of vehicles and build associative
  // arrays of the values
  for (var v = 0; v < arrVehicleList.length; v++) {
    for (var f = 0; f < arrFilterFields.length; f++) {
      if (!arrFilterFields[f].prepopulated) {
        if (arrFieldValues[f][arrVehicleList[v][arrFilterFields[f].name]] == null) {
          arrFieldValues[f][arrVehicleList[v][arrFilterFields[f].name]] = arrVehicleList[v][arrFilterFields[f].name];
        }
      }
    }
  }

  // Now populate the field values in the select boxes
  for (var f = 0; f < arrFilterFields.length; f++) {
    var select = document.forms['filtersForm'].elements[arrFilterFields[f].name];
    // See if it's a select box.  It may be an input or checkbox
    if (select.selectedIndex != null) {
        // It's a select box
      if (!arrFilterFields[f].prepopulated) {
        // Clear the existing elements
        select.length = 1;
        for (var value in arrFieldValues[f]) {
          select.options[select.options.length] = new Option(value, value);
        }
      } else {
        // Just reset to the first element here
        select.selectedIndex = 0;
      }
    } else if (select.type != null && select.type == "text") {
      select.value = "";
    } else if (select.type != null && select.type == "checkbox") {
      select.checked = false;
    }
  }
}

/**
 * Function to filter a set of vehicles based on a set of 
 * filter criteria.
 */
function filterVehicles(arrVehicleList) {
  if (arrVehicleList == null) {
    return null;
  }

  var arrFilteredList = new Array();
  for (var v = 0; v < arrVehicleList.length; v++) {
    var excluded = false;
    for (var f = 0; f < arrFilterFields.length; f++) {
      if (!arrFilterFields[f].matcher(arrFilterFields[f], arrVehicleList[v])) {
        excluded = true;
        break;
      }
    }
    if (!excluded) {
      arrFilteredList[arrFilteredList.length] = arrVehicleList[v];
    }
  }    
  return arrFilteredList;
}

/**
 * Function to filter the current set of vehicles based on the currently
 * selected filter values in the UI select boxes.
 *
 * Expects there to be elements of the form 'filtersForm' with the names
 * of each of the values in arrFilterFields.
 */
function filterCurrentVehicles() {
  if (arrVehicles == null) {
    return;
  }

  // Reset the list to the start
  intListIndex = 0;

  arrVehiclesFiltered = filterVehicles(arrVehicles);
  arrVehiclesFiltered.sort(sortVehicles);  

  renderVehicleList(arrVehiclesFiltered, 0, intPageSize, document.getElementById("vehicleListDiv"), document.getElementById("vehicleCountDiv"));
}

/**
 * Function to sort the current filtered vehicle list
 */
function sortCurrentVehicles() {
  if (arrVehiclesFiltered == null) {
    return;
  }

  // Reset the list to the start
  intListIndex = 0;

  arrVehiclesFiltered.sort(sortVehicles);  

  renderVehicleList(arrVehiclesFiltered, 0, intPageSize, document.getElementById("vehicleListDiv"), document.getElementById("vehicleCountDiv"));
}

function clearFilters() {
  rebuildFilters(arrVehicles);
  filterCurrentVehicles();
  renderVehicleList(arrVehiclesFiltered, 0, intPageSize, document.getElementById("vehicleListDiv"), document.getElementById("vehicleCountDiv"));
}

/**
 * Reverse the current sort order
 */
function reverseSortOrder() {
  sortOrder = sortOrder * -1;
  sortCurrentVehicles();
}

/**
 * Function to update the page numbers links based on the current list of 
 * vehicles.
 */
function updatePageNumbers(objNumbersDiv, objNumbersDivBottom) {
  var curPage = (intListIndex / intPageSize) + 1;
  var numPages = Math.ceil(arrVehiclesFiltered.length / intPageSize);

  var strHTML = "";
  // Only show some of the pages
  var intMinPage = Math.max(1, curPage - 3);
  var intMaxPage = Math.min(numPages, curPage + 3);
  if (intMinPage > 1) {
    strHTML += "... ";
  }
  for (var i = intMinPage; i <= intMaxPage; i++) {
    if (i != curPage) {
      strHTML += "<a class='text' href='javascript:toVehiclePage(" + i + ")'>" + i + "</a> ";
    } else {
      strHTML += i + " ";
    }
  }
  if (intMaxPage < numPages) {
    strHTML += " ...";
  }
  objNumbersDiv.innerHTML = strHTML;

  if (objNumbersDivBottom != null) {
    objNumbersDivBottom.innerHTML = strHTML;
  }
}

function nextVehiclePage() {
  if (intListIndex + intPageSize + 1 > arrVehiclesFiltered.length) {
    // Do nothing
  } else {
    intListIndex += intPageSize;
    renderVehicleList(arrVehiclesFiltered, intListIndex, intListIndex+intPageSize, document.getElementById("vehicleListDiv"), document.getElementById("vehicleCountDiv"));
  }
}

function previousVehiclePage() {
  if (intListIndex - intPageSize < 0) {
    // Do nothing
  } else {
    intListIndex -= intPageSize;
    renderVehicleList(arrVehiclesFiltered, intListIndex, intListIndex+intPageSize, document.getElementById("vehicleListDiv"), document.getElementById("vehicleCountDiv"));
  }
}

function toVehiclePage(intPage) {
  intListIndex = intPageSize * (intPage-1);
  renderVehicleList(arrVehiclesFiltered, intListIndex, intListIndex+intPageSize, document.getElementById("vehicleListDiv"), document.getElementById("vehicleCountDiv"));
}

function showStatusMessage(strMessage) {
  var status = document.getElementById("statusDiv");
  status.innerHTML = "<p>" + strMessage + "</p>";
  status.style.visibility = "visible";
  showIFrameMask("belowStatusDiv", "statusDiv");
}

function hideStatusMessage() {
  var status = document.getElementById("statusDiv");
  status.style.visibility = "hidden";
  hideIFrameMask("belowStatusDiv");
}

/**
 * Function for the Yahoo AJAX toolkit.  This gets called when the AJAX call returns
 * with success.
 */
var handleSuccess = function(o) {
  result = JSON.parse(o.responseText);
  
  if(result == null){
    hideStatusMessage();
    var listDiv = document.getElementById("vehicleListDiv");
    listDiv.innerHTML = "<div class='error'>Error retrieving vehicle list.  Please try again later.</div>";
    return;
  }
  
  intListIndex = 0;
  arrVehicles = result.vehicles;
  arrVehiclesFiltered = arrVehicles;
  arrVehiclesFiltered.sort(sortVehicles);
  rebuildFilters(arrVehicles);
  renderVehicleList(arrVehiclesFiltered, 0, intPageSize, document.getElementById("vehicleListDiv"), document.getElementById("vehicleCountDiv"));
  if (postProcessVehicleList != null) {
    postProcessVehicleList();
  }
  hideStatusMessage();
}

/**
 * Function for the Yahoo AJAX toolkit.  This gets called when the AJAX call returns
 * with failure.
 */
var handleFailure = function(o) {
  hideStatusMessage();
  var listDiv = document.getElementById("vehicleListDiv");
  listDiv.innerHTML = "<div class='error'>Error retrieving vehicle list.  Please try again later.</div>";
}

/**
 * An object for the Yahoo AJAX toolkit.
 */
var callback =
{
  success: handleSuccess,
  failure: handleFailure
};

/**
 * Opens a popup window with the payment estimator for the specified vehicle.
 * @param intIndex index into the arrVehiclesFiltered array
 */
function openPaymentEstimatorForVehicle(intIndex, extURL) {
  // Only called for new vehicles
  var strMake = null;
  for(var i=0;i<arrModels.length;i++)
  {
    if(arrModels[i].id == strModelInfoId)
    {
      strMake = arrModels[i].make;
    }
  }
  var objVehicle = arrVehiclesFiltered[intIndex];
  openPaymentEstimator(objVehicle.ModelName, objVehicle.Price, objVehicle.Year, objVehicle.BodyDesc, 0, strMake, objVehicle.VIN, "N", extURL);
}

/**
 * Change the number of vehicles per page.
 * @param the select object.  Will use the selected number from here.
 */
function changeVehiclesPerPage(objSelect) {
  var numPerPage = objSelect.options[objSelect.selectedIndex].value;
  intPageSize = parseInt(numPerPage);
  filterCurrentVehicles();
}

/**
 * Function to pre-select filter values based on request parameters.
 * Note that filtering doesn't happen based on the onchange events of 
 * form elements, so rerunning the filter will have to be triggered 
 * externally.
 */
function preselectFiltersFromRequest(strFormName) {
  var requestParams = getRequestParams();

  for (var param in requestParams) {
    // See if that's a real form field
    var objField = document.forms[strFormName].elements[param];
    // Only need to support select boxes for now, so check for a selectedIndex
    if (objField != null && objField.selectedIndex != null) {
      for (var i = 0; i < objField.options.length; i++) {
        if (relaxedCompare(objField.options[i].value,requestParams[param])) {
          // Set the selected index of the field
          objField.selectedIndex = i;
        }
      }
    }
  }  
}

/*
 * Used for a relaxed(see stripString) String comparison
 */
function relaxedCompare(str1,str2)
{
  return stripString(str1) == stripString(str2);
}

/*
 * Strips off '%20', '%26', '+' and spaces from  string
 */
function stripString(str)
{
  var tempStr = str.replace(/%20/g,'');
  tempStr = tempStr.replace(/%26/g,'&');
  tempStr = tempStr.replace(/\+/g,'');
  tempStr = tempStr.replace(/ /g,'');
  tempStr = tempStr.toLowerCase()
  return tempStr;
}
