if(1==0)
    var dbg = {write:function(l,a){if(typeof this.w=='undefined') this.w=window.open().document; window.focus(); this.w.write(l.id+" - "+a+"<br/>");}};
else var dbg = {write:function(l,a){}};

/**
 * Builder method to construct the LogicalOperator composite
 *
 * Note that when using the javascript LogicalOperator's
 * and exploiting the passback values, it's important that
 * the passback be a single level deep.  copyPassback()
 * does _not_ do a _deep_ copy.
 */
function buildLogicalOperator(config,values,parent) {

    var operators = {
        or:Ca_Util_LogicalOperator_Or,
        and:Ca_Util_LogicalOperator_And
    };
    var op = new operators[config.type]();
    dbg.write(op,"in buildLogicalOperator, type is '"+config.type+"', parent is "+((typeof parent!='undefined')?parent.id:'undefined'));

    if( typeof config.negate != 'undefined' )
        op.negate    = config.negate;
        
    if( typeof config.inputs != 'undefined' ) 
        op.inputs    = config.inputs;

    if( typeof config.passback != 'undefined' )
        op.passback  = config.passback;

    op.values   = values;
    if( typeof config.conds != 'undefined' )
        for( var i = 0; i<config.conds.length; i++ ) {
            op.conds.push( buildLogicalOperator(config.conds[i],values,op) );
        }
            
    return op;
}

function Ca_Util_LogicalOperator() {
    this.__construct = function() {
        this.id       = Math.round(1000*Math.random());
        this.negate   = false;
        this.inputs   = {}; // input key/value pairs...value may be an array.  value arrays always are OR relationships
        this.conds    = []; // sub-expressions
        this.passback = {}; // a javascript object to pass back on successful evaluation...should essentially be a map of key/value pairs
        this.values   = {}; // input-name/jquery-selector pairs
    }
    this.evaluate = function() {
        return false;
    }
    this.value = function(inputName) {
        var val = this.values[inputName]; 
        return val;
    }
    this.objectContains = function(thisObj, value) {
        for( i in thisObj ) {
            dbg.write(this,"for "+thisObj[i]+" testing "+i+" against "+value);
            if( thisObj[i] == value ) {
                dbg.write(this,"matched!");
                return true;
            }
        }
        return false;
    }
    this.mergeObjects = function(o1, o2) {
        for( name in o2 ) {
            o1[name]=o2[name];
        }
    }
    this.getCondsIds = function() {
        var ids = [];
        if( typeof this.conds!='undefined' ) {
            for( n in this.conds )
                ids.push(this.conds[n].id)
        }
        return ids;
    }
    /**
     * Creates a shallow copy of this LogicalOperator's passback object
     *
     * Intended to be used in the evaluate() methods.
     *
     * Note that when using the javascript LogicalOperator's
     * and exploiting the passback values, it's important that
     * the passback be a single level deep.  copyPassback()
     * does _not_ do a _deep_ copy.
     */
    this.copyPassback = function() {
        var o = {};
        for( i in this.passback )
            o[i]=this.passback[i];
        return o;
    }
}

function Ca_Util_LogicalOperator_And() {
    this.__construct();
    this.evaluate = function(evaluatorValue) {
        dbg.write(this,"entering "+this.id+" ("+this.getCondsIds()+") LogicalAndOperator::evaluate");
        var toRet = true;
        var passback = this.copyPassback();
		for( varName in this.inputs ) {
			var inValue = this.value(varName);
			dbg.write(this,varName+" in value is "+inValue);
			dbg.write(this,"testing against "+this.inputs[varName]);
			if(typeof this.inputs[varName] == 'object' && typeof this.inputs[varName]['useFunction'] != 'undefined'){
				eval('var evalValue = this.inputField.domElement.' + this.inputs[varName]['useFunction'] + '(inValue);');
				if(evalValue == this.inputs[varName]['value'])toRet = true;
				else toRet = false;
			}
			else {
				if( typeof this.inputs[varName].length=='undefined' && ! this.objectContains(this.inputs[varName], inValue) ) {
					dbg.write("returning false");
					toRet=false;
					break;
				} else if ( typeof this.inputs[varName].length!='undefined' && inValue!=this.inputs[varName] ) {
					toRet=false;
					break;
				}
			}
		}
            
        if( toRet==true && typeof this.conds.length != 'undefined' )
            for( var i = 0; i<this.conds.length; i++ ) {
                var cond = this.conds[i];
                var ret = cond.evaluate();
                if( ret===false ) {
                    return false;
                } else this.mergeObjects(passback,ret);
            }
            
        if( this.negate == true )
            toRet=toRet?false:true;
        dbg.write(this,"AND returning "+toRet);
        if( toRet==true ) {
            return passback;
        } else return false;
    }
}
Ca_Util_LogicalOperator_And.prototype = ( function(){ return new Ca_Util_LogicalOperator(); } )();

function Ca_Util_LogicalOperator_Or() {
    this.__construct();
    this.evaluate = function() {
        dbg.write(this,"entering "+this.id+" ("+this.getCondsIds()+") LogicalOrOperator::evaluate");
        var toRet = false;
        var passback = this.copyPassback();
        for( varName in this.inputs ) {
            var inValue = this.value(varName);
            dbg.write(this,varName+" in value is "+inValue);
            dbg.write(this,"testing against "+this.inputs[varName]);
			if(typeof this.inputs[varName] == 'object' && typeof this.inputs[varName]['useFunction'] != 'undefined'){
				eval('var evalValue = this.inputField.domElement.' + this.inputs[varName]['useFunction'] + '(inValue);');
				if(evalValue == this.inputs[varName]['value'])toRet = true;
				else toRet = false;
			}
			else {
				if( typeof this.inputs[varName].length=='undefined' && this.objectContains(this.inputs[varName], inValue) ) {
					toRet=true;
					break;
				} else if ( typeof this.inputs[varName].length!='undefined' && inValue==this.inputs[varName] ) {
					toRet=true;
					break;
				}
			}
        }
        
        if( toRet!=true && typeof this.conds.length != 'undefined' )
            for( var i = 0; i<this.conds.length; i++ ) {
                var cond = this.conds[i];
                var ret = cond.evaluate();
                if( ret!==false ) {
                    this.mergeObjects(passback,ret);
                    toRet=true;
                    break;
                } 
            }
            
        if( this.negate == true )
            toRet=toRet?false:true;
        dbg.write(this,"OR returning "+toRet);
        if( toRet ) {
            return passback;
        } else return false;
    }
}
Ca_Util_LogicalOperator_Or.prototype = ( function(){ return new Ca_Util_LogicalOperator(); } )();
