// ******************************************************************
// AttributeExpressionEvaluator.js
//
// Version 1.0
// Copyright (c) 2007 Catalog Data Solutions. All Rights Reserved.
//
// Class encapsulating a logical expression of attributes and 
// attribute values within a configurator which can determine
// whether the expression should evaluate to true or false depending
// on the current state of the configurator
// ******************************************************************

cds.namespace("configurator");

// ---------------------------------------------------------
// AttributeExpressionEvaluator class
// ---------------------------------------------------------

// Format: a1=v1,v2;a2<5;!a3=v3;a4=*
cds.configurator.AttributeExpressionEvaluator = function(attributeManager, expressionString) {
	this.attributeManager = attributeManager;
	this.attributes = new Array();
	
	var alist = expressionString.split(";");
	for (var i = 0; i < alist.length; i++) {
		this.attributes[this.attributes.length] = new cds.configurator.AttributeExpressionEvaluator.AttributeExpression(attributeManager, alist[i]);
	}
	
	this.evaluate = function() {
		for (var i = 0; i < this.attributes.length; i++) {
			if (!this.attributes[i].evaluate()) return false;
		}
		return true;
	}

	this.destroy = function() {
		this.attributeManager = null;
		for (var i = 0; i < this.attributes.length; i++) {
			if (typeof this.attributes[i].destroy == "function") this.attributes[i].destroy();
		}
		this.attributes = null;
	}

	this.toString = function() {
		return "cds.configurator.AttributeExpression";
	}
}

// Evaluate function returns true if the given attribute expression is
// true given the current state of the AttributeManager
// Format: attribute=attributeValue1,!attributeValue2 or !attribute<6
// When multiple values are given, these are ORed together.
// Numeric operators: =, <, >, <=, >=
cds.configurator.AttributeExpressionEvaluator.AttributeExpression = function(attributeManager, expressionString) {
	this.attributeManager = attributeManager;
	this.attribute = null;
	this.isNot = false;
	this.isWild = false;
	this.operator = null;
	this.operand = null;
	this.values = null;
	
	// parse expressionString
	var expression = expressionString.trim();
	var pos = expression.indexOf("!");
	if (pos == 0) {
		this.isNot = true;
		expression = expression.substring(1);
	}
	if ((pos = expression.indexOf("<=")) > 0) {
		this.attribute = this.attributeManager.getAttribute(expression.substring(0, pos));
		this.operator = "<=";
		if (expression.substring(pos + 2) == "*") this.isWild = true;
		else this.operand = parseFloat(expression.substring(pos + 2));
	} else if ((pos = expression.indexOf(">=")) > 0) {
		this.attribute = this.attributeManager.getAttribute(expression.substring(0, pos));
		this.operator = ">=";
		if (expression.substring(pos + 2) == "*") this.isWild = true;
		else this.operand = parseFloat(expression.substring(pos + 2));
	} else if ((pos = expression.indexOf("<")) > 0) {
		this.attribute = this.attributeManager.getAttribute(expression.substring(0, pos));
		this.operator = "<";
		if (expression.substring(pos + 1) == "*") this.isWild = true;
		else this.operand = parseFloat(expression.substring(pos + 1));
	} else if ((pos = expression.indexOf(">")) > 0) {
		this.attribute = this.attributeManager.getAttribute(expression.substring(0, pos));
		this.operator = ">";
		if (expression.substring(pos + 1) == "*") this.isWild = true;
		else this.operand = parseFloat(expression.substring(pos + 1));
	} else if ((pos = expression.indexOf("=")) > 0) {
		this.attribute = this.attributeManager.getAttribute(expression.substring(0, pos));
		this.operator = "=";
		if (this.attribute != null) {

			// if we're dealing with set attributes
			if (this.attribute instanceof cds.configurator.SetAttribute) {
				if (expression.substring(pos + 1) == "*") {
					this.isWild = true;
					this.values = new Array();
				} else {
					var vlist = expression.substring(pos + 1).split(",");
					if (vlist != null && vlist.length > 0) {
						for (var i = 0; i < vlist.length; i++) {
							if (this.values == null) this.values = new Array();
							this.values[this.values.length] = 
										new cds.configurator.AttributeExpressionEvaluator.SetValueExpression(attributeManager, vlist[i]);
						}
					}
				}

			// else we're dealing with range attributes
			} else {
				if (expression.substring(pos + 1) == "*") this.isWild = true;
				else this.operand = parseFloat(expression.substring(pos + 1));
			}
		}
	} else {
		alert("Error creating AttributeExpression for: " + expressionString + "\nInvalid operator.");
	}
	
	// check for errors
	if (this.attribute == null) alert("Error creating AttributeExpression for: " + expressionString + "\nInvalid attribute identifier.");
	if (this.values == null && (this.operand == null || isNaN(this.operand))) alert("Error creating AttributeExpression for: " + expressionString + "\nInvalid operand for numeric expression.");
	
	this.evaluate = function() {
		var eval = false;

		// if the values array has something, eval the SetValueExpressions
		if (this.values != null) {
			if (this.isWild) {
				eval = this.attribute.hasSelection();
			} else {
				for (var i = 0; i < this.values.length; i++) {
					if (this.values[i].evaluate()) {
						eval = true;
						break;
					}
				}
			}
		
		// otherwise we have a numeric expression
		} else {
			var v = this.attribute.value;
			if (v != null) {
				if (this.isWild) {
					eval = true;
				} else {
					if (this.operator == "=") eval = (v == this.operand);
					else if (this.operator == "<") eval = (v < this.operand);
					else if (this.operator == ">") eval = (v > this.operand);
					else if (this.operator == "<=") eval = (v <= this.operand);
					else if (this.operator == ">=") eval = (v >= this.operand);
				}
			}
		}
		
		// alert("attribute: " + this.attribute + "\nisNot: " + this.isNot + "\nisWild: " + this.isWild + "\noperator: " + this.operator + "\noperand: " + this.operand + "\nvalues: " + this.values + "\neval: " + eval);
		return (this.isNot) ? !eval : eval;		
	}

	this.destroy = function() {
		this.attributeManager = null;
		this.attribute = null;
		for (var i = 0; i < this.values.length; i++) {
			if (typeof this.values[i].destroy == "function") this.values[i].destroy();
		}
		this.values = null;
	}

	this.toString = function() {
		return "cds.configurator.AttributeExpressionEvaluator.AttributeExpression";
	}
}

// Evaluate function returns true if the given SetAttributeValue expression is
// true given the current state of the AttributeManager
// Format: attributeValue, !attributeValue (to negate the expression)
cds.configurator.AttributeExpressionEvaluator.SetValueExpression = function(attributeManager, expressionString) {
	this.attributeManager = attributeManager;
	this.value = null;
	this.isNot = false;
	
	// parse expressionString
	var expression = expressionString.trim();
	var pos = expression.indexOf("!");
	if (pos == 0) {
		this.isNot = true;
		expression = expression.substring(1);
	}
	this.value = this.attributeManager.getAttributeValue(expression);
	if (this.value == null) alert("Error creating SetValueExpression for: " + expressionString + "\nInvalid attribute value identifier.");
	
	this.evaluate = function() {
		var eval = (this.value != null && (this.value.isSelected || this.value.isHovered));
		// alert("value: " + this.value + "\nisNot: " + this.isNot + "\neval: " + eval);
		return (this.isNot) ? !eval : eval;		
	}

	this.destroy = function() {
		this.attributeManager = null;
		this.value = null;
	}

	this.toString = function() {
		return "cds.configurator.AttributeExpressionEvaluator.SetValueExpression[" + ((this.isNot) ? "!" : "") + ((this.value != null) ? this.value.id : "null") + "]";
	}
}




