// This file depends on localized strings and functions currently located in:
// resources/new/<locale>/js/properties.js

var ALL_VALUE = "";
var ALL_VALUES_RE = /^(All||Any)?$/i;
// Chained basic search criteria: used for dropdown names, HTML div names, and QuickSearch property names
var SEARCHTYPE_NAME = "search";
var SEARCHTYPE_ROW = "qsSearchType";
var LOCATION_NAME = "location";
var LOCATION_ROW = "qsLocation";
var MAKE_NAME = "make";
var MAKE_ROW = "qsMake";
var MODEL_NAME = "model";
var MODEL_ROW = "qsModel";
var TRIM_NAME = "trim";
var TRIM_ROW = "qsTrim";
var VEHICLETYPE_NAME = "body_type";
var VEHICLETYPE_ROW = "qsBody_type";
// Handy collections of fields
var CHAINED_FIELDS = [LOCATION_NAME, VEHICLETYPE_NAME, MAKE_NAME, MODEL_NAME, TRIM_NAME];
var allFields = null;
var criteriaList = null;

/** Encapsulates everything public in the search API. **/
function QuickSearch(jsDataObj) {
	this.jsdata = jsDataObj;
	this.searchForm = null;
	this.showNullSearchTypes = true;
	// initialize allFields to have advanced search criteria if necessary
	allFields = CHAINED_FIELDS;
	// populate properties with any preselected values, or "all"
	criteriaList = new CriteriaList();
	for (var i=0; i<allFields.length; i++) {
		criteriaList.add(new QuickSearchCriteria(allFields[i], ALL_VALUE, i));
	}
}
/** Get the showNullSearchTypes property */
QuickSearch.prototype.getShowNullSearchTypes = function() { return this.showNullSearchTypes; }
/** Disable and show message in drop downs if no inventory data is available */
QuickSearch.prototype.handleNoInventory = function(startCriteriaName) {
	if (!startCriteriaName) {
		this.searchForm[SEARCHTYPE_NAME].options.length = 0;
		this.searchForm[SEARCHTYPE_NAME].options[0] = new Option(INV_NOINVENTORYAVAILABLE, "", true, true);
		this.searchForm[SEARCHTYPE_NAME].disabled = true;
		var startCriteriaName = LOCATION_NAME;
	}
    for (var i=criteriaList[startCriteriaName].index; i<allFields.length; i++) {
		if (this.searchForm[allFields[i]]) {
			criteriaList[allFields[i]].setOptions(this.searchForm[allFields[i]], [], new Option(INV_NOINVENTORYAVAILABLE, ""));
			this.searchForm[allFields[i]].disabled = true;
		}
	}
	return;
}
QuickSearch.prototype.init = function(searchForm) {
	this.searchForm = (searchForm) ? searchForm : document.forms[0];
	// verify the jsdata object has data
	var hasData = false;
	for (var searchType in this.jsdata){
		hasData = true;
		this["has" + searchType.capitalize()] = false;
		for (var location in this.jsdata[searchType]) {
			this["has" + searchType.capitalize()] = true;
			break;
		}
	}
	// if jsdo has no data, disable all fields and display "no inventory" message on search pages
	if (!hasData) {
		this.jsdata = new Object();
		this.handleNoInventory();
	}
	// initialize search criteria
	this.initCriteria();
}
/** Initialize the "All" options for each search criteria, and populate the searchType dropdown */
QuickSearch.prototype.initCriteria = function() {
	this.populateSearchType();
	// set up chained criteria customized "All" options
	criteriaList[LOCATION_NAME].setAllOption(new Option(INV_ALLLOCATIONS, ALL_VALUE, true, true));
	criteriaList[VEHICLETYPE_NAME].setAllOption(new Option(INV_ALLVEHICLETYPES, ALL_VALUE, true, true));
	criteriaList[MAKE_NAME].setAllOption(new Option(INV_ALLMAKES, ALL_VALUE, true, true));
	criteriaList[MODEL_NAME].setAllOption(new Option(INV_ALLMODELS, ALL_VALUE, true, true));
	criteriaList[TRIM_NAME].setAllOption(new Option(INV_ALLTRIMS, ALL_VALUE, true, true));
}
/** Populate the searchType dropdown with labels for each key in QuickSearch.jsdo */
QuickSearch.prototype.populateSearchType = function() {
	if (this.searchForm[SEARCHTYPE_NAME]) {
		this.searchForm[SEARCHTYPE_NAME].options.length = 0;
		for (var type in this.jsdata) {
			if (!this.showNullSearchTypes && !this["has" + type.capitalize()]) continue;
			this.searchForm[SEARCHTYPE_NAME].options[this.searchForm[SEARCHTYPE_NAME].options.length] = new Option(INV_SEARCHTYPELABEL[type], type);
		}
		if (this.searchForm[SEARCHTYPE_NAME].options.length == 0) this.handleNoInventory();
	}
}
/* Call in the onClick of the search dropdown */
QuickSearch.prototype.setSearchType = function(newType) {
	this.searchForm[SEARCHTYPE_NAME].value = newType;
	if (!this["has" + newType.capitalize()]) {
		this.handleNoInventory(LOCATION_NAME);
		return;
	}
	criteriaList.resetCriteria(LOCATION_NAME);
	criteriaList[LOCATION_NAME].nodeList[criteriaList[LOCATION_NAME].nodeList.length] = this.jsdata[newType];
	criteriaList[LOCATION_NAME].addToValueMap(this.jsdata[newType]);
	criteriaList[LOCATION_NAME].resetNodeLists();
	criteriaList[LOCATION_NAME].set(this.searchForm);
}
/** Set the showNullSearchTypes property */
QuickSearch.prototype.setShowNullSearchTypes = function(showNullSearchTypes) { this.showNullSearchTypes = showNullSearchTypes; }
/* Call in the onClick of the location dropdown */
QuickSearch.prototype.setLocation = function(newValue) {
	criteriaList[LOCATION_NAME].value = newValue;
	criteriaList[LOCATION_NAME].resetNodeLists();
	criteriaList[VEHICLETYPE_NAME].set(this.searchForm);
}
/* Call in the onClick of the vehicleType dropdown */
QuickSearch.prototype.setVehicleType = function(newValue) {
	criteriaList[VEHICLETYPE_NAME].value = newValue;
	criteriaList[VEHICLETYPE_NAME].resetNodeLists();
	criteriaList[MAKE_NAME].set(this.searchForm);
}
/* Call in the onClick of the make dropdown */
QuickSearch.prototype.setMake = function(newValue) {
	criteriaList[MAKE_NAME].value = newValue;
	criteriaList[MAKE_NAME].resetNodeLists();
	criteriaList[MODEL_NAME].set(this.searchForm);
}
/* Call in the onClick of the model dropdown */
QuickSearch.prototype.setModel = function(newValue) {
	criteriaList[MODEL_NAME].value = newValue;
	criteriaList[MODEL_NAME].resetNodeLists();
	criteriaList[TRIM_NAME].set(this.searchForm);
}
/* Call in the onClick of the trim dropdown */
QuickSearch.prototype.setTrim = function(newValue) {
	criteriaList[TRIM_NAME].value = newValue;
}
/** Submits the search form. 
	Adds data to the URL to support Traffic Reporter data gathering.
	POST data is not available in the Apache log, so we need to add it here. **/
QuickSearch.prototype.submitForm = function(){
	var makeParam = "&make=" + escape(criteriaList[MAKE_NAME].getSelectedValue(this.searchForm));
	var modelParam = "&model=" + escape(criteriaList[MODEL_NAME].getSelectedValue(this.searchForm));
	var trimParam = "&trim=" + escape(criteriaList[TRIM_NAME].getSelectedValue(this.searchForm));
	var searchTypeParam = "&searchVal=" + this.searchForm.search.value;
	//this variable is used to track new searches as opposed to paging through results
	var searchFreshParam = "&searchFresh=yes"
	this.searchForm.action = this.searchForm.action + makeParam + modelParam + trimParam + searchTypeParam + searchFreshParam;
	this.searchForm.submit();
}

/** Encapsulates one criteria in the inventory search dropdown. **/
function QuickSearchCriteria(name, value, index) {
	this.name = name;
	this.value = value;
	this.index = index;
	this.option = null;
	this.valueMap = {};
	this.nodeList = [];
	this.isPreSorted = false;
	return this;
}
/** Add values in an array to this criteria's valueMap. **/
QuickSearchCriteria.prototype.addToValueMap = function(node) {
	for (var key in node) this.valueMap[key] = node[key][0];
}
/** Do not populate model/trim dropdowns till a make has been selected. **/
QuickSearchCriteria.prototype.doPopulate = function() {
	return (!(((this.name == MODEL_NAME) && (criteriaList[MAKE_NAME].value == ALL_VALUE)) || ((this.name == TRIM_NAME) && (criteriaList[MODEL_NAME].value == ALL_VALUE))));
}
QuickSearchCriteria.prototype.doTruncate = function(optionsLength) { return (((this.name == MAKE_NAME) || (this.name == MODEL_NAME)) && (optionsLength == 1)); }
/** Create list of Options for this criteria, from a key/value map. **/
QuickSearchCriteria.prototype.getOptions = function() {
	var optionList = new Array();
	for (var key in this.valueMap) {
		if (key.trim()) optionList[optionList.length] = new Option(this.valueMap[key], key);
	}
	return optionList;
}
/** Get the value of the dropdown selection, or if it's blank, the value of the criteria object. **/
QuickSearchCriteria.prototype.getSelectedValue = function(someForm) {
	if (someForm[this.name]) return someForm[this.name].value;
	else return this.value;
}
/** Reset all fields holding selection and state values. **/
QuickSearchCriteria.prototype.reset = function() {
	this.nodeList = [];
	this.valueMap = {};
	this.value = ALL_VALUE;
}
/** Call this once per user click, for the criteria selected.  Initiate the node recursion. **/
QuickSearchCriteria.prototype.resetNodeLists = function() {
	// reset the nodeList of every criteria below the one being set (the one the user clicked).
	criteriaList.resetCriteria(allFields[this.index + 1]);
	// start recursing through each node in the nodeList, to set nodeLists down the food chain.
	for (var j=0; j<this.nodeList.length; j++) this.recurseNodes(this.nodeList[j], (this.index + 1));
}
/** Create a list of nodes for each criteria following the one clicked, filtered by selected values.
	Optionally populate valueMaps with the node data.  Also initiate advanced criteria population. **/
QuickSearchCriteria.prototype.recurseNodes = function(node, nextIndex) {
	var nextCriteria = criteriaList[CHAINED_FIELDS[nextIndex]];
	if (this.value == ALL_VALUE) {
		for (var key in node) {
			if (nextCriteria) {
				nextCriteria.nodeList[nextCriteria.nodeList.length] = node[key][1];
				if (nextCriteria.doPopulate()) nextCriteria.addToValueMap(node[key][1]);
				nextCriteria.recurseNodes(node[key][1], (nextIndex + 1));
			}
		}
	} else if (node[this.value]) {
		if (nextCriteria) {
			nextCriteria.nodeList[nextCriteria.nodeList.length] = node[this.value][1];
			if (nextCriteria.doPopulate()) nextCriteria.addToValueMap(node[this.value][1]);
			nextCriteria.recurseNodes(node[this.value][1], (nextIndex + 1));
		}
	}
}
/** Populate this criteria's dropdown with the values in its valueMap; call set for the next criteria in the chain. **/
QuickSearchCriteria.prototype.set = function(someForm) {
	// the requested value doesn't exist in the list of options (as from a bogus preselect)
	if ((this.value != ALL_VALUE) && !this.valueMap[this.value]) {
		this.value = ALL_VALUE;
		// reset all criteria under this one to ALL
		if (criteriaList.resetCriteria(allFields[this.index + 1]));
	}
	// retrieve the list of dropdown options
	var options = (this.isPreSorted) ? this.getOptions() : this.getOptions().sort(optionComparator);
	// make and model should not have "all" option if list is length 1
	var allOption = (this.doTruncate(options.length)) ? null : this.option;
	if (this.doTruncate(options.length)) {
		this.value = options[0].value;
		// manually create next criteria's valueMap from its nodeList, as this will not have been done in recurseNodes()
		var nextCriteria = criteriaList[CHAINED_FIELDS[this.index + 1]];
		for (var i=0; i<nextCriteria.nodeList.length; i++) nextCriteria.addToValueMap(nextCriteria.nodeList[i]);
	}
	// put this criteria's options in the appropriate dropdown
	this.setOptions(someForm[this.name], options, allOption);
	// set the next dropdown in the chained hierarchy, except trim which sets all advanced search criteria
	if ((this.index + 1) < CHAINED_FIELDS.length) criteriaList[CHAINED_FIELDS[this.index + 1]].set(someForm);
}
/** Set a custom "All" option for this criteria. **/
QuickSearchCriteria.prototype.setAllOption = function(option) { this.option = option; }
/** Set to true when valueMap is already in desired sort order (e.g. year). **/
QuickSearchCriteria.prototype.setIsPreSorted = function(isPreSorted) { this.isPreSorted = isPreSorted; }
/** Set Options for criteria's dropdown.  Optionally pass an "all" option if required. **/
QuickSearchCriteria.prototype.setOptions = function(dropdown, options, allOption) {
	if (!dropdown) return;
	dropdown.disabled = false;
	dropdown.length = 0;
	var selectedIndex = 0;
	if (allOption) dropdown.options[0] = allOption;
	if (options && options.length) {
		for (var i=0; i<options.length; i++) {
			if (options[i].value == this.value) selectedIndex = dropdown.options.length;
			dropdown.options[dropdown.options.length] = options[i];
		}
	} else if (dropdown.options[0].value != ALL_VALUE) {
		dropdown.options[0] = new Option(INV_ALLTEXT, ALL_VALUE, true, true);
	}
	dropdown.options[selectedIndex].selected = true;
}

/** Associative array of all criteria in the search form. **/
function CriteriaList() {}
/** Add a criteria to the list. **/
CriteriaList.prototype.add = function(criteria) {
	if (!criteria.name) return;
	this[criteria.name] = criteria;
}
/** Cycle through each criteria in the list and reset its values **/
CriteriaList.prototype.resetCriteria = function(startName) {
	for (var i=this[startName].index; i<allFields.length; i++) this[allFields[i]].reset();
}

/** Case-insensitive alpha sort, e.g. White, white, Yellow **/
function alphaComparator(a, b) {
	if (a.toLowerCase() > b.toLowerCase()) return 1;
	else if (a.toLowerCase() < b.toLowerCase()) return -1;
	else return 0;
}
/** Numeric sort, e.g. 1, 2, 10 **/
function numberComparator(n1, n2) { return n1 - n2; }
/** Case-insensitive alpha sort on Options **/
function optionComparator(a, b) { return alphaComparator(a.text, b.text); }
/** Trim whitespace off the start and end of a string **/
String.prototype.trim = function() { return this.replace(/^\s*([^\s]*)\s$/g, "\1"); }
/** Capitalize the first letter of a string */
String.prototype.capitalize = function() {
	var cap = new RegExp("^([a-z])").exec(this)[1].toUpperCase();
	return this.replace(/^[a-z]/, cap);
}
/** Return an array of keys in a map **/
function toArray(map) {
	var array = [];
	for (var key in map) array[array.length] = key;
	return array;
}



