
/** YMMOComponent Class
 * USE: Update dependent select boxes based on user selection and results from Ajax calls
 *
 * NOTES:
 * 1. The ajax functionality is not defined as a privileged method in order to keep it loosely
 *  coupled from the rest of the functionality as there is no need for it to be exposed to private
 *  variables
 *
 */
function YMMOComponent(){

	/**
		YMMO Component Static Variables
		for now leave them as MMYO in the pages
	*/
	YEARS_ID = 'MMYO:years';
	MAKES_ID = 'MMYO:makes';
	MODELS_ID = 'MMYO:models';
	OPTIONS_ID = 'MMYO:options';
	TOP_SEARCH_BUTTON = "DLRLOCATION:findTiresTop";
	BOTTOM_SEARCH_BUTTON = "DLRLOCATION:findTiresBottom";
	FLOAT_SEARCH_BUTTON = "MMYO:findTires";

	this.YMMO_FIELDS = new Array(YEARS_ID, MAKES_ID, MODELS_ID, OPTIONS_ID);

	//maintain the conversation id for use by YMMO between remoting calls
	//this is NOT the same as the conversation id for the current page
	var conId = null;

	//Instance of stubs for communication to remote YMMOInformationBean; create instance
	//during init and then use same instance for all remoting calls in order to support
	//searches in multiple browser tabs / windows.
	var	ymmoInformationComponent = null;
	
	//Variables for the preservation of the current state of the dropdown lists and their selections
	//These are used to save the original state of the dropdowns before they are updated by the Seam
	//Remoting calls.  The onChange event handler for the findTires button restores these values
	//into the DOM before firing the search, in order for the DOM to be in the correct state if the
	//user hits the back button.
	var currentMakeList = null;
	var currentMakeIndex = null;
	var currentModelList = null;
	var currentModelIndex = null;
	var currentYearList = null;
	var currentYearIndex = null;
	var currentOptionsList = null;
	var currentOptionIndex = null;

	DEBUG = false ;

	this.debug = function(debugMessage) {
	//Simple debug message function that uses alerts.  Controlled by setting DEBUG variable to
	//either true to activate debug, or false to deactive debug.
		if (DEBUG) {
			alert(debugMessage);
		}
	}

	function ymmoExists(){
		if (document.getElementById(YEARS_ID) == null) {
			return false;
		}
		return true;
	}

	this.init = function(pageConversationId) {
		//Verify that a valid YMMO exists for the selections - if not reset the
		//dropdown boxes such that the year will be available to be selected.
		//Do this before any additional validation logic.
		this.debug("YMMO.init() called");
		if (!ymmoExists()){
			return
		}
		
		if (!this.isYMMOValid()){
			this.debug("YMMO.init() - calling resetLists()");
			this.resetLists();
		}

		//Set the component to default to use the page's conversationId for
		//YMMO selection remoting calls
		this.conId = pageConversationId;
		
		//Create a new instance of the client stubs for the YMMOInformationBean for use in
		//all subsequent remoting calls for choosing the YMMO options to ensure no
		//cross-contamination of state in a multi-tab/window browser environment.
		this.ymmoInformationComponent = Seam.Component.newInstance("ymmoInformation");		
				
		//Save the current state of the MMYO select lists; will be used to
		//restore the DOM when the findTires button is pressed.  Restoration is
		//necessary so DOM will be in correct state if the back button is pressed.
		this.currentYearList = this.copyOptions(document.getElementById(YEARS_ID).options);
		this.currentYearIndex = document.getElementById(YEARS_ID).selectedIndex;

		this.currentMakeList = this.copyOptions(document.getElementById(MAKES_ID).options);
		this.currentMakeIndex = document.getElementById(MAKES_ID).selectedIndex;

		this.currentModelList = this.copyOptions(document.getElementById(MODELS_ID).options);
		this.currentModelIndex = document.getElementById(MODELS_ID).selectedIndex;

		this.currentOptionsList = this.copyOptions(document.getElementById(OPTIONS_ID).options);
		this.currentOptionsIndex = document.getElementById(OPTIONS_ID).selectedIndex;
	}
	
	this.copyOptions = function(sourceList) {
		//build a new array of options from an existing array
		var result = [];
		for (var i = 0; i<sourceList.length; i++)
		{
			result[i] = new Option(sourceList[i].text, sourceList[i].value);
		}
		return result;
	}

	this.restoreSelectList = function(listObj, sourceOptionArray, selectedIndex){
		//Rebuild the "listObj" select list using the options in the "sourceOptionArray".  Set
		//the selected value to the index value contained in "selectedIndex".
		listObj.disabled = true;
		listObj.length = 0;
		for (var i = 0;i<sourceOptionArray.length;i=i+1)
		{
			listObj.options[i] = new Option(sourceOptionArray[i].text, sourceOptionArray[i].value);
		}
		listObj.selectedIndex = selectedIndex;
	}

	this.restoreDOM = function(){
		//Restore the DOM to the correct MMYO state to support the back button; selected MMYO
		//state for next search is already stored server-side by the SEAM remoting calls,
		//so we don't have to worry about posting it.
		this.restoreSelectList(document.getElementById(YEARS_ID), this.currentYearList, this.currentYearIndex);
		this.restoreSelectList(document.getElementById(MAKES_ID), this.currentMakeList, this.currentMakeIndex);
		this.restoreSelectList(document.getElementById(MODELS_ID), this.currentModelList, this.currentModelIndex);
		this.restoreSelectList(document.getElementById(OPTIONS_ID), this.currentOptionsList, this.currentOptionsIndex);
		addUnloadEvent(this.enableYMMO);
		this.debug("restoreDOM: end of list restores");
	}
	
	/**
	 * enable the YMMMO search dropdowns by level
	 * @param {Object} level
	 */
	this.enableYMMO = function(level) {
		if (level == "year") {
			document.getElementById(YEARS_ID).disabled = false;			
		}
		else if (level == "make"){
			document.getElementById(YEARS_ID).disabled = false;
			document.getElementById(MAKES_ID).disabled = false;		
		} else if (level == "model"){
			document.getElementById(YEARS_ID).disabled = false;
			document.getElementById(MAKES_ID).disabled = false;
			document.getElementById(MODELS_ID).disabled = false;
		} else if (level == "option"){
			//option or no level - enable all
			document.getElementById(YEARS_ID).disabled = false;
			document.getElementById(MAKES_ID).disabled = false;
			document.getElementById(MODELS_ID).disabled = false;
			document.getElementById(OPTIONS_ID).disabled = false;
			var btn = document.getElementById(FLOAT_SEARCH_BUTTON);
			if (btn) {
				btn.disabled = false;
			}	
		} else {
			//determine state of selections to enable appropriately on back button,
			//page reload, etc.
			var optionList = document.getElementById(OPTIONS_ID);
			if (optionList) {
				if (optionList.selectedIndex > 0) {
					//an option is selected, so enable all
					document.getElementById(YEARS_ID).disabled = false;
					document.getElementById(MAKES_ID).disabled = false;
					document.getElementById(MODELS_ID).disabled = false;
					document.getElementById(OPTIONS_ID).disabled = false;
					var btn = document.getElementById(FLOAT_SEARCH_BUTTON);
					if (btn) {
						btn.disabled = false;
					}
				} else {
					document.getElementById(YEARS_ID).disabled = false;					
				}
			}

		}
	}

	/**
	 * Determine if an option has previously been selected
	 */
	this.isOptionSelected = function() {
		var optionList = document.getElementById(OPTIONS_ID);
		if (optionList) {
			if (optionList.selectedIndex > 0) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Disable YMMO dropdowns without restoring previous values; used on Landing page
	 */
	this.disableYMMO = function() {
		document.getElementById(YEARS_ID).disabled = true;
		document.getElementById(MAKES_ID).disabled = true;
		document.getElementById(MODELS_ID).disabled = true;
		document.getElementById(OPTIONS_ID).disabled = true;
		this.disableYMMOSearchButtons();
		addUnloadEvent(this.enableYMMO);
		//alert("disableYMMO: end of list disables");
	}
	
	/**
	 * Disable the YMMO search buttons.  Called from onclick handler to prevent
	 * re-entrant searches.
	 */
	this.disableYMMOSearchButtons = function() {
		var btn = document.getElementById(TOP_SEARCH_BUTTON);
		if (btn) {
			btn.disabled = true;
		}
		btn = document.getElementById(BOTTOM_SEARCH_BUTTON);
		if (btn) {
			btn.disabled = true;
		}
		btn = document.getElementById(FLOAT_SEARCH_BUTTON);
		if (btn) {
			btn.disabled = true;
		}	
	}
	
	/**
	 * This function is invoked during the page load to match the YMMO values in 
	 * the DOM to the correct values in the Seam YMMOInformationBean.  Note that
	 * the invoker of this function should set the desired conversationId prior
	 * to invocation - done to allow this call to be executed in a batch with
	 * other initialization remoting calls.
	 */
	this.synchronize = function(){

		this.debug("YMMO.synchronize() called");
		if (!ymmoExists()){
			this.debug("YMMO.synchronize(); returning after ymmoExists() check");
			return
		}
		var yearSelection = document.getElementById(YEARS_ID).value;
		var makeSelection = document.getElementById(MAKES_ID).value;
		var modelSelection = document.getElementById(MODELS_ID).value;
		var optionSelection = document.getElementById(OPTIONS_ID).value;

		//Now check to see that the remote MMYO matches what exists in the DOM
		//Use the current conversation as we are rendering the page, and use
		//a distinct instance of the ymmoInformation component as well to ensure no
		//cross-contamination of state in a multi-tab/window browser environment.
		this.debug("YMMO.synchronize making validateYMMOInPage call");
		Seam.Component.getInstance("ymmoInformation").
			validateYMMOInPage(yearSelection, makeSelection, modelSelection, optionSelection,
			this.populateYMMO.bindObj(ymmoComponent));
	}

	this.populateYMMO = function( ymmoObject){

		this.debug("AJAX Callback (populateYMMO)");

		//If valid or if there is nothing in session then do nothing
		if (ymmoObject[0][0] == 'Valid YMMO' ||
			ymmoObject[0][0] == 'Not in Session'){
			this.debug("AJAX Callback (populateYMMO); not updating");
			return;
		}
	
		var selectedYear = ymmoObject[0][0];
		var selectedMake = ymmoObject[1][0];
		var selectedModel = ymmoObject[2][0];
		var selectedOption = ymmoObject[3][0];

		this.debug("YMMO.populateYMMO: " + selectedYear +","+ selectedMake +","+ selectedModel +","+ selectedOption );

		var years = assignFrom2DArray(ymmoObject,0);
		var makes = assignFrom2DArray(ymmoObject,1);
		var models = assignFrom2DArray(ymmoObject,2);
		var options = assignFrom2DArray(ymmoObject,3);

		//Reset the values in the DOM
		this.updateMake(makes);
		this.updateModel(models);
		this.updateOptions(options);

		document.getElementById(YEARS_ID).selectedIndex=selectedYear;
		document.getElementById(MAKES_ID).selectedIndex=selectedMake;
		document.getElementById(MODELS_ID).selectedIndex=selectedModel;
		document.getElementById(OPTIONS_ID).selectedIndex=selectedOption;
		this.debug("YMMO.populateYMMO: ; updated - Make = " + selectedMake + " Model = " +
			selectedModel + " Year = " + selectedYear + " Options = " + selectedOption);

		if (isOpera()) {
			//have to reset dealer selections here due to Opera sequence of event execution
			if (typeof dlctrComponent != "undefined" && dlctrComponent) {
				dlctrComponent.enableSelections("state/miles")
			}
		}
	}

	function assignFrom2DArray(mmyoObject, fieldIndex){
		//Assign to one because you actually want the second value in the array
		var i=1;
		var returnValue = new Array();

		while (mmyoObject[fieldIndex][i] != null){
			returnValue[(i-1)] = mmyoObject[fieldIndex][i];
			i=i+1;
		}
		return returnValue;
	}


	this.clearSelectBoxes = function(level) {
		if (level == "make"){
			document.getElementById(MAKES_ID).options.length=1;
			document.getElementById(MAKES_ID).selectedIndex=0;
			document.getElementById(MODELS_ID).options.length=1;
			document.getElementById(MODELS_ID).selectedIndex=0;
			document.getElementById(OPTIONS_ID).options.length=1;
			document.getElementById(OPTIONS_ID).selectedIndex=0;
		}

		if (level == "model"){
			document.getElementById(MODELS_ID).options.length=1;
			document.getElementById(MODELS_ID).selectedIndex=0;
			document.getElementById(OPTIONS_ID).options.length=1;
			document.getElementById(OPTIONS_ID).selectedIndex=0;
		}

		if (level == "option"){
			document.getElementById(OPTIONS_ID).options.length=1;
			document.getElementById(OPTIONS_ID).selectedIndex=0;
		}
	}
	
	this.updateMake = function(makeList) {
		//Handle case where only one item has been returned
		//Set to the current value
		if (makeList.length == 1){

			document.getElementById(MAKES_ID).options[1] = new Option(makeList[0],makeList[0]);
			document.getElementById(MAKES_ID).selectedIndex = 1;

			// Update the models list
			if (!isIE()) {
				//setting focus in IE6 causes rest of script not to execute!
				document.getElementById(MODELS_ID).focus();
			}
			this.remoteUpdate("model", document.getElementById(YEARS_ID).value,
				document.getElementById(MAKES_ID).value,
				document.getElementById(MODELS_ID).value );
		} else {
			//More than one returned - update as normal
			for (var i = 0;i<makeList.length;i=i+1)
			{
				document.getElementById(MAKES_ID).options[i+1] = new Option(makeList[i],makeList[i]);
			}
		}
		this.enableYMMO("make");
	}

	this.updateModel = function(modelList) {
		//Handle case where only one item has been returned
		//Set to the current value
		if( modelList.length == 1){
			document.getElementById(MODELS_ID).options[1] = new Option(modelList[0],modelList[0]);
			document.getElementById(MODELS_ID).selectedIndex = 1;

			// Update the options list
			if (!isIE()) {
				//setting focus in IE6 causes rest of script not to execute!
				document.getElementById(OPTIONS_ID).focus();
			}
			this.remoteUpdate("option", document.getElementById(YEARS_ID).value,
				document.getElementById(MAKES_ID).value,
				document.getElementById(MODELS_ID).value,
				document.getElementById(OPTIONS_ID).value );

		} else {
			//More than one returned - update as normal
			for (var i = 0;i<modelList.length;i=i+1)
			{
				document.getElementById(MODELS_ID).options[i+1] = new Option(modelList[i],modelList[i]);
			}
		}
		this.enableYMMO("model");
	}

	this.updateOptions = function(optionList) {
		//Handle case where only one item has been returned
		if (optionList.length == 1){
			document.getElementById(OPTIONS_ID).options[1] = new Option(optionList[0],optionList[0]);
			document.getElementById(OPTIONS_ID).selectedIndex = 1;
			if (!isIE()) {
				//setting focus in IE6 causes rest of script not to execute!
				document.getElementById(OPTIONS_ID).focus();
			}
			this.remoteUpdate("setOption",document.getElementById(YEARS_ID).value,
				document.getElementById(MAKES_ID).value,
				document.getElementById(MODELS_ID).value,
				document.getElementById(OPTIONS_ID).value);
		} else {
			//More than one returned - update as normal
			for (var i = 0;i<optionList.length;i=i+1)
			{
			 document.getElementById(OPTIONS_ID).options[i+1] = new Option(optionList[i],optionList[i]);
			}
		}
		this.enableYMMO("option");
	}

	this.rewriteButtonHandler = function(button) {
		this.debug("YMMO.rewriteButtonHandler; original onClick = " + button.onclick);
		//get function text and strip off function wrappings and closing brace
		var handlerScript = button.onclick.toString();
		handlerScript = handlerScript.replace(/[^{]+{/,"");
		handlerScript = handlerScript.substring(0, handlerScript.length-1);
		//find and replace the cid; add a bogus parameter (foo) to contain the old cid
		//not changing the cid here; it is just a good reference point to add the other params
		//to the URL
		var cidIndex = handlerScript.indexOf("cid=");
		var newHandlerScript = handlerScript.substring(0, cidIndex+4);
		newHandlerScript += this.conId;
		//add selected MMYO as parameters
		newHandlerScript += "&sYear=";
		newHandlerScript += encodeURIComponent(document.getElementById(YEARS_ID)[document.getElementById(YEARS_ID).selectedIndex].value);
		newHandlerScript += "&sMake=";
		newHandlerScript += encodeURIComponent(document.getElementById(MAKES_ID)[document.getElementById(MAKES_ID).selectedIndex].value);
		newHandlerScript += "&sModel=";
		newHandlerScript += encodeURIComponent(document.getElementById(MODELS_ID)[document.getElementById(MODELS_ID).selectedIndex].value);
		newHandlerScript += "&sOptions=";
		newHandlerScript += encodeURIComponent(document.getElementById(OPTIONS_ID)[document.getElementById(OPTIONS_ID).selectedIndex].value);
		newHandlerScript += "&foo=";
		newHandlerScript += handlerScript.substring(cidIndex+4);
		//construct a new function ptr with our updated code
		button.onclick = new Function(newHandlerScript);
		this.debug("YMMO.rewriteButtonHandler; new handler = " + button.onclick);
	}

	this.updateSelectedOption = function(successFlag) {
		//Callback from final remoting call
		this.debug("Start updateSelectedOption; remote callback, cid = " + Seam.Remoting.getContext().getConversationId() + " successFlag = " + successFlag);
	
		//rewrite onClick handler for the findTires button so that it
		//is on the correct conversation id
		var ele = document.getElementById("MMYO:findTires");
		if( ele ) {
			this.rewriteButtonHandler(ele);
		} else {
			this.debug("updateSelectedOption; button not found in DOM");
		}
		
		//enable the YMMO selections that were disabled before the remote AJAX call
		this.enableYMMO("option");
		//if the dealerLocater component is present, enable the first selection
		if (typeof dlctrComponent != "undefined" && dlctrComponent) {
			dlctrComponent.enableSelections("state/miles")
		}
	}
	
	this.isYMMOValid = function(){
		for (var i=0;i<this.YMMO_FIELDS.length;i=i+1){
			var fieldID = this.YMMO_FIELDS[i];
			if( !(document.getElementById(fieldID).selectedIndex > 0) ){
				return false ;
			}
		}
		return true ;
	}

	this.validateFindTireButton = function(){
		if (!this.isYMMOValid()){
		 	var errorLabel = document.getElementById('label.mmyoErrorMessage' );
			alert(errorLabel.innerHTML) ;
			return false ;
		}
		return true;
	}

	this.resetLists = function(){

		if (!ymmoExists()){
			return
		}

		this.clearSelectBoxes("make");
		document.getElementById(YEARS_ID).selectedIndex=0;
	}

	// this function will detect if a drop down
	// is set to index 0 or 'Select blah...'
	this.isSelectionValid = function( eleId ){
		// this.debug( eleId );
		var ele = document.getElementById( eleId );
		if( ele ){

			// get the selected index
			var idx = ele.selectedIndex ;
			if( idx == 0 ){
				return false ;
			}
			return true ;
		}
		return false ;
	}	
	
} //YYMOComponent

/** YMMOComponent.remoteUpdate -- AJAX Calling Method
 * Additional AJAX method on the YMMOComponent class - Defined outside of the class constructor as it will never require access
 * to any private variables within the YMMOComponent class.
 *
 * NOTE: The callback function from SEAM is in a different scope than the remoteUpdate call and
 * the bindObj method is used to keep the reference to the ymmoComponent allowing class methods to be called in the
 * callback method.
 *
 * 
 */
YMMOComponent.prototype.remoteUpdate = function(level, yearSelection, makeSelection, modelSelection, optionsSelection){
	this.debug( "remoteUpdate:"+ level );
	ymmoComponent.clearSelectBoxes(level);

	if (level == "make") {
		if( !ymmoComponent.isSelectionValid( 'MMYO:years' ) ){
			return false;
		}

		//disable the selections while the remote AJAX call runs
		ymmoComponent.disableYMMO();
		//if the dealerLocater component is present, disable selections
		if (typeof dlctrComponent != "undefined" && dlctrComponent) {
			dlctrComponent.disableSelections()
		}
		
		//Make the remote call to get data for the list of makes		
		this.debug("Before getYYMOMakes call - year: "+ yearSelection + "; conversationId = " + this.conId);
		Seam.Remoting.getContext().setConversationId(this.conId);		
		this.ymmoInformationComponent.getYMMOMakes( yearSelection, this.updateMake.bindObj(ymmoComponent));

	} else if (level == "model") {

		if( !ymmoComponent.isSelectionValid( 'MMYO:makes' ) ){
			return false;
		}

		//disable the selections while the remote AJAX call runs
		ymmoComponent.disableYMMO();
		//if the dealerLocater component is present, disable selections
		if (typeof dlctrComponent != "undefined" && dlctrComponent) {
			dlctrComponent.disableSelections()
		}

		//Make the remote call to get data for the list of models		
		this.debug("Before getYYMOModels call year:"+ yearSelection +" makeSelection:"+ makeSelection + "; conversationId = " + this.conId);
		Seam.Remoting.getContext().setConversationId(this.conId);		
		this.ymmoInformationComponent.getYMMOModels( yearSelection, makeSelection, this.updateModel.bindObj(ymmoComponent));

	} else if (level == "option") {
		if( !ymmoComponent.isSelectionValid( 'MMYO:models' ) ){
			return false;
		}

		//disable the selections while the remote AJAX call runs
		ymmoComponent.disableYMMO();
		//if the dealerLocater component is present, disable selections
		if (typeof dlctrComponent != "undefined" && dlctrComponent) {
			dlctrComponent.disableSelections()
		}

		//Make the remote call to get data for the list of options		
		this.debug("Before YMMO.getYYMOOptions call yearSelection: "+ yearSelection +" makeSelection: "+ makeSelection 
			+ "modelSelection: " + modelSelection + "; conversationId = " + this.conId) ;
		Seam.Remoting.getContext().setConversationId(this.conId);		
		this.ymmoInformationComponent.getYMMOOptions( yearSelection, makeSelection, modelSelection, this.updateOptions.bindObj(ymmoComponent));

	} else if (level == "setOption") {
		if( !ymmoComponent.isSelectionValid( 'MMYO:options' ) ){
			return false;
		}

		//disable the selections while the remote AJAX call runs
		ymmoComponent.disableYMMO();
		//if the dealerLocater component is present, disable selections
		if (typeof dlctrComponent != "undefined" && dlctrComponent) {
			dlctrComponent.disableSelections()
		}

		//Make the remote call to set all the selected values in the backing bean		
		this.debug("Before YYMO.setSelectedOptions call yearSelection: "+ yearSelection +" makeSelection: "+ makeSelection 
			+ "modelSelection: " + modelSelection + " optionsSelection: " + optionsSelection + "; conversationId = " + this.conId) ;
		Seam.Remoting.getContext().setConversationId(this.conId);		
		this.ymmoInformationComponent.setSelectedOptions( yearSelection, makeSelection, modelSelection, optionsSelection, this.updateSelectedOption.bindObj(ymmoComponent));
	}
}

