// Global variables ----------------------------------------
var selectedOptions = new Array();
var requiredOptions = new Array();
var includedOptions = new Array();
var excludedOptions = new Array();

var includedRules = new Array();
var requiredRules = new Array();
var excludedRules = new Array();


// TermExpression ------------------------------------------
function Term(optionId) {
	this.optionId = optionId;
	this.evaluate = function() {
		return _contains(selectedOptions,optionId ) || _contains(includedOptions,optionId ) || _contains(requiredOptions,optionId );
	}
	this.addElementsTo = function( array ) {
		array[array.length] = optionId;
	}
}
// AndExpression ------------------------------------------
function And(left, right) {
	this.left = left;
	this.right = right;
	this.evaluate = function() {
		return this.left.evaluate() && this.right.evaluate();
	}
	this.addElementsTo = function( array ) {
		left.addElementsTo( array );
		right.addElementsTo( array );
	}
}
// OrExpression ------------------------------------------
function Or(left, right) {
	this.left = left;
	this.right = right;
	this.evaluate = function() {
		return this.left.evaluate() || this.right.evaluate();
	}
	this.addElementsTo = function( array ) {
		left.addElementsTo( array );
		right.addElementsTo( array );
	}
}
// TotalExpression ------------------------------------------
function Total(left, right) {
	this.left = left;
	this.right = right;
}


// Logic ----------------------------------------------
function getInputs( name, formName ){
	var el;
	var _form = document.forms[((formName!=null)?formName:0)];
	var els = new Array();
	var ln = _form.elements.length;
	for( var i=ln; i>0; i-- ){
		el = _form.elements[ln-i];
		if( el.name == name )
			els[els.length] = el;
	}
	return els;
}

function doLogic(checkbox,formName) {	
	var pckgs = getInputs("pckg",formName);
	var opts = getInputs("opt",formName);
	var extclrs = getInputs("extclr",formName);
	var intclrs = getInputs("intclr",formName);

	if (checkbox.checked) {
		// packages, exterior colors, and interior colors can have at most 1 item selected so de-select the previous one
		setUnique( pckgs, checkbox.id );
		setUnique( extclrs, checkbox.id );
		setUnique( intclrs, checkbox.id );

		// select the option
		selectedOptions[selectedOptions.length] = checkbox.id;		
	}
	else {
		_remove( selectedOptions, checkbox.id );
		_remove( requiredOptions, checkbox.id );
		// this doesn't account for all possibilities.. ideally we need a recursive function to
		// find out which options are somehow required by the selected option just removed
		if ( selectedOptions.length == 0 ) {
        	delete requiredOptions;
	        requiredOptions = new Array();
		}
	}

	// these 2 arrays are refreshed everytime
	delete includedOptions;
	delete excludedOptions;
	includedOptions = new Array();
	excludedOptions = new Array();

	var temp = new Array();
	// figure out whats required from the persistent selected and required arrays
	for (var i = 0; i < requiredRules.length; i++) {
        if ( requiredRules[i].left.evaluate() )
			requiredRules[i].right.addElementsTo( temp );
	}
	// there should be no overlap between the selected and required arrays.. required takes precedence
	for (var i = 0; i < temp.length; i++) {
	    if( _contains( selectedOptions, temp[i] ) ) {
	        _remove( selectedOptions, temp[i] );
	        requiredOptions[requiredOptions.length] = temp[i];
		}
	}
    // remove anything left over in the required options that is not in the temp array
    // maybe they should be converted to selected?  this would be a user preference
	for (var i = 0; i < requiredOptions.length; i++) {
	    if( !_contains( temp, requiredOptions[i] ) ) {
	        _remove( requiredOptions, requiredOptions[i] );
	        i--;
	    }
	}

    // set the included options based on everything that is selected or required
	for (var i = 0; i < includedRules.length; i++) {
        if ( includedRules[i].left.evaluate() )
		    includedRules[i].right.addElementsTo( includedOptions );
	}

	// if an included option was specifically unchecked.. it needs to be removed
	if( !checkbox.checked )
		_remove( includedOptions,checkbox.id );

	// there should be no overlap between the selected and included arrays.. included takes precedence because they are free
	_removeAll( includedOptions, selectedOptions );

	// there should be no overlap between the required and included arrays.. included takes precedence because they are free
	_removeAll( includedOptions, requiredOptions );


	// look for includes violations
	for (var i = 0; i < includedRules.length; i++) {
        if ( includedRules[i].left.evaluate() && !includedRules[i].right.evaluate() ) {
	    	violation( includedRules[i].left, includedRules[i].right, 'includes' );
			return;
		}
	}

	// look for excludes violations and track excluded options
	for (var i = 0; i < excludedRules.length; i++) {
        if ( excludedRules[i].left.evaluate() ) {
		    excludedRules[i].right.addElementsTo( excludedOptions );
			if ( excludedRules[i].right.evaluate() ) {
				violation( excludedRules[i].left, excludedRules[i].right, 'excludes' );
				return;
			}
		}
	}

	// look for required violations
	for (var i = 0; i < requiredRules.length; i++) {
        if ( requiredRules[i].left.evaluate() && !requiredRules[i].right.evaluate() ) {
			violation( requiredRules[i].left, requiredRules[i].right, 'requires' );
			return;
		}
	}
	
	// unselect all
	clearAll( pckgs );
	clearAll( opts );
	clearAll( extclrs );
	clearAll( intclrs );

	// now use the new selected/included/excluded lists to set everything again from scratch
	updateUI();
}

function updateUI(){
	for (var i = 0; i < selectedOptions.length; i++) {
		document.getElementById( selectedOptions[i] ).checked = true;
	}	
	
	for (var i = 0; i < includedOptions.length; i++) {
		document.getElementById( includedOptions[i] ).checked = true;
		document.getElementById( 'span-' + includedOptions[i] ).style.color = "blue";
	}
	
	for (var i = 0; i < requiredOptions.length; i++) {
		document.getElementById( requiredOptions[i] ).checked = true;
		document.getElementById( 'span-' + requiredOptions[i] ).style.color = "green";
	}

	for (var i = 0; i < excludedOptions.length; i++) {
		document.getElementById( 'span-' + excludedOptions[i] ).style.color = "red";
	}
}

function violation( left, right, violationType ) {
	violationDialog.getDoc().body.innerHTML = "";
	violationDialog.showDialog();
	displayExpression( left, violationDialog.getDoc() );
	violationDialog.getDoc().body.innerHTML += "<br><b>" + violationType + "</b><br>";
	displayExpression( right, violationDialog.getDoc() );
}

function closeViolationDialog(){
	if ( !displayingExpression )
		violationDialog.closeDialog();
}

// recursive function that displays violation expressions
var displayingExpression = false;
function displayExpression( expression, doc ) {
	displayingExpression = true;
	if ( expression instanceof Or ) {
		doc.body.innerHTML += "( ";
		displayExpression( expression.left, doc );
		doc.body.innerHTML += " OR ";
		displayExpression( expression.right, doc );
		doc.body.innerHTML += " )";
	}
	else if ( expression instanceof And ) {
		doc.body.innerHTML += "( ";
		displayExpression( expression.left, doc );
		doc.body.innerHTML += " AND ";
		displayExpression( expression.right, doc );
		doc.body.innerHTML += " )";
	}
	else {
		// construct the checkbox html which references the Option objects

		var checkbox = new Array();
		checkbox.push("<input type='checkbox' ");
		if ( _contains(selectedOptions,expression.optionId) || _contains(includedOptions,expression.optionId) ||
		        _contains(requiredOptions,expression.optionId) )
			checkbox.push("checked ")
		checkbox.push("onClick=\"parent.closeViolationDialog();parent.doLogic( this"+((self.fName)?", '"+self.fName+"'":"")+" )\" id='" + expression.optionId + "'>");

		var option = findObject( packages, expression.optionId );
		if ( option == null )
			option = findObject( options, expression.optionId );
		if ( option == null )
			option = findObject( exteriorColors, expression.optionId );
		if ( option == null )
			option = findObject( interiorColors, expression.optionId );
			
		checkbox.push(option.name);
		checkbox.push("</input>");

		doc.body.innerHTML += checkbox.join("");
	}
	displayingExpression = false;
}

function clearAll( inputs ) {
	for( var i = 0; i < inputs.length; i++ ) {
		document.getElementById( inputs[i].id ).checked = false;
		document.getElementById( 'span-' + inputs[i].id ).style.color = "";
	}
}

function setUnique( inputs, id ) {
	if ( findObject( inputs, id ) != null ) {
		for( var i = 0; i < inputs.length; i++ ) {
			_remove(selectedOptions,inputs[i].id );
			_remove(includedOptions,inputs[i].id );
			_remove(requiredOptions,inputs[i].id );
		}
	}
}


function findObject( inputs, id ) {
	for( var i = 0; i < inputs.length; i++ ) {
		if ( inputs[i].id == id )
			return inputs[i];
	}
}

/*function findObject( id ){
	return document.getElementById( id );
}*/

// Array Helper Methods
// add a few methods to the array object so it behaves more like a Java List
function _contains( array, item ) {
	for (var i = 0; i < array.length; i++) {
		if (array[i] == item) 
			return true;
	}
	return false;
}

function _remove( array, item ) {
	for (var i = 0; i < array.length; i++) {
		if (array[i] == item){
			array.splice( i, 1 );
		}
	}
}

function _removeAll( array, array2 ) {
	for (var i = 0; i < array.length; i++) {
		_remove( array2,array[i] );
	}
}
