var DRIVETRAIN_DESCRIPTION = "drivetrainDesc";
var DRIVETRAIN_ROW         = "drivetrainRow";
var DRIVETRAIN_HEADER      = "drivetrainHeader";
var PACKAGE_TABLE_ROW      = "packageTableRow";
var PACKAGE_TABLE_HEADER   = "packageTableHeader";
var ACCESSORY_TABLE_ROW    = "accessoryTableRow";
var ACCESSORY_TABLE_HEADER = "accessoryTableHeader";
var NISMO_TABLE_ROW        = "nismoTableRow";
var NISMO_TABLE_HEADER     = "nismoTableHeader";
var nextPage = "options";
var optionConfiguratorState;
var optionConfigState;
var configuratorState; 
var configState;  //last ConfiguratorUIState returned

var pages = {
	options: { name: "SelectOptions" },
	summary: { name: "Summary",       zip: true, external: true },
	raq    : { name: "RequestAQuote", zip: true, external: true }
};

var tabs = [
	{ id: "tab3", page: pages.options },
	{ id: "tab4", page: pages.summary },
	{ id: "tab5", page: pages.raq }
];

//repaint the entire summary panel
function repaintSummaryPanel( state ) {

	$('sp_baseMsrp').innerHTML = formatCurrency(state.baseMsrp);
	$('sp_configuredMsrp').innerHTML = formatCurrency(state.configuredMsrp);
	$('sp_totalMsrp').innerHTML = formatCurrency(state.totalMsrp);


	paintOptions( state.drivetrains, DRIVETRAIN_HEADER,      DRIVETRAIN_ROW,      false );
	paintOptions( state.packages,    PACKAGE_TABLE_HEADER,   PACKAGE_TABLE_ROW  , true ); 
	paintOptions( state.accessories, ACCESSORY_TABLE_HEADER, ACCESSORY_TABLE_ROW, true ); 
	paintOptions( state.nismos,      NISMO_TABLE_HEADER,     NISMO_TABLE_ROW,     true ); 
}

function repaintSelectableOptions( state ) {
	paintSelectableOptions( state.packages,    PACKAGE_TABLE_HEADER,   PACKAGE_TABLE_ROW  , true ); 
	paintSelectableOptions( state.accessories, ACCESSORY_TABLE_HEADER, ACCESSORY_TABLE_ROW, true ); 
}

//paint a set of options
function paintOptions( options, optionHeader, optionRow, pricable ) {
	if( ! options ) return;

    var selectedOptions = [];
    
    //gather the selected options
	for( var i = 0; i < options.length; i++ ) {
		var pstate = options[i].option.state.name;
		
		if( pstate == "completed" 
		 || pstate == "userselected" 
		 || pstate == "included" ) {
		 
		 	var opt = options[i];
		 	if( pricable && opt.option.price >= 0 ) {
			 	opt.formprice = (opt.option.price > 0) ? formatCurrency( opt.option.price ) : "incl";
			} else {
			    opt.formprice = "";
			}
			
			selectedOptions.push( opt );
		}  
	} 

	clearTemplate( $(optionRow) );

	if( selectedOptions.length > 0 ) {
		instantiateTemplate( $(optionRow), selectedOptions );
		show( $(optionHeader) );
	} else {
		hide( $(optionHeader) );
	}

}

// paints a set of selectable options
// this function simply switches various images on and off
function paintSelectableOptions( options, optionHeader, optionRow, pricable ) {
	if( ! options ) return;

    //gather the selected options
	for( var i = 0; i < options.length; i++ ) {
		var optionId = options[i].option.id;
		var pstate = options[i].option.state.name;
		
		if (!$('option_' + optionId))
			continue;
			
		$(optionId + '_ajaxbusy'  ).style.display = "none";
					
        //enable/disable the selection link for untouchables
        var linkElem = $(optionId + '_selected').parentNode;
        if( pstate == "untouchable" ) linkElem.removeAttribute( "href" );
        else linkElem.href = "javascript:selectOption('" + optionId + "')";
			
		// Handle selection vs. deselection
		if( pstate == "completed" || pstate == "userselected" || pstate == "included" || pstate == "untouchable" ) {
			$(optionId + '_selected').style.display = "inline";
			$(optionId + '_deselected').style.display = "none";
		}
		else {
			$(optionId + '_selected').style.display = "none";
			$(optionId + '_deselected').style.display = "inline";
		}
		
		// Handle inclusion vs. exclusion
		if( pstate == "included" || pstate == "completed" ) {
			$(optionId + '_included').style.display = "inline";
			$(optionId + '_excluded').style.display = "none";
		}
		else if( pstate == "excluded" ) {
			$(optionId + '_included').style.display = "none";
			$(optionId + '_excluded').style.display = "inline";
		}
		else {
			$(optionId + '_included').style.display = "none";
			$(optionId + '_excluded').style.display = "none";
		}
		
		// Update the pricing
		var price = options[i].option.price;
		if( pricable && price >= 0 ) {
			$(optionId + '_price').innerHTML = (price > 0) ? formatCurrency( price ) : "incl";
		} else {
			$(optionId + '_price').innerHTML = "";
		}
	} 
}

//turn all option links on or off
function turnOnOffAllOptionLinks( state, turnOn ) {
	turnOnOffOptionLinks( state.transmissions, turnOn );
	turnOnOffOptionLinks( state.drivetrains,   turnOn );
	turnOnOffOptionLinks( state.packages,      turnOn ); 
	turnOnOffOptionLinks( state.accessories,   turnOn ); 
	turnOnOffOptionLinks( state.nismos,        turnOn ); 
}

//turn a set of option links on or off
function turnOnOffOptionLinks( options, turnOn ) {
	if( ! options ) return;

    //gather the selected options
	for( var i = 0; i < options.length; i++ ) {
		var optionId = options[i].option.id;
		
		if (!$('option_' + optionId)) continue;

        //enable/disable the selection link
        var linkElem = $(optionId + '_selected').parentNode;
        if( ! turnOn ) linkElem.removeAttribute( "href" );
        else linkElem.href = "javascript:selectOption('" + optionId + "')";
	}
}

//update the tabs with the new config state
function updateTabs( configStateUI ) {
 	for( var i = 0; i < tabs.length; i++ ) {
 		var tab = tabs[i];
        try{ $(tab.id).href = makePageLink( tab.page, configStateUI ); } catch( nada ) {} 
 	}
}

//make an external link to a page
function makePageLink( page, configStateUI ) {
alert(configStateUI);
    var configuratorState = configStateUI.serializedConfigState.replace(/&amp;/g,'&'); 
    var prefix = window.location.pathname;
    var zipcode = readCookie(configuratorZipCodeCookie);

	var service = '?service=external/';

 	var link = prefix + service + page.name + '&' + configuratorState;  	    
 	if(page.zip) link += "&zipcode=" + zipcode;

	//add wrapper to prevent click when ajax call is live
	link = "javascript:nav('" + link + "');";

	return link;
} 

//navigate to a given URL if there is no ajax call in progress
function nav( url ) {
	if( ! ajaxCallInFlight ) location.href = url;
} 

//update the next button(s) and the RAQ link
function updateNextButtonAndRAQ( configStateUI, nextPage ) {
	var button1 = $("nextButton1");
	var button2 = $("nextButton2");
	
	if( button1 ) button1.href= makePageLink( nextPage, configStateUI ); 
	if( button2 ) button2.href= makePageLink( nextPage, configStateUI ); 
	$("raqLink").href = makePageLink( pages.raq, configStateUI );
} 

//make the anchor (for back-button logic)reflect the current state
function updateBackButton( configStateUI ) {
    window.location.hash = configStateUI.serializedConfigState;
}

//update the page after a state change (also used as DWR callback)
function updateState( configStateUI ) {
	
	configuratorState = optionConfiguratorState = configStateUI.serializedConfigState;
	configState       = optionConfigState = configStateUI;
	
	repaintSummaryPanel( configStateUI );
	try{ repaintSelectableOptions( configStateUI ); } catch(nada) {}
	updateTabs( configStateUI );
	updateBackButton( configStateUI );
	updateNextButtonAndRAQ( configStateUI, pages[nextPage] );
	
	//update colors
	selectedExtColor = configStateUI.selectedExteriorColor ? configStateUI.selectedExteriorColor.id : null;
	selectedIntColor = configStateUI.selectedInteriorColor ? configStateUI.selectedInteriorColor.id : null;
	showColorPreviewFromState( configStateUI );
    setSummaryColorNamesFromState( configStateUI ); 
	
	$('debugArea').innerHTML = generateDebugPanel( configStateUI );
}

//restore the state if the back button was used
function backButtonRestoreState() {
	var stateString = window.location.hash;
	if( stateString && stateString.length > 1 ) {	
		stateString = stateString.substring(1);
	
		ajaxCall( ConfigState.getState, [ locale, stateString ], stateRestoreCallback ); 
	} else {
		//use the preloaded state
		stateRestoreCallback( configState );
	}
}
function stateRestoreCallback( configStateUI ) {

	if( $('txtIntColor') ) {

		if( configStateUI.selectedExteriorColor ) {
			displayExteriorColor( configStateUI.selectedExteriorColor.id.toString() ); 
		}
		
		if( configStateUI.selectedInteriorColor ) {
			displayInteriorColor( configStateUI.selectedInteriorColor.id.toString() ); 
		} else {
			displayInteriorColor( null ); 
		}
	}
	
	updateState( configStateUI );
}

//Display the given exterior color
function displayExteriorColor( extId ) {

	if ( selectedExtColor && $(selectedExtColor)) {$(selectedExtColor).className='tChipImg'}
	
	if ($(extId)) $(extId).className='tChipImgH';
	if ($('exteriorColorName')) setHtml('exteriorColorName', cPList[extId].d );

	selectedExtColor=extId
	try {
		extChipOver( extId ); //set up main image and int chip
	}
	catch (ignored) {}
} 

//Display the given interior color
function displayInteriorColor( intId ) {
    try{ if( selectedIntColor ){$('intChipImg'+selectedIntColor).className='tChipImg'}; } catch( ignored ) {}

	if( intId == null ){ 
		selectedIntColor=null

			
		if ($('interiorColorName'))
			setHtml('interiorColorName',  '' );
		if ($('txtIntColor'))			
			setHtml('txtIntColor', '' );		
        $('colorImg').src = blankIntColor;
		$('trimImg' ).src = blankIntTrim;
		return;
	}

	$('intChipImg'+intId).className='tChipImgH'
	setHtml('interiorColorName',  cPList[selectedExtColor].i[intId].d.replace(/ *\/ */g,' / '));

	selectedIntColot=intId	
	intChipOver( intId ); //set up main trim swatch 
}

//=============================================================
// Interface to the vehicle and interior color preview images
// Also the Summary Panel color names
//=============================================================

//Show vehicle color preview image 
function showVehicleColorPreview( extId ) {
	if( extId ) $('veh_img').style.backgroundImage = "url("+cPList[extId].l+")";
	else        $('veh_img').style.backgroundImage = "url("+blankExtColor+")";
}

//Set trim/color preview swatches
function showTrimColorPreview( intId ) {
	if( intId && selectedExtColor ) { 
		$('trimImg' ).src = cPList[selectedExtColor].i[intId].t;
		$('colorImg').src = cPList[selectedExtColor].i[intId].c;
	} else {
		$('trimImg' ).src = blankIntTrim;
		$('colorImg').src = blankIntColor;		
	}
} 

//Set the vehicle and interior previews from the state
function showColorPreviewFromState( configUIState  ) {

	showVehicleColorPreview( configUIState.selectedExteriorColor ? 
	                             configUIState.selectedExteriorColor.id :
	                             null );
	showTrimColorPreview( configUIState.selectedInteriorColor? 
	                          configUIState.selectedInteriorColor.id : 
	                          null );
}

//Set exterior color name in the summary panel
function setSummaryExtColorName( extId ) {
	if( extId ) setHtml('exteriorColorName', cPList[extId].d );
	else        setHtml('exteriorColorName', "" );
} 

//Set interior color name in the summary panel
function setSummaryIntColorName( intId ) {
	if( intId ) setHtml('interiorColorName', cPList[selectedExtColor].i[intId].d.replace(/ *\/ */g,' / ') );
	else        setHtml('interiorColorName', "" );
}

//Set the color names in the summary panel from the state
function setSummaryColorNamesFromState( configUIState ) {

	setSummaryExtColorName( configUIState.selectedExteriorColor ? 
	                            configUIState.selectedExteriorColor.id :
	                            null );
	setSummaryIntColorName( configUIState.selectedInteriorColor? 
	                            configUIState.selectedInteriorColor.id : 
	                            null );
}


//=============================================================
// Template Engine
//=============================================================

function show( elem ) { elem.style.display = ""; }
function hide( elem ) { elem.style.display = "none"; }

/**
 * Instantiate the given template element by duplicating it for each object
 * in the valueObjs and filling in the variables from the objects properties.
 * The new elements are inserted before the template element.
 */
function instantiateTemplate( templateElement, valueObjs ) {
	var parent = templateElement.parentNode;

	for( var i = 0; i < valueObjs.length; i++ ) {
		var elem = applyTemplateElement( templateElement, valueObjs[i], i );
	    parent.insertBefore( elem, templateElement );
	}
}

/**
 * Clear the template instantiated rows (have ids starting with the same 
 * id as prefix)
 */
function clearTemplate( templateElement ) {
	if( !templateElement )
		return;
	
	var id = templateElement.id;
	
	var i = 0;
	while( true ) {
		var instanceId = id + "_" + i; 
		var inst = $(instanceId);
		if( inst ) {
			inst.parentNode.removeChild( inst );		
		} 
		else break;
		
		i++;
	}
}

/**
 * Process a set of nodes against the object
 */
function applyTemplateNodes( nodes, obj, index ) {
	var resultNodes = [];

	for( var i = 0; i < nodes.length; i++ ) {
		var node = nodes[i];
		if( node.nodeType == 1 ) {		

	    	resultNodes.push( applyTemplateElement( node, obj, index ));

		} else {			
			//other node types - just copy the content
			resultNodes.push( document.createTextNode( substituteVars( node.nodeValue, obj )));
		} 
	} 
    	
	return resultNodes;
} 

/**
 * Process a node element against the object
 */
function applyTemplateElement( node, obj, index ) {
    //recreate the element
    var elem = document.createElement( node.nodeName );
	
	//copy the attributes
    var attrs = node.attributes;
    for( var j = 0; j < attrs.length; j++ ) {
        var att = attrs[j];
    	
		try {
	        var attName = att.name;

	        if( attName == "id" ) continue; 
	        if( attName == "style" ) continue;
	        
            elem.setAttribute( attName, substituteVars( att.nodeValue, obj ));
        } catch ( ieSucks ) {}
    }   		

    //copy the style
    var style = (node.style) ? node.style.cssText : "";
	var sanitizedStyle = style ? style.toLowerCase().replace( /display:[ ]*none/, "" ) : "";
 	elem.style.cssText = substituteVars( sanitizedStyle );

    //set the id
    var id = node.id;
    if( id ) {
    	elem.id = id + "_" + index;
    }

	//process children
	var childInsts = applyTemplateNodes( node.childNodes, obj, index );
	for( var j = 0; j < childInsts.length; j++ ) {
	    var child = childInsts[j];
	    if( child.nodeType == 3 ) {
	    	elem.innerHTML = child.nodeValue;
	    } else { 
	        elem.appendChild( childInsts[j] ); 			
	    }
	} 		
	
	return elem;
} 


/**
 * Substitute vars of the form ${name} in the text with value from the
 * same-named property of the object
 */
function substituteVars( text, obj ) {

	var result = "";

	for( var i = 0; i < text.length; i++ ) {
		var ch = text.charAt(i);
		if( ch == "$" && text.charAt(i+1) == "{" ) {
			var varName = "";
			var j;
			for( j = i+2; j < text.length; j++ ) {
				var ch2 = text.charAt(j);
				if( ch2 == "}" ) break;
				varName += ch2;
		    }
		    
		    if( varName.length > 0 ) {
		    	var varValue = obj[varName];
		    	if( varValue ) result += varValue;
		    } 
		    
		    i = j;
		    continue;
		} 
	
		result += ch;
	}
	
	return result;
} 

//show the ajax in-progress icon for an option
function showAjaxBusyImage( optionId ) {
	var busyImg = $(optionId + '_ajaxbusy' );

	busyImg.style.display = "inline";
	$(optionId + '_selected'   ).style.display = "none";
	$(optionId + '_deselected' ).style.display = "none";
	
	busyImg.src = busyImg.src;	//required to make IE start the animation
} 

//state before the last option change
var stateBeforeOptionChange;

function selectOption( optionId ) {

    if( ajaxCallInFlight ) return;

	showAjaxBusyImage( optionId );
	turnOnOffAllOptionLinks( configState, false );

	if (optionConfiguratorState == null)
		optionConfiguratorState = initialState;
		
	stateBeforeOptionChange = configState;
	
	ajaxCall( ConfigState.makeSelection, [ locale, optionConfiguratorState, optionId ], updateOptions );
}

//callback after de/selecting an option
function updateOptions( configStateUI ) {

	updateState( configStateUI );
	
	// If we have notables, show the notable box
	if( configStateUI.notables.length > 0 ) {
		showNotable( configStateUI );
	}
}

function formatCurrency(num) {
	num = num.toString().replace(/\$|\,/g,'');
	if(isNaN(num))
		num = "0";
	sign = (num == (num = Math.abs(num)));
	num = Math.floor(num*100+0.50000000001);
	cents = num%100;
	num = Math.floor(num/100).toString();
	if(cents<10)
		cents = "0" + cents;
	for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
		num = num.substring(0,num.length-(4*i+3))+','+ num.substring(num.length-(4*i+3));
	return (((sign)?'':'-') + '$' + num + '.' + cents);
}

function showNotable( configStateUI ) {
	var notables = configStateUI.notables;

//	alert(DWRUtil.toDescriptiveString(notables,4));

	// Populate the notable div appropriately
	var firstNotable = notables[0];
	var action = firstNotable.notable.action;
	switch( action ) {
		case 1:
			$('notableActionString').innerHTML = "selection";
			break;

		case 0:
		case 2:
			$('notableActionString').innerHTML = "unselection";
			break;

		default:
			$('notableActionString').innerHTML = "";
			break;
	}
	
	$('notableSubjectString').innerHTML = firstNotable.subjectOption.title;
	$('notableRejectPrice').innerHTML = formatCurrency( firstNotable.notable.rejectTotalPrice );
	$('notableAcceptPrice').innerHTML = formatCurrency( firstNotable.notable.acceptTotalPrice );
	
	var notableChangeList = "";
	for( var notableIndex = 0; notableIndex < notables.length; ++notableIndex ) {
		var notable = notables[notableIndex];
		var acceptOptions = notable.acceptOptions;
		for( var optionIndex = 0; optionIndex < acceptOptions.length; ++optionIndex ) {
			var notableOption = acceptOptions[optionIndex];
			notableChangeList = notableChangeList + formatNotableOptionListItem( notableOption );
		}
	}
	
	$('notableChangeList').innerHTML = notableChangeList;

	// Turn the notable "on"
	$('notableContainer').style.position="absolute";
	$('notableContainer').style.top=document.body.scrollTop;

	$('notableContainer').style.display = "block";
	$('shaderDiv').style.display = "block";
}

function formatNotableOptionListItem( notableOption ) {
	return "<li>"
		+ "<b>"
		+ notableOption.title
		+ "</b> will be "
		+ getNotableOptionTypeString(notableOption)
		+ ".</li>";
}

function getNotableOptionTypeString( notableOption ) {

//	alert(DWRUtil.toDescriptiveString(notableOption,3));


	var typeString = "added";
	var stateName = notableOption.option.state.name;
	if( stateName == "completed" ) {
		typeString = "selected for you by default, but other alternatives are available";
	}
	else if ( stateName == "excluded" || stateName == "selectable") {
		typeString = "removed";
	}
	
	return typeString;
}

function rejectNotables() {

	// Turn the notable "off"
	$('notableContainer').style.display = "none";
	$('shaderDiv').style.display = "none";
	
	//restore previous state
	updateState( stateBeforeOptionChange ); 
}

//function that can be called from a bookmarklet
function showState() {
    $('debugArea').setAttribute('style', "display: inline");
	$('debugArea').innerHTML = generateDebugPanel( configState );
}

//function that can be called from a bookmarklet
function hideState() {
    $('debugArea').setAttribute('style', "display: none");
}


function generateDebugPanel( configStateUI ) {

	var s = "<hr /><p>Serialized: <b>" + configStateUI.serializedConfigState + "</b></p>"
          + "<table border=0 cellpadding=2 cellspacing=3>"
          + "<tr><td>Base MSRP</td>    <td><b>" + configStateUI.baseMsrp + "</b></td></tr>"
          + "<tr><td>Config MSRP</td>  <td><b>" + configStateUI.configuredMsrp + "</b></td></tr>"
          + "<tr><td>Dest Handling</td><td><b>" + configStateUI.destHandling + "</b></td></tr>"
          + "<tr><td>Total MSRP</td>   <td><b>" + configStateUI.totalMsrp + "</b></td></tr>"
          + "<tr><td>Sel Int Color</td><td><b>" + configStateUI.selectedInteriorColor + "</b></td></tr>"
          + "<tr><td>Sel Ext Color</td><td><b>" + configStateUI.selectedExteriorColor + "</b></td></tr>"
          + "<tr><td>Packages</td>     <td>"    + optionDebugList( configStateUI.packages ) + "</td></tr>"
          + "<tr><td>Accessories</td>  <td>"    + optionDebugList( configStateUI.accessories ) + "</td></tr>"
          + "<tr><td>Nismos</td>       <td>"    + optionDebugList( configStateUI.nismos ) + "</td></tr>"
          + "<tr><td>Transmissions</td><td>"    + optionDebugList( configStateUI.transmissions ) + "</td></tr>"
          + "<tr><td>DriveTrains</td>  <td>"    + optionDebugList( configStateUI.drivetrains ) + "</td></tr>"
          + "<tr><td>User Selects</td> <td><b>" + debugList( configStateUI.userSelections ) + "</b></td></tr>"
          + "<tr><td>Selections</td>   <td><b>" + debugList( configStateUI.selections ) + "</b></td></tr>"
          + "<tr><td>Selectables</td>  <td><b>" + debugList( configStateUI.selectables ) + "</b></td></tr>"
          + "<tr><td>Completions</td>  <td><b>" + debugList( configStateUI.completions ) + "</b></td></tr>"
          + "<tr><td>Inclusions</td>   <td><b>" + debugList( configStateUI.inclusions ) + "</b></td></tr>"
          + "<tr><td>Exclusions</td>   <td><b>" + debugList( configStateUI.exclusions ) + "</b></td></tr>"
          + "<tr><td>Notables</td>     <td><b>" + notableList( configStateUI.notables ) + "</b></td></tr>"
          + "</table>"

    return s;
}

function notableList( nots ) {
	if( ! nots || nots.length == 0 ) return " - ";
	
	var s = "";

	for( var i = 0; i < nots.length; i++ ) {
	    var no = nots[i];
		
		s += "<table border=0 cellpadding=3 cellspacing=3>"
		  +  "<tr><td>Subject Option</td><td><b>" + no.subjectOption.option.id + "</b></td></tr>"
		  +  "<tr><td>Subject Action</td><td><b>" + ( no.notable.action==1?"select":"unselect") + "</b></td></tr>"
		  +  "<tr><td>Accept Options</td><td><b>" + optionDebugList( no.acceptOptions ) + "</b></td></tr>"
		  +  "<tr><td>Accept Action</td><td><b>" + ( no.notable.type==1?"inclusion":"exclusion") + "</b></td></tr>"
		  +  "</table><hr />"
	}
		
	return s;
} 

function debugList( items ) {
	var s = "";
	
	for( var i = 0; i < items.length; i++ ) {
	    var it = items[i];

		if( i > 0 ) s += ", ";
		s += it; 
	}
		
	return s;
}

function optionDebugList( options ) { 
	if( ! options || options.length == 0 ) return " - ";

	var s = "<table border=0 cellpadding=3 cellspacing=3>";
	
	for( var i = 0; i < options.length; i++ ) {
	    var opt = options[i].option;
	    	    
	    var description = opt.title ? opt.title : opt.description;
	    var option      = opt.title ? opt.option : opt;
	    
	    s += "<tr><td><b>" + option.id + "</b></td>"
	      +  "<td>" + description + "</td>"
	      +  "<td><i>" + option.state.name + "</i></td></tr>";
	} 
	
	s += "</table>";
	
	return s;
}



//============================================================================
// Functions for creating the color info structure (generated by Tapestry)
//============================================================================


var cPList = new Array();

function extColorLet(largeImg, description, interiors) {
	this.l = largeImg;
	this.d = description;
	this.i = new Array();
	
	for( var i=0; i < interiors.length; i ++ ){
		var interior = interiors[i];
		this.i[interior.id] = interior;
	}
}

function intColorLet(intId, chipImg, colorImg, trimImg, description, directURL) {
	this.id = intId;
	this.s = chipImg;
	this.c = colorImg;
	this.t = trimImg;
	this.d = description;
    this.directURL = directURL;
}

function addColorSelection(extId, fullPath, description, interiors) {
	cPList[extId] = new extColorLet(fullPath, description, interiors);
}

//============================================================================
// Mouse handlers for color selection 
//============================================================================

function extChipOut(extId) {
	
	if( selectedExtColor == null ){
		 $('veh_img').style.backgroundImage = "url("+blankExtColor+")";
		 setHtml('txtExtColor', "&nbsp;" );
	} else {
		$('veh_img').style.backgroundImage = "url("+cPList[selectedExtColor].l+")";
		setHtml('txtExtColor', cPList[selectedExtColor].d );
	}
	
	generateIntChips(selectedExtColor);
}

function intChipOut(intId) {
	if( selectedIntColor == null ){
		 $('colorImg').src = blankIntColor;
		 $('trimImg' ).src = blankIntTrim;
		 setHtml('txtIntColor', "&nbsp;" );
	} else {
		$('colorImg').src = cPList[selectedExtColor].i[selectedIntColor].c;
		$('trimImg' ).src = cPList[selectedExtColor].i[selectedIntColor].t;
		setHtml('txtIntColor', cPList[selectedExtColor].i[selectedIntColor].d );
	}
}

function extChipOver(extId) {
	showVehicleColorPreview( extId );
	setHtml('txtExtColor', cPList[extId].d );
	generateIntChips(extId);
}

function intChipOver(intId) {
	if( cPList[ selectedExtColor ] ) { 
        try { 
			$('trimImg' ).src = cPList[selectedExtColor].i[intId].t;
			$('colorImg').src = cPList[selectedExtColor].i[intId].c;
			setHtml('txtIntColor', cPList[selectedExtColor].i[intId].d );
			
		} catch( ignored ) {} //exception may happen if user moves mouse very
		//quickly over the interior chip of of an exterior color that's not the
		//selected one before the int chips have been regenerated (by the mouse
		//out on the exterior chip) 
	}
}

//============================================================================
// Color selection
//============================================================================

var selectedExtColor;
var selectedIntColor;

/**
 * Make an exterior color selection
 */
function selectExt( extId ){

    if( ajaxCallInFlight ) return;

	if( extId == selectedExtColor ) return; //nothing to do 

    //chips:
	if( selectedExtColor ) {
		// deselect previous exterior
		$(selectedExtColor).className='tChipImg';
	}
	if( extId ) {
		// select new exterior
		$(extId).className='tChipImgH';
	}

	// set exterior name in summary panel
    setSummaryExtColorName( extId );
	
	// set name of color above chip row
	setHtml('exteriorColorName', cPList[extId].d );

	selectedExtColor = extId;
	
	//this is required to support Selenium or people who select the color
	//via keyboard without mousing over the ext color chip
	generateIntChips( extId );
	setHtml('txtExtColor', cPList[extId].d );
	
	selectInt( null );

	//if (configuratorState == null) configuratorState = initialState;

	// invoke ajax call			
	//ajaxCall( ConfigState.makeSelection, [locale, configuratorState, extId], updateState);	
}

/**
 * Make an interior color selection
 */
function selectInt( intId ){

    if( ajaxCallInFlight ) return;

	// set interior name in summary panel
    setSummaryIntColorName( intId );
	showTrimColorPreview  ( intId );

	if ( intId == null ) {
		selectedIntColor = null;
		setHtml('txtIntColor', '' );

	} else if( intId != selectedIntColor ) {
		if( selectedIntColor ) {
			// unselect previous selection
			$('intChipImg'+selectedIntColor).className = 'tChipImg';
		}

		// select current
		$('intChipImg'+intId).className = 'tChipImgH';
 
		selectedIntColor = intId;

		//if (configuratorState == null) configuratorState = initialState;
	
		//AJAX call		
		//ajaxCall( ConfigState.makeSelection, [locale, configuratorState, intId], updateState);	
	}	
}

//============================================================================
// Interior chip generation
//============================================================================

/**
 * Generate the interior selection chips for a given exterior color
 */
function generateIntChips(extId) {
		
		var output = '';		
		
		var ext = cPList[extId];			
		if( ext != null) { 
			for( var i in ext.i ){
				var intObj = ext.i[i];
				
				output += "<li><a href='javascript:void(0)' id='intChipImg" + intObj.id + "' title='" + intObj.d	+ "' " +
						"onmouseout='intChipOut(selectedIntColor);' onclick='selectInt(\""+ intObj.id+ "\");selectOptions();' onmouseover='intChipOver(\"" + intObj.id + "\");' " +				
						"title='" + intObj.d + "' class='" + getIntClass(intObj,extId) + "'" +
						"><img src='" + intObj.s + "' /></a></li> \n";
			}
		}
		output="<ul>"+output+"</ul>";
		setHtml('intChipImageContainer', output);
}

/**
 * Get the CSS class for an interior chip.
 * If the chip represents the selected interior and the exterior color is
 * the selected one then use a "selected" class, otherwise not
 */
function getIntClass( intObj, extId ){
	if( intObj.id == selectedIntColor && selectedExtColor == extId ){
		return "tChipImgH";
	} else {
		return "tChipImg"
	}
}


//============================================================================
// AJAX Call Management
//============================================================================

/**
 * This flag can be used to inhibit user action while an Ajax call is ongoing
 */
var ajaxCallInFlight = false;

/**
 * Make an Ajax call and set the in-flight flag.
 * Clear the flag upon return or if there a timeout or error.
 */
function ajaxCall( method, args, callbackFn ) {
    if( ajaxCallInFlight ) return;
    
	var callback = function( val ) {
		ajaxCallDone();
		callbackFn( val );
	}

	var callInfo =  {
		  callback    : callback,
		  timeout     : 60000,
		  verb		  : "GET",
		  errorHandler: function(message) { 
		  	  ajaxCallDone(); 
		  	  updateState( stateBeforeOptionChange );
		  }
	};

	args.push( callInfo );

	ajaxCallInFlight = true;
    method.apply( ConfigState, args );
} 

function ajaxCallDone() {
	ajaxCallInFlight = false;
}
