/************ CONSTANTS ************/

/* Used for form dropdown names and DAO API method names */
var INV_TYPE = "type";
var INV_WEBID = "location";
var INV_MAKE = "make";
var INV_BODYSTYLE = "bodystyle";
var INV_MODEL = "model";
//SJ:START
var INV_TRIM = "trim";


/* Used to set the order of precedence for ALL data attributes */
//SJ:START
var INV_PRECEDENCE = [INV_TYPE, INV_WEBID, INV_MAKE, INV_BODYSTYLE, INV_MODEL, INV_TRIM];

/* Strings containing default text for dropdowns
 * variables commented out are coming from the localized property files
 */

//var INV_NOINVENTORY = "N/A";
//var INV_DEFAULTTEXT = "Select Any ";
var INV_DEFAULTVALUE = "Any";
ALL_MAKES = "Any";

/* Specifies whether the first option in each dropdown should be left blank (e.g. "select a make") */
var INV_ENABLEDEFAULTOPTION = true;

/************ END CONSTANTS ************/

/************ PUBLIC METHODS ************/

/*
** initInvSearch
** DESCRIPTION:
 * Stores an _Inv_search object for each given form.  Objects are named "inv_search_1", ... "inv_search_n" where n is the number of forms given.
 * Initializes an _Inv_criteria object for each recognized dropdown in the given form(s) (i.e. each dropdown named using a value in INV_PRECEDENCE).
 * If the DAO is empty or does not exist, disables every dropdown and displays "no inventory" text.
** ARGUMENTS:
 *	Required:
 *	- searchform (Form): HTML Form object containing relevant dropdowns
 * -- OR --
 *	- searchform (Array):  Array containing multiple HTML Form objects containing relevant dropdowns
** RETURNS: void
** EXAMPLE USAGE:
 * initInvSearch(document.searchNewForm);	// initializes "inv_search_1"
 * initInvSearch([document.searchNewForm, document.searchUsedForm])	// initializes "inv_search_1" and "inv_search_2"
 */
function initInvSearch(searchform) {
	var formList = (searchform.elements) ? [searchform] : searchform;
	for (var i=0; i<formList.length; i++) {
		eval("inv_search_" + (i+1) + " = new _Inv_search(formList[i])");
		for (var j=0; j<INV_PRECEDENCE.length; j++) {
			var tryElement = eval("formList[i]." + INV_PRECEDENCE[j]);
			if (tryElement && (tryElement.type == "select-one")) eval("inv_search_" + (i+1) + "._add(formList[i]." + INV_PRECEDENCE[j] + ", INV_PRECEDENCE[j])");
		}
		if (eval("inv_search_" + (i+1) + "['location']")) eval("inv_search_" + (i+1) + "['location'].replaceDataSrc('FamilyInventorySummaryJSDAO.getLocationNames', 'text')");
	}
	if (!window.FamilyInventorySummaryJSDAO || (window.FamilyInventorySummaryJSDAO && !FamilyInventorySummaryJSDAO.hasData())) {
		var inv_search_ind = 1;
		while (eval("window.inv_search_" + inv_search_ind)) {
			var this_inv_search = eval("inv_search_" + inv_search_ind++);
			for (var k=0; k<this_inv_search.hierarchy.length; k++) {
				_trimToFit(this_inv_search.hierarchy[k], this_inv_search.hierarchy[k].defaultText, this_inv_search.hierarchy[k].defaultValue, 1);
				this_inv_search.hierarchy[k].dropdown.disabled = true;
			}
		}
	}
}

/*
** get Type/WebId/Make/Bodystyle/Model
** DESCRIPTION:
 * Get current "Type", "WebId", "Make", "Bodystyle", "Model"  and "Trim"values for an _Inv_search object.
** ARGUMENTS: none
** RETURNS: String
** EXAMPLE USAGE:
 * var currentType = inv_search_1.getType();
 *
** set Type/WebId/Make/Bodystyle/Model/Trim
** DESCRIPTION:
 * Set current "Type", "WebId", "Make", "Bodystyle", "Model" and "Trim" values for an _Inv_search object.
** ARGUMENTS:
 * Required:
 *	new<propertyname> (String): new value to use
** RETURNS: void
** EXAMPLE USAGE:
 * inv_search_1.setType("New");
 */
_Inv_search.prototype.getType = function() { return this.Type; }
_Inv_search.prototype.setType = function(newType) { this.Type = newType; }
_Inv_search.prototype.getLocation = function() { return this.Location; }
_Inv_search.prototype.setLocation = function(newLocation) { this.Location = newLocation; }
_Inv_search.prototype.getMake = function() { return this.Make; }
_Inv_search.prototype.setMake = function(newMake) { this.Make = newMake; }
_Inv_search.prototype.getBodystyle = function() { return this.Bodystyle; }
_Inv_search.prototype.setBodystyle = function(newBodystyle) { this.Bodystyle = newBodystyle; }
_Inv_search.prototype.getModel = function() { return this.Model; }
_Inv_search.prototype.setModel = function(newModel) { this.Model = newModel; }
//SJ:START
_Inv_search.prototype.getTrim = function() { return this.Trim; }
_Inv_search.prototype.setTrim = function(newTrim) { this.Trim = newTrim; }

/*
** get noInventory/defaultText/defaultValue
** DESCRIPTION:
 * Get current "noInventory", "defaultText", "defaultValue" values for an _Inv_criteria object.
** ARGUMENTS: none
** RETURNS: String
** EXAMPLE USAGE:
 * var locationDefaultText = inv_search_1["location"].getDefaultText();
 *
** set noInventory/defaultText/defaultValue
** DESCRIPTION:
 * Set current "noInventory", "defaultText", "defaultValue" values for an _Inv_criteria object.
** ARGUMENTS:
 * Required:
 *	new<propertyname> (String): new value to use
** RETURNS: void
** EXAMPLE USAGE:
 * inv_search_1["location"].setDefaultText("All");
 */
_Inv_criteria.prototype.enableDefaultOption = function() { this.enableDefaultOption = true; }
_Inv_criteria.prototype.disableDefaultOption = function() { this.enableDefaultOption = false; }
_Inv_criteria.prototype.getNoInventory = function() { return this.noInventory; }
_Inv_criteria.prototype.setNoInventory = function(newNoInventory) { this.noInventory = newNoInventory; }
_Inv_criteria.prototype.getDefaultText = function() { return this.defaultText; }
_Inv_criteria.prototype.setDefaultText = function(newDefaultText) { this.defaultText = newDefaultText; }
_Inv_criteria.prototype.getDefaultValue = function() { return this.defaultValue; }
_Inv_criteria.prototype.setDefaultValue = function(newDefaultValue) { this.defaultValue = newDefaultValue; }

/*
** choose
** DESCRIPTION:
 * Typically called from the onChange event handler of a dropdown.
 * Registers the new selection and populates the next dropdown in the hierarchy.
 * Note: this becomes a dropdown method, enabling it to be called from each dropdown using the "this" keyword.
** ARGUMENTS: none
** RETURNS: void
** EXAMPLE USAGE:
 * <select name="make" onChange="this.choose();">
 */
_Inv_criteria.prototype.choose = function() {
	var newValue = this.options[this.selectedIndex].value;
	if (this.criteria._isEmpty(newValue) && (this.criteria.selected != 0)) {
		this.options[this.criteria.selected].selected = true;
	} else {
		this.criteria.selected = this.selectedIndex;
		eval("this.criteria.inv_search.set" + this.criteria.name.upperFirst() + "('" + newValue + "')");
		if ((this.criteria.i+1) < this.criteria.inv_search.hierarchy.length) this.criteria.inv_search.hierarchy[(this.criteria.i+1)].populate();
	}
}

/*
** populate
** DESCRIPTION:
 * Retrieves values for all previous properties in the hierarchy, extracts relevant data from FamilyInventorySummaryJSDAO, and writes out the new values to its dropdown.
** ARGUMENTS: none
** RETURNS: void
** EXAMPLE USAGE:
 * inv_search_1["make"].populate();
 */
_Inv_criteria.prototype.populate = function(force) {
	if (window.FamilyInventorySummaryJSDAO && FamilyInventorySummaryJSDAO.hasData()) {
		var continueChoosing = true;
		if ((this.i > 0) && !force) var populateWithData = !this._isEmpty(eval("this.inv_search.get" + this.inv_search.hierarchy[this.i-1].name.upperFirst() + "()"));
		else var populateWithData = true;
		if (populateWithData) {
			var args = new Array();
			var prec_index = 0;
			while (INV_PRECEDENCE[prec_index] != this.name) args[args.length] = eval("this.inv_search.get" + INV_PRECEDENCE[prec_index++].upperFirst() + "()");
			var argString = "";
			for (var i=0; i<args.length; i++) argString += "'" + args[i] + "',";
			var textDataArray = this._getTextDataSources(argString.slice(0,-1));
			var valueDataArray = this._getValueDataSources(argString.slice(0,-1));
			if (((textDataArray.length > 0) && (valueDataArray.length > 0)) && (textDataArray.length == valueDataArray.length)) {
				var newLength = textDataArray.length;
				var optionInd = 0;
				if (this.enableDefaultOption && textDataArray.length > 1) {
				    optionInd = 1;
				    newLength += 1;
				}
				for (var j=0; j<textDataArray.length; j++) this.dropdown.options[optionInd++] = new Option(textDataArray[j], valueDataArray[j]);
				if (this.enableDefaultOption && textDataArray.length > 1) {
					_trimToFit(this, this.defaultText, this.defaultValue, newLength);
				} else {
					this.dropdown.options.length = newLength;
					this.dropdown.options[0].selected = true;
				}
			} else {
				for (var k=this.inv_search.hierarchy.length-1; k>=0; k--) {
					_trimToFit(this.inv_search.hierarchy[k], this.noInventory, "", 1);
					if (this.inv_search.hierarchy[k].name == this.name) break;
				}
				continueChoosing = false;
			}
		} else {
		    //alert("X");
			_trimToFit(this, this.defaultText, this.defaultValue, 1);
		}
		if (continueChoosing) this.dropdown.choose();
	}
}

/*
** replaceDataSrc
** DESCRIPTION:
 * Replaces the source data for an array of values to go in the dropdown's value, text, or both.
** ARGUMENTS:
 * Required:
 *	dataSrcNames (String): name of function (and data object it belongs to) that retrieves new data
 * -- OR --
 *	dataSrcNames (Array): array of function names (and data objects they belongs to) that retrieves new data
 * Optional:
 *	target (String): 'text' || 'value' specifies which property to set; default is both
** RETURNS: void
** EXAMPLE USAGE:
 * inv_search_1["location"].replaceDataSrc("DealerInfoJSDAO.getDealerNames", "text");
 */
_Inv_criteria.prototype.replaceDataSrc = function(dataSrcNames, target) {
	var replaceDataNames = (typeof dataSrcNames == "string") ? [dataSrcNames] : ((dataSrcNames.length) ? dataSrcNames : []);
	this.textDataSources = [];
	if (target) {
		eval("this." + target + "DataSources = replaceDataNames");
	} else {
		this.textDataSources = replaceDataNames;
		this.valueDataSources = replaceDataNames;
	}
}

/*
** addDataSrc
** DESCRIPTION:
 * Adds a source of data for an array of values to go in the dropdown's value, text, or both.
** ARGUMENTS:
 * Required:
 *	dataSrcNames (String): name of function (and data object it belongs to) that retrieves new data
 * -- OR --
 *	dataSrcNames (Array): array of function names (and data objects they belongs to) that retrieves new data
 * Optional:
 *	target (String): 'text' || 'value': specifies which property to set; default is both
 *	pos (Number): specifies the position to insert the new data at; default is the end of the list
** RETURNS: void
** EXAMPLE USAGE:
 * inv_search_1["model"].addDataSrc("FamilyInventorySummaryJSDAO.getBodystyles");
 */
_Inv_criteria.prototype.addDataSrc = function(dataSrcNames, target, pos) {
	var addDataNames = (typeof dataSrcNames == "string") ? [dataSrcNames] : ((dataSrcNames.length) ? dataSrcNames : []);
	if (arguments.length < 3) var pos = this.textDataSources.length;
	var oldDataSources = this.textDataSources;
	if (target) {
		eval("this." + target + "DataSources = oldDataSources.slice(0, pos).concat(addDataNames,oldDataSources.slice(pos))");
	} else {
		this.textDataSources = oldDataSources.slice(0, pos).concat(addDataNames,oldDataSources.slice(pos));
		this.valueDataSources = oldDataSources.slice(0, pos).concat(addDataNames,oldDataSources.slice(pos));
	}
}

/*
** deleteDataSrc
** DESCRIPTION:
 * Deletes a source of data for an array of values to go in the dropdown's value, text, or both.
** ARGUMENTS:
 * Required:
 *	dataSrcNames (String): full name of data-retrieving function to delete, exactly as it was added
 * -- OR --
 *	dataSrcNames (Array): array of full names of data-retrieving functions to delete, exactly as they were added
 * Optional:
 *	target (String): 'text' || 'value': specifies which property to set; default is both
** RETURNS: void
** EXAMPLE USAGE:
 * inv_search_1["model"].deleteDataSrc("FamilyInventorySummaryJSDAO.getBodystyles");
 */
_Inv_criteria.prototype.deleteDataSrc = function(dataSrcNames, target) {
	var deleteDataNames = (typeof dataSrcNames == "string") ? [dataSrcNames] : ((dataSrcNames.length) ? dataSrcNames : []);
	var targets = (target) ? [target] : ["text","value"];
	for (var i=0; i<deleteDataNames.length; i++) {
		for (var j=0; j<targets.length; j++) {
			var thisTarget = eval("this." + targets[j] + "DataSources");
			for (var k=0; k<thisTarget.length; k++) {
				if (deleteDataNames[i] == thisTarget[k]) {
					delete thisTarget[k];
					break;
				}
			}
		}
	}
}

/************ END PUBLIC METHODS ************/

/************ PRIVATE METHODS ************/

/*
** _Inv_search
** DESCRIPTION:
 * Constructor for a custom object build around an inventory search form containing relevant dropdowns.
 */
function _Inv_search(thisform) {
	this.form = thisform;
	this.hierarchy = new Array();
	this.Type = "";
	this.Location = "All";
	this.Make = "";
	this.Bodystyle = "";
	this.Model = "";
	//SJ:START
	this.Trim = "";
}

/*
** _add
** DESCRIPTION:
 * Initializes and adds a new _Inv_criteria object to an _Inv_search's hierarchy array, for each recognized dropdown.
 * Also sets an associative reference to the array object, using the dropdown's name (e.g. inv_search_1["make"]).
 */
_Inv_search.prototype._add = function(thisdropdown, thisname) {
	var newCriteria = new _Inv_criteria(thisdropdown, thisname, this.hierarchy.length, this);
	this.hierarchy[this.hierarchy.length] = this[thisname] = newCriteria;
}

/*
** _Inv_criteria
** DESCRIPTION:
 * Constructor for a custom object built around a recognized dropdown.
 */
function _Inv_criteria(thisdropdown, thisname, inv_search_index, thisinv_search) {
	this.inv_search = thisinv_search;
	this.name = thisname;
	this.selected = 0;
	this.i = inv_search_index;
	this.dropdown = thisdropdown;
	this.dropdown.choose = this.choose;
	this.dropdown.criteria = this;
	this.textDataSources = ["FamilyInventorySummaryJSDAO.get" + thisname.upperFirst() + "s"];
	this.valueDataSources = ["FamilyInventorySummaryJSDAO.get" + thisname.upperFirst() + "s"];
	this.enableDefaultOption = (this.name == "location") ? false : INV_ENABLEDEFAULTOPTION;
	this.noInventory = INV_NOINVENTORY;
	//this.defaultText = INV_DEFAULTTEXT + this.name.upperFirst();
	// SJ : default text no longer says - Select a Make etc
	this.defaultText = INV_DEFAULTTEXT;
	this.defaultValue = INV_DEFAULTVALUE;
}

/*
** _getTextDataSources
** DESCRIPTION:
 * Parses textDataSources property and builds master array of all option text values to use in a dropdown.
 */
_Inv_criteria.prototype._getTextDataSources = function(argString) {
	if (!argString) argString = "";
	var dataArray = new Array();
	for (var i=0; i<this.textDataSources.length; i++) {
		if (this.textDataSources[i]) {
			var newData = eval(this.textDataSources[i] + "(" + argString + ")");
			var oldData = dataArray;
			dataArray = oldData.concat(newData);
		}
	}
	return dataArray;
}

/*
** _getValueDataSources
** DESCRIPTION:
 * Parses valueDataSources property and builds master array of all option values to use in a dropdown.
 * Note: this includes the dirty rotten hack for appending "bodystyles" on to the "models" dropdown... BLEGH BLEGH.
 */
_Inv_criteria.prototype._getValueDataSources = function(argString) {
	if (!argString) argString = "";
	var dataArray = new Array();
	// BEGIN DIRTY ROTTEN HACK FOR MODEL/TYPE DROPDOWN, PART I...
	var isModelType = false;
	for (var j=0; j<this.valueDataSources.length; j++) {
		if (this.valueDataSources[j].indexOf("getBodystyles") != -1) {
			isModelType = true;
			break;
		}
	}
	// END DIRTY ROTTEN HACK FOR MODEL/TYPE DROPDOWN, PART I
	for (var i=0; i<this.valueDataSources.length; i++) {
		if (this.textDataSources[i]) {
			var newData = eval(this.valueDataSources[i] + "(" + argString + ")");
			// BEGIN DIRTY ROTTEN HACK FOR MODEL/TYPE DROPDOWN, PART II...
			if (isModelType && (this.valueDataSources[i].indexOf("getModels") != -1)) {
				for (var k=0; k<newData.length; k++) newData[k] = "M%3A" + newData[k];
			} else if (isModelType && (this.valueDataSources[i].indexOf("getBodystyles") != -1)) {
				for (var k=0; k<newData.length; k++) newData[k] = "B%3A" + newData[k];
			}
			// END DIRTY ROTTEN HACK FOR MODEL/TYPE DROPDOWN, PART II
			var oldData = dataArray;
			dataArray = oldData.concat(newData);
		}
	}
	return dataArray;
}

/*
** _isEmpty
** DESCRIPTION:
 * Utility function- determines whether the given string is "empty" by inventory dropdown standards.
 */
// _Inv_criteria.prototype._isEmpty = function(str) { return ((str == "") || (str == null) || (str == "null") || (str == this.getDefaultValue())); }
_Inv_criteria.prototype._isEmpty = function(str) { return ((str == "") || (str == null) || (str == "null")); }
String.prototype.upperFirst = function() {
	var char1 = this.charAt(0);
	var re = new RegExp("^" + char1);
	return this.replace(re, char1.toUpperCase());
}

/*
** _trimToFit
** DESCRIPTION:
 * Utility function- trims dropdown to given length, resets 1st option's text/value, and sets dropdown selection to 0.
 */
function _trimToFit(thiscriteria, newText, newValue, newLength) {
	thiscriteria.dropdown.options.length = newLength;
	thiscriteria.dropdown.options[0].text = newText;
	thiscriteria.dropdown.options[0].value = newValue;
	thiscriteria.dropdown.options[0].selected = true;
	thiscriteria.selected = 0;
}

/************ END PRIVATE METHODS ************/