function buildFormProcessor(form,config)
{
    if( typeof config.type != 'undefined' )
        eval('var clazz = '+config.type);
    else var clazz = Ca_Form_Processor;
    var ret = new clazz(form,config);
    
    return ret;
}
    
/**
 * Base class for all form processors
 * 
 * Clients are expected to set:
 *     this.processCallback = ['objectName','methodName']
 */
function Ca_Form_Processor(form,config)
{
    this.__construct = function(form,config) {
        this.form = form;
        this.config = config;
        if( typeof form != 'undefined' )
            this.inputValues = form.inputValues;
    }
    this.process = function() {
        // to be filled in by descendent classes
    }
    this.reset = function() {
    }
    this.__construct(form,config);
}

/**
 * Clients are expected to set:
 *     this.questionSelector = '#question-content-location'
 */
function Ca_Form_Processor_LogicalOperator(form,config)
{
    this.__parentConstruct = this.__construct;
    
    this.__construct = function(form,config) {
        this.__parentConstruct(form,config);

        // there may be linked forms
        this.linked = {};
        if( typeof config.linked != 'undefined' )
            for( var link in config.linked ) {
                var tmp = buildFormProcessor( form, config.linked[link] );
                this.linked[link] = tmp;
            }

        // there ought to be logic, elsewise why use this at all??
        this.logic = {};
        this.logicArray = [];
		this.previousLogic = [];
        if( typeof config.logic != 'undefined' ) {
                
            for( var input in config.logic ) {
                this.logic[input] = buildLogicalOperator( config.logic[input], this.form.inputValues );
                this.logic[input].name = input;
                this.logicArray.push( this.logic[input] );
            }
            
            // the logic that we are using will change if they 
            // transition to a linked form...then we'll use that logic instead.
            this.currentProcessor = this;
            
            // default to the first input
            this.currentInput = this.logicArray[0].name;
        }
                
        if( typeof config.javascript != 'undefined' && typeof config.javascript.processCallback != 'undefined' && config.javascript.processCallback.length > 0 )
            this.processCallback = config.javascript.processCallback;
        else this.processCallback = [];
        
        this.linkedStack = [];
    }
    
    /**
     * Evaluates the form against the loaded logical operators and executes the processCallback with the result.
     */
    this.process = function()
    {
        // pull the value from the dom element representing the input
        // and update the shared inputValues object with the result
		var inp = form.inputs[this.currentInput];
		
        var value = inp.domElement.getValue();
        this.inputValues[this.currentInput] = value;
        
        //var actualVal = form.inputs[this.currentInput].domElement.getActualValue();
        //alert( this.currentInput+'='+value+','+actualVal);
        
        // get the logical operator associated to the current input
        // call it's evaluate method and retain the passback for further
        // processing
        var currentOperator = this.currentProcessor.logic[this.currentInput];
		currentOperator.inputField = inp;
		var passback = currentOperator.evaluate();

        // for forms, the passback specifies the next action to occur
        // it can be either :
        //   - a link to another form processor (branch of logic),
        //   - or it can be the next question alone
        //   - or it can be a result
        if( typeof passback.type != 'undefined' ) {
            
            // the passback.value for both links and questions will be
            // the name of another question to transition to
            // the transition itself should be handled by the 
            // processCallback.
            if( passback.type == 'question' ) {
                this.currentInput = passback.value;
				this.previousLogic.push({passback:passback,input:passback.value,processor:this.currentProcessor});
            }
            else if( passback.type == 'link' && typeof passback.form != 'undefined' && typeof passback.value != 'undefined' ){
                // if it's a link, then we should update the currentLogic member,
                // so that the next call to process will be on the correct
                // branch of logic.
                if( passback.form=='parent' && this.linkedStack.length > 0 ){
					var tempProcessor = this.linkedStack.pop();
                    this.currentProcessor = tempProcessor;
					this.previousLogic.push({passback:passback,input:passback.value,processor:tempProcessor});
                }
				else if( typeof this.currentProcessor.linked[passback.form] != 'undefined' ){
                    this.linkedStack.push( this.currentProcessor );
					this.previousLogic.push({passback:passback,input:passback.value,processor:this.currentProcessor.linked[passback.form]});
                    this.currentProcessor = this.currentProcessor.linked[passback.form];
                }
                this.currentInput = passback.value;
            }
			else if(passback.type == 'result') {
				this.currentInput = 'result';
				this.previousLogic.push({passback:passback,input:passback.type,processor:this.currentProcessor});
			}
			
			var tempLinkedStack = [];
			for(i in this.linkedStack){
				tempLinkedStack[i] = this.linkedStack[i];
			}
			this.previousLogic[this.previousLogic.length-1].linkedStack = tempLinkedStack;
			
        } 
		else return passback;
        
        // execute the callback, if one has been supplied
        var currentInput = this.currentInput;

        if( this.processCallback.length==2 )
            var toEval = this.processCallback[0]+"."+this.processCallback[1]+"(this,currentInput,value,passback);";
        else if( this.processCallback.length==1 )
            var toEval = this.processCallback[0]+"(this,currentInput,value,passback);";
        if( typeof toEval != 'undefined' )
            eval(toEval);
        
        return passback;
    }
	
	/**
	 * So, we want to push to another input without using any logic.
	 * passed to this is a passback that it will use.
	 */
	this.deadProcess = function(passback)
    {
		var inp = form.inputs[this.currentInput];
        var value = inp.domElement.getValue();
        // for forms, the passback specifies the next action to occur
        // it can be either :
        //   - a link to another form processor (branch of logic),
        //   - or it can be the next question alone
        //   - or it can be a result
        if( typeof passback.type != 'undefined' ) {
            
            // the passback.value for both links and questions will be
            // the name of another question to transition to
            // the transition itself should be handled by the 
            // processCallback.
            if( passback.type == 'question' ) {
                this.currentInput = passback.value;
				this.previousLogic.push({passback:passback,input:passback.value,processor:this.currentProcessor});
            }
            else if( passback.type == 'link' && typeof passback.form != 'undefined' && typeof passback.value != 'undefined' ){
                // if it's a link, then we should update the currentLogic member,
                // so that the next call to process will be on the correct
                // branch of logic.
                if( passback.form=='parent' && this.linkedStack.length > 0 ){
					var tempProcessor = this.linkedStack.pop();
                    this.currentProcessor = tempProcessor;
					this.previousLogic.push({passback:passback,input:passback.value,processor:tempProcessor});
                }
				else if( typeof this.currentProcessor.linked[passback.form] != 'undefined' ){
                    this.linkedStack.push( this.currentProcessor );
					this.previousLogic.push({passback:passback,input:passback.value,processor:this.currentProcessor.linked[passback.form]});
                    this.currentProcessor = this.currentProcessor.linked[passback.form];
                }
                this.currentInput = passback.value;
            }
			else this.previousLogic.push({passback:passback,input:passback.value,processor:this.currentProcessor});
			
			var tempLinkedStack = [];
			for(i in this.linkedStack){
				tempLinkedStack[i] = this.linkedStack[i];
			}
			this.previousLogic[this.previousLogic.length-1].linkedStack = tempLinkedStack;
			
        } 
		else return passback;
        
        // execute the callback, if one has been supplied
        var currentInput = this.currentInput;

        if( this.processCallback.length==2 )
            var toEval = this.processCallback[0]+"."+this.processCallback[1]+"(this,currentInput,value,passback);";
        else if( this.processCallback.length==1 )
            var toEval = this.processCallback[0]+"(this,currentInput,value,passback);";
        if( typeof toEval != 'undefined' )
            eval(toEval);
        
        return passback;
    }
	
	
	this.goBack = function(){
		this.previousLogic.pop()
		var backInput = this.previousLogic[this.previousLogic.length - 1];
		this.currentProcessor = backInput.processor;
		this.currentInput = backInput.input;
		this.currentProcessor.currentInput = backInput.input;
		productMatcher.previousCallback(this,this.currentInput,form.inputs[this.currentInput].domElement.getValue(),backInput.passback);
		if(typeof backInput.linkedStack != 'undefined'){
			this.linkedStack = [];
			for(i in backInput.linkedStack){
				this.linkedStack[i] = backInput.linkedStack[i];
			}
		}
		return true;
	}
    
    this.reset = function() {
		this.previousLogic = [];
		this.previousLogic.push({passback:null,input:this.logicArray[0].name,processor:this});
        this.linkedStack = [];
        this.currentProcessor = this;
        this.currentInput = this.logicArray[0].name;
        this.linkedStack=[];
    }
    
    this.__construct(form,config);
}
Ca_Form_Processor_LogicalOperator.prototype = ( function(config) { return new Ca_Form_Processor(config); } )();
