var Conditions = new Object();

Conditions.FORM_NAME = "quizExec";

Conditions.Manager = function()
{
	this.goTo = null;
	
	// for debug
	window.document.conditionManager = this;	
}


Conditions.Manager.prototype = 
{
	//
	//	Init condition manager, before page is loaded
	//
	/////////////////////////////////////////////////////
	initManager: function(conditions, indexFirstQuestion, questionsNumber, questionsOutOfPage, disable)
	{
		// store conditions array
		this.conditions = conditions;
		
		// store parameters relatives to questions on page
		this.indexFirstQuestion = indexFirstQuestion;
		this.indexLastQuestion = indexFirstQuestion + questionsNumber - 1;
		
		// store boolean indicating if questions must be hidden or disabled
		this.disable = disable;
		
		// build questions' visibility object
		// if showedQuestion[Qi] = true, then question n°i must be showed
		this.showedQuestions = new Object();
		for (var i = this.indexFirstQuestion; i <= this.indexLastQuestion; i++)
			this.showedQuestions["Q" + i] = true;
				
		// build questions' values object, used when evaluating conditions
		this.questionsValues = new Object();
		
		// questions out of page - may have multiple values('+' separated),
		// so put it in an array
		for (var prop in questionsOutOfPage)
			this.questionsValues[prop] = questionsOutOfPage[prop].split("+");
		
		// questions on page
		for (var i = this.indexFirstQuestion; i <= this.indexLastQuestion; i++)
			this.questionsValues["Q" + i] = null;	
			
		// process all logical expressions
		for	(var iC = 0; iC < this.conditions.length; iC++)
		{
			var condition = this.conditions[iC];
			
			// loop on ifs
			for (var iIf = 0; iIf < condition.oIf.length; iIf++)
				condition.oIf[iIf].logicExp = this.processLogicalExp(condition.oIf[iIf].logicExp);	
		}
		
		// hide all questionnaire (to prevent questions flickering)
		/*if (navigator.appName == "Microsoft Internet Explorer")
	  	document.styleSheets[0].addRule("#" + Conditions.FORM_NAME,"display: none;");
	  else
			document.styleSheets[0].insertRule("#" + Conditions.FORM_NAME + " { display: none; }", 0);*/
		
		if (navigator.appName == "Microsoft Internet Explorer")
	  	document.styleSheets[0].addRule("#content", "display: none;");
	  else
			document.styleSheets[0].insertRule("#content { display: none; }", 0);
		
	},
	

	//
	//  onLoad : 
	//
	/////////////////////////////////////////////////////
	onLoad: function()
	{
		// build questions cardinality array
		this.buildQuestionsCard();
		
		// retrieve questions inital values
		for (var iQ = this.indexFirstQuestion; iQ <= this.indexLastQuestion; iQ++)
		{
			var indexQuestion = iQ - this.indexFirstQuestion;
			
			// simple questions
			if (this.questionCard[indexQuestion] == 0)
				this.retrieveQuestionValue(iQ);
			// table questions
			else if (this.questionCard[indexQuestion] > 0)
				for (var iC = 1; iC <= this.questionCard[indexQuestion]; iC++)
					this.retrieveQuestionValue(iQ + "_" + iC);
		}
		
		// evaluate conditions
		for	(var iC = 0; iC < this.conditions.length; iC++)
		{
			if (!this.evaluateCondition(iC))
				break;	
		}
		
		// if page empty, goto next page
		if (this.isPageEmpty())
		{
			this.redirect();
			//document.getElementById(Conditions.FORM_NAME).submit();
			//alert("empty page !");
			return;
		}
		
		// show all questionnaire (flickering problem)
		//document.getElementById(Conditions.FORM_NAME).style.display = "block";
		document.getElementById("content").style.display = "block";
				
		// update html page
		this.updatePage(-1);
	},
	
	//
	//  redirect : 
	//
	/////////////////////////////////////////////////////
	redirect: function()
	{
		var form = document.getElementById(Conditions.FORM_NAME);
		
		var strParam = "";
		
		for (var i = 0; i < form.elements.length; i++)
		{
			if (form.elements[i].type == "hidden")
				strParam += "&" + form.elements[i].name + "=" + form.elements[i].value;
		}	
		
		window.location = form.action + strParam;
	},
	
	//
	//  buildQuestionsCard : build an array containing 
	//	the cardinality of each question :
	//	0 - simple question
	//	n > 0 - table question consisting of n sub questions
	//  -1 - error 
	//
	//////////////////////////////////////////////////////////
	buildQuestionsCard: function()
	{
		this.questionCard = new Array();
		
		for (var iQ = this.indexFirstQuestion; iQ <= this.indexLastQuestion; iQ++)
		{
			// simple question
			if (document.forms[Conditions.FORM_NAME]["input_answer_" + iQ] != undefined)
				this.questionCard.push(0);
			// table question
			else
			{
				var iS = 1;
				
				while(document.forms[Conditions.FORM_NAME]["input_answer_" + iQ + "_" + iS] != undefined)
					iS++;
				
				// iS should be 2 or more
				this.questionCard.push((iS - 1) ? iS - 1 : -1);
			}
		}
	},
	
	//
	//  onChange : 
	//
	/////////////////////////////////////////////////////
	onChange: function(object)
	{
		//window.document.thisObject = object;
		
		// retrieve question num
		var iQ = object.name.slice("input_answer_".length);
		this.retrieveQuestionValue(iQ);
		
		// evaluate conditions
		for	(var iC = 0; iC < this.conditions.length; iC++)
		{
			if (!this.evaluateCondition(iC))
				break;
		}

		var iQStr = iQ + "";
		if( iQStr.indexOf("_") != -1 )
		{ //For a matrix table we must retrieve the question number (for instance we have 18_1 and we calculate 18)
      iQ = iQStr.split("_")[0];
    }
    // update html page
		this.updatePage(iQ);
	},
	
	//
	//  updatePage : update display of the questions
	//
	/////////////////////////////////////////////////////
	updatePage: function(triggerQuestion)
	{
		// update question display
		for (var i = this.indexFirstQuestion; i <= this.indexLastQuestion; i++)
		{
		  if( i != triggerQuestion )  //to avoid loop effect with manageExclusiveChoice 
		  {        //if not when a box is disabled for exclu, it would be enabled again by conditions
        if (this.showedQuestions["Q" + i])
        {
				  this.showQuestion(i);
        }
        else
        {
          this.hideQuestion(i);
        }
      }
		}
			
	},
	
	//
	//  isPageEmpty : returns true if every question in the 
	//	page is hidden
	//
	/////////////////////////////////////////////////////
	isPageEmpty: function()
	{
		for (var i = this.indexFirstQuestion; i <= this.indexLastQuestion; i++)
			if (this.showedQuestions["Q" + i])
				return false;
		
		return true;
	},
	
	//
	//	showQuestion and hideQuestion need to understand the HTML structure
	//
	/////////////////////////////////////////////////////////////////////////
		/*
		The HTML structure for ordinary question is :
		---------------------------------------------
	<tr id="tr_input_answer_1" >
		<td><SPAN>label</SPAN></td>
	</tr>
	<TR id="tr_input_input_answer_1" class="inputForm" >		
		<td id="td_input_answer_1">
	      	<input type="radio" name="input_answer_1" value="O"/>
	      	<input type="radio" name="input_answer_1" value="N"/>
      	</td>
  	</TR>
  	
        The HTML structure for table question is :
		------------------------------------------
	<tr id="tr_input_answer_2" >
		<td><SPAN>label</SPAN></td>
	</tr>
  	<TR id="tr_input_input_answer_2" class="inputForm">
		<td>
			<table class="questionTable">
				<tr class="even">
					<td class="subLabel"> <SPAN>Quels romans ? </SPAN> </td>
					
					<td id="td_input_answer_2_1">
    		<textarea name="input_answer_2_1" cols="70" rows="2"></textarea>
					</td>
				</tr>
									
				<tr class="odd">
					<td class="subLabel"> <SPAN>Quelles pièces de théâtre ? </SPAN> </td>
					
					<td id="td_input_answer_2_2">
    		<textarea name="input_answer_2_2" cols="70" rows="2"></textarea>
					</td>
				</tr>	
			</table>
 	 	</td>
 	 </TR>
		*/
		
	//
	//  showQuestion : show (or enable) question iQ
	//
	/////////////////////////////////////////////////////
	showQuestion: function(iQ)
	{
		if (!this.disable)		//=== condition_strategy is "Hide / Masquer"
		{
			var trToShow = null;
			
			// show question
			trToShow = document.getElementById("tr_br_input_answer_" + iQ);
			if (trToShow)
				trToShow.style.display = "";
			
			trToShow = document.getElementById("tr_hr_input_answer_" + iQ);
			if (trToShow)
				trToShow.style.display = "";
			
			trToShow = document.getElementById("tr_input_answer_" + iQ);
			if (trToShow)
				trToShow.style.display = "";
			
			trToShow = document.getElementById("tr_input_input_answer_" + iQ);
			if (trToShow)
				trToShow.style.display = "";
				
		}
		else					//=== condition_strategy is "Disable / Invalider"
		{
			// enable question
			var input = document.forms[Conditions.FORM_NAME]["input_answer_" + iQ];
			
			// simple question
			if( input != undefined )
			{
				this.disableSimpleQuestion(input, "input_answer_" + iQ, false);
			}
			// table question
			else
			{
        this.disableTableQuestions(iQ, false);
			}
		}
	},
	
	//
	//  hideQuestion : hide (or disable) question iQ
	//
	/////////////////////////////////////////////////////
	hideQuestion: function(iQ)
	{	
		if (!this.disable)		//=== condition_strategy is "Hide / Masquer"
		{
			var trToHide = null;
			
			// hide question
			trToHide = document.getElementById("tr_br_input_answer_" + iQ);
			if (trToHide)
				trToHide.style.display = "none";
			
			trToHide = document.getElementById("tr_hr_input_answer_" + iQ);
			if (trToHide)
				trToHide.style.display = "none";
			
			trToHide = document.getElementById("tr_input_answer_" + iQ);
			if (trToHide)
				trToHide.style.display = "none";
			
			trToHide = document.getElementById("tr_input_input_answer_" + iQ);
			if (trToHide)
				trToHide.style.display = "none";
			
		}
		else					//=== condition_strategy is "Disable / Invalider"
		{
			// disable question
			var input = document.forms[Conditions.FORM_NAME]["input_answer_" + iQ];
			
			// simple question
			if( input != undefined )
			{
				this.disableSimpleQuestion(input, "input_answer_" + iQ, true);
			}
			// table question
			else
			{
        this.disableTableQuestions(iQ, true);
			}
		}
	},
	
	//
	//  Disable or Enable a question
	//
	/////////////////////////////////////////////////////
	disableSimpleQuestion: function(input, inputName, status)
	{
    if( input.type == "hidden" && !input.length )
    {
      input.disabled = status;    //also disable the hidden field, useful for test of mandatory
      var dateName = inputName;
      
      var dateField = document.forms[Conditions.FORM_NAME][dateName + "_day"];
      if( dateField )
        dateField.disabled = status;
        
      dateField = document.forms[Conditions.FORM_NAME][dateName + "_month"];
      if( dateField )
        dateField.disabled = status;
      
      dateField = document.forms[Conditions.FORM_NAME][dateName + "_year"];
      if( dateField )
        dateField.disabled = status;
    }
    else
    {
			// for input text and select
			input.disabled = status;
			
			for (var i = 0; i < input.length; i++)
				input[i].disabled = status;
		}
	},
	
	//
	//  Disable or Enable all questions of a table
	//
	/////////////////////////////////////////////////////
	disableTableQuestions: function(iQ, status)
	{
    var iS = 1;
		while(true)
		{
			input = document.forms[Conditions.FORM_NAME]["input_answer_" + iQ + "_" + iS];
			if( input == undefined )
			{
        if( iS == 1 )   //This is not a simple table question with rows
        {               //This is a matrix question with rows and columns
          var iRow = 1;
          while(true)
          {
            if( document.forms[Conditions.FORM_NAME]["input_answer_" + iQ + "_" + iRow + "_1"] == undefined )
              break;
              
            this.disableTableQuestions(iQ+"_"+iRow, status);
            iRow++;
          }
				}
        break;
			}
			this.disableSimpleQuestion(input, "input_answer_" + iQ + "_" + iS, status);
			iS++;
		}
	},
	
	//
	//  Process logical Expression
	//
	/////////////////////////////////////////////////////
	processLogicalExp: function(logicExp)
	{
		var processedStr = "";
		
		var regexp = /((?:\|\||&&)?[^\|&]+)/g;
		var regexp2 = /(.*)@([^@]+)@(.*)@([^\@]+)@/;
		
		var result = logicExp.match(regexp);
	
		for (var i=0; i < result.length; i++)
		{
			var result2= result[i].match(regexp2);
		
			processedStr += result2[1];
			processedStr += "this.compareValue('" + result2[2]; 
			processedStr += "','" + result2[4] + "','" + result2[3] + "') ";
		}
		
		return processedStr;
	},
	
	//
	//	Hide every question in condition n°iC
	//
	/////////////////////////////////////////////////////
	hideCondition: function(iC)
	{	
		var condition = this.conditions[iC];
			
		// loop on ifs
		for (var iIf = 0; iIf < condition.oIf.length; iIf++)
			if (condition.oIf[iIf].questions)
				for (var i = 0; i < condition.oIf[iIf].questions.length; i++)
				{
					var iQ = condition.oIf[iIf].questions[i];
					this.showedQuestions["Q" + iQ] = false;
				}	
					
		// else	
		if (condition.oElse)
			if (condition.oElse.questions)
				for (var i = 0; i < condition.oElse.questions.length; i++)
				{
					var iQ = condition.oElse.questions[i]
					this.showedQuestions["Q" + iQ] = false;
				}				
	},
	
	//
	//	Evaluate condition n°iC
	// 	returns true if condition does not trigger a goto
	//
	/////////////////////////////////////////////////////
	evaluateCondition: function(iC)
	{
		var condition = this.conditions[iC];
		
		// hide condition
		this.hideCondition(iC);
		
		// check if condition can be evaluated
		for (var i = 0; i < condition.questions.length; i++)
		{
			if (!this.questionsValues["Q" + condition.questions[i]])
			{
				return true; 
			}
		}
		
		// the sub condition that evaluates to true
		var subCond = null;
		
		// loop on ifs
		for (var iIf = 0; iIf < condition.oIf.length; iIf++)
		{
      //alert("on evalue :" + condition.oIf[iIf].logicExp);
			if ( eval("(" + condition.oIf[iIf].logicExp + ")") )
			{
				subCond = condition.oIf[iIf];
				break;
			}
		}
		// should else be used ?	
		if (!subCond && condition.oElse)
		{
			subCond = condition.oElse;
		}
		
		// if we find no applicable sub condition, signal there is no goto and returns
		if (!subCond)
		{
			this.handleNoGoto(iC);
			return true;
		}
			
		// does subcondition evaluates to goto ?
		if (subCond.goto != null)
		{
			this.handleGoto(subCond.goto, iC); 
			return false;
		}
		else
		{
			this.handleNoGoto(iC);
		}
		// show only corresponding questions
		for (var i = 0; i < subCond.questions.length; i++)
		{
			var iQ = subCond.questions[i];
			this.showedQuestions["Q" + iQ] = true;
		}	
		
		return true;
	},
	
	
	//
	//	handleGoto : update the manager to reflect the effect of a goto in the page
	// 	goto : the index of the page to jump to
	//	iC : index of the condition that triggers the goto
	//
	///////////////////////////////////////////////////////////////////////////////
	handleGoto: function(goTo, iC)
	{	
		this.goTo = goTo;
		var condition = this.conditions[iC];
		
		// hide following questions
		if (condition.nextQuestion)
			for (var iQ = condition.nextQuestion; iQ <= this.indexLastQuestion; iQ++)
				this.showedQuestions["Q" + iQ] = false;
		
		// update hidden field gotoPage
		var field;
    if( parseInt(this.goTo) >= 0 )
    {
      field = document.forms[Conditions.FORM_NAME]["gotoPage"];
      field.value = this.goTo;
    }
    else
    {
      field = document.forms[Conditions.FORM_NAME]["gotoQuiz"];
      field.value = -1 * parseInt(this.goTo);
    }
		// including questions of following conditions
		//for (var i = iC; i < this.conditions.length; i++)
		//	this.hideCondition(i);
	},
	
	
	//
	//	handleNoGoto : update the manager when there is no goto in the condition
	//	iC : index of the condition where there is no goto
	//
	///////////////////////////////////////////////////////////////////////////////
	handleNoGoto: function(iC)
	{
		this.goTo = null;
		var condition = this.conditions[iC];
		
		// show following questions
		if (condition.nextQuestion)
			for (var iQ = condition.nextQuestion; iQ <= this.indexLastQuestion; iQ++)
				this.showedQuestions["Q" + iQ] = true;
				
		// update hidden field gotoPage
		var field1 = document.forms[Conditions.FORM_NAME]["gotoPage"];
		field1.value = "";
		var field2 = document.forms[Conditions.FORM_NAME]["gotoQuiz"];
		if( field2 )    //For compatibility, it may not exist
      field2.value = "";
	},
	
	
	//
	//	retrieveQuestionValue : retrieve the value(s) of a question on the page
	//
	///////////////////////////////////////////////////////////////////////////////
	retrieveQuestionValue: function(iQ)
	{
		var field = document.forms[Conditions.FORM_NAME]["input_answer_" + iQ];
		var value;
		
		// select
		//if (field instanceof HTMLSelectElement) // this do not work properly with ie
		if (typeof field == "object" && field.nodeName && field.nodeName.toLowerCase() == "select")
		{
			value = field.value;
			this.questionsValues["Q"+iQ] = value;	
		}
		// input or text area
		else if (	typeof field == "object" && field.nodeName && 
							(field.nodeName.toLowerCase() == "textarea" || field.nodeName.toLowerCase() == "input") )
		{
			;
		}
		// radio or checkbox
		else                  //array of inputs for radio of checkbox, this is a collection
		{
		  if( field[0].type.toLowerCase() == "radio" || field.length == 0 )
		  {
		      for (var i=0; i < field.length; i++)
          {
            if (field[i].checked == true)
            {
              this.questionsValues["Q"+iQ] = field[i].value;
              break;
            }
          }
      }
      else
      {
        value = new Array();
        for (var i=0; i < field.length; i++)
          if (field[i].checked == true)
            value.push(field[i].value)
			
        if (value.length > 0)
          this.questionsValues["Q"+iQ] = value;
        else
          this.questionsValues["Q"+iQ] = null;
      }
		}
	},
	
	//
	//	compareValue : compare the value(s) of question iQ with value, according operator
	//	return true if one of the comparison returns true
	//
	////////////////////////////////////////////////////////////////////////////////////
	compareValue: function(iQ, value, operator)
	{
		if (!this.questionsValues["Q"+iQ])
			return false;
			
		// if operator is superior or egal, convert value from
		// string to integer
		if (operator.indexOf("<") != -1 || operator.indexOf(">") != -1)
			value = parseInt(value);
		
		var qValue = this.questionsValues["Q"+iQ];
		
		//=== multiple values	
		if (qValue instanceof Array)
		{
		  return this.compareMultiple( qValue, operator, value);
		}
		
		//=== single value
		return eval("'" + qValue + "' " + operator + " '" + value + "'");
	},
	
	//
	//	compareMultiple : compare the value(s) of question iQ with value, according operator
	//	return true if one of the comparison returns true
	//
	////////////////////////////////////////////////////////////////////////////////////
	compareMultiple: function(qValue, operator, value)
	{
		// multiple values : 
    //    the meaning of operator == is 'contains'
    //    the meaning of operator == is 'does not contain'
    //
    // assume qValue is an array with '1 and 3' and value is V
    // we build a string :
    // for the operator == : is "1 or 3 equal to v" ?
    //    ('1' == v || '3' == v)
    // for the operator != : is "1 and 3 different from v" ?
    //    ('1' != v && '3' != v) 
    
    var toEval = "";
    var composer = " && ";
    if( operator.indexOf("==") > 0 )
      composer = " || ";

    for (var i=0; i < qValue.length; i++)     // we go there when there is at least one qValue 
    {
      if( i != 0 )
        toEval += composer;
        
      toEval += "'" + qValue[i] + "' " + operator + " '" + value + "'";
    }
    //alert( toEval );
    return eval(toEval);
    
    // Other implementation  
    /*
    for (var i=0; i < qValue.length; i++)
    {
       if(qValue[i] == value)
       {
          if( operator.indexOf("==") > 0 )  //not a strict equality, if the value is checked we return true
            return true;
          else if ( operator.indexOf("!=") > 0 ) //may have later other operators
            return false;
       }
    }
    if( operator.indexOf("==") > 0 )       //not a strict equality, if the value is checked we return true
      return false;
    return true;
    */
  }
}
