/*
	mathexam.js
	author bryce fisher
	date 1/15/10
	bryce.fisher@inbox.com
	for www.learn-with-math-games.com

----mathexam - global object which has everything to run all the different kinds of math exercises. the idea is that 
i would create abstract classes which each version of the exam can call on, and the file would be cached for faster
loads on repeat visits. there would be a initializer method which activates the correct parameters for the desired
type of exam

----state: 1=set parameters / 2=enter answers / 3=correct answers
ill need to track the mathquiz state, check that the page has updated html & css to its current state, update 
the html & css.

----availableoperators=a list operators available to each exercise (+ - * /)

----equationrange=2 element array containing the low / high values for numbers on a given exam in state 2

-----answerrange=2 element array containing the lowest / highest values for the answer of exercises

----decimalplaces=integer that would determine the number of the decimal places on a given question

----numberofexercises

----questionformats=an array of predetermined and hard coded types of questions for the exercises

----exercise=a private constructor function which would be instantiated mathexam.numberofexercises number of times
when updating for state2. each exercise must retain its data between states 2 and 3, and be erased in state 1. Each
exercise would maintain its own html representatives of itself in states 2 and 3. Exercise ought to be passed a number 
which will tell it how many numbers it ought to generate randomly. It will replace 'a' - 'n' in the pattern with those 
numbers, 'o' - 'w' with availableoperators, and solve for x to determine the answer.

--checkexercises=

--setstate=method of mathexam

*/


//Using the module pattern from http://yuiblog.com/blog/2007/06/12/module-pattern/. Thanks, Eric Miraglia!
//Everything before return { ... } are private methods and properties accessible only within the mathexam module.
//Everything within return { ... } are privileged methods and properties accessible from mathexam.mymethod() or mathexam.myproperty
var mathexam = function (){
	//private DOM interface
	document.write('<style type="text/css">@import url(\'support-files/mathexam.css\');</style><form id="mathexam"></form>'); //Unorthodox, but I don't have access to <head>
	function $(id){ return document.getElementById(id); }
	function txtnode(text){ return document.createTextNode(text); }
	function setClass(obj,newclass){ try{ obj.setAttribute('class',newclass); } catch(err) { obj.setAttribute('className',newclass); } }
	var form = $('mathexam');
	function makeelem (elem,attribs,text){
		//returns DOM element elem, with attributes specified in 2d array attribs, with appended text node text.
		//elem must be a a valid HTML element, attribs must be an array (use [] for no attributes), nonstring values for text will be ignored.
		var element = document.createElement(elem);
		for(var i=0; i<attribs.length; i++){
			element.setAttribute(attribs[i][0],attribs[i][1]);
		}
		if(typeof text == 'string'){
			element.appendChild( txtnode(text) );
		}
		return element;
	}
	
	//universal config settings for the questions
	var questions = new Array();
	var numofquestions = 10;
	var operator = '+';
	var facts = new Array();
	var facttables = new Array();
	function resetfacttable(factindex){
		switch(operator){
			case '+': //just add, no weird cases to avoid
				facttables[factindex] = new Array('0','1','2','3','4','5','6','7','8','9','10');
				break;
			case '-': //no negative numbers allowed!
				facttables[factindex] = new Array('0','1','2','3','4','5','6','7','8','9','10');
				break;
			case 'x': 
				facttables[factindex] = new Array('1','2','3','4','5','6','7','8','9','10','11','12');
				break;
			case '÷':
				newfacttable = new Array();
				for(var i=0; i<=12; i++){
					newfacttable.push( i*facts[factindex] );
				}
				facttables[factindex] = newfacttable;
				break;
		}
	}
	function getfactpair(factindex){
		var fact = facts[factindex];
		if(facttables[factindex].length < 1){ resetfacttable(factindex); }
		var partnerindex = Math.floor(facttables[factindex].length*Math.random() - 0.00001);
		var partner = facttables[factindex][partnerindex];
		var lowerslice = facttables[factindex].slice(0,partnerindex);
		var upperslice = facttables[factindex].slice(partnerindex+1);
		facttables[factindex] = lowerslice.concat( upperslice );
		if(operator == '÷'){
			return new Array(partner,fact);
		} else if (operator == '-' && ((partner*1) > (fact*1))){
			return new Array(partner,fact);
		} else {
			return new Array(fact,partner);
		}
	}
	
	//Private constructor for the main child class, question which generates, maintains, and updates the HTML side of each question
	function question(qid){
		//Properties
		this.qid = "question" + qid;
		this.li = this.input = null;
		this.terms = new Array();
		this.solution = 0;
		this.correct = false;
		
		//Generate random questions of a beginner level
		if(facts.length){
			this.terms = getfactpair(Math.floor(facts.length*(Math.random() - 0.00001)));
		} else {
			if(operator == '+' || operator == '-'){
				this.terms[0] = Math.floor(11*(Math.random() - 0.00001));
				this.terms[1] = Math.floor(11*(Math.random() - 0.00001));
			} else if (operator == 'x') {
				this.terms[0] = Math.floor(13*(Math.random() - 0.00001));
				this.terms[1] = Math.floor(13*(Math.random() - 0.00001));
			} else {
				this.terms[1] = Math.floor(13*(Math.random() - 0.00001));
				this.terms[0] = this.terms[1] * Math.floor(13*(Math.random() - 0.00001));
			}
		}
		
		//Calculate the solution and write the text for HTML element
		this.text= "";
		for(var i=0; i<this.terms.length; i++){
			this.text += this.terms[i];
			if(i+1 < this.terms.length){
				this.text += " " + operator + " ";
			} 
		}
		this.solution = (eval(this.text.replace(/÷/, '/').replace(/x/, '*')));
		this.text += " = ";
		
		//Methods
		this.get = function(){
			this.li = makeelem('li',[],this.text);
			this.input = makeelem('input',[['name','question'],['id',this.qid],['title','Type only the number (ex: 4)'],['type','text']],null);
			this.li.appendChild(this.input);
			return this.li;
		};
		this.check = function(){
			if (this.input.value == this.solution){
				this.correct = true;
				setClass(this.li, 'correct');
				return true;
			} else {
				setClass(this.li, 'incorrect');
				this.input.value = this.solution;
				return false;
			}
		};
	}
	
	//module configuration
	var state = 1; //state 1 - set options, state 2 - ask the questions, state 3 - grade the answers
	function setstate(newstate){ 
		//Core logic. Updates the state, internal variables, and the maintains the HTML on the page.
		switch(newstate){
			case 1: //Let the user input the mathexam parameters (number of questions, what type, etc)
				questions = new Array(); //delete the old questions
				form.innerHTML = ''; //erase the old html
			
				//Use DOM to allow user to pick options
				//Okay, okay. I cheated on my whole 'objected oriented' thing here and 
				//went straight up procedural. So I still have some learning to do!
				function picknumofqs(){
					form.appendChild( makeelem('h2', [], 'How Many Questions?') );
					var numol = makeelem('ol', [['id','numofquestions']],null);
					var options = new Array('5','10','25','50','75','100');
					for(var i=0; i< options.length; i++){
						var li = makeelem('li', [[]], null);
						var a = makeelem('a', [['href','#' + i]], options[i]);
						a.onclick = function(e){
							var event = window.event || e;
							var elem = event.target || event.srcElement;
							var href = elem.href;
							href = href.split('#');
							numofquestions = options[href[1]];
							form.innerHTML = '';
							pickfacts();
						};
						li.appendChild( a );
						numol.appendChild( li );
					}
					form.appendChild( numol );
				}
				
				function pickfacts(){
					form.appendChild( makeelem('h2', [], 'Focus on Which Numbers?') );
					var factol = makeelem('ol', [['id','facts']],null);
					switch(operator){
						case '+':
							var options = new Array('1','2','3','4','5','6','7','8','9','10');
							break;
						case '-':
							var options = new Array('1','2','3','4','5','6','7','8','9');
							break;
						case 'x':
							var options = new Array('2','3','4','5','6','7','8','9','10','11','12');
							break;
						case '÷':
							var options = new Array('2','3','4','5','6','7','8','9','10','11','12');
							break;
					}
					for(var i=0; i< options.length; i++){
						var li = makeelem('li', [[]], null);
						var input = makeelem('input', [['name','fact'],['id','fact' + i],['type','checkbox'],['value',options[i]]], null);
						li.appendChild( input );
						var label = makeelem('label', [['for','fact'+i]], options[i]);
						label.onclick = function(e){
							var event = window.event || e;
							var elem = event.target || event.srcElement;
							elem = elem.parentNode;
							if(elem.className){
								setClass(elem,'');							
							} else {
								setClass(elem,'selected');
							}
						};
						li.appendChild( label );
						factol.appendChild( li );
					}
					form.appendChild(  factol );
					var beginExamButton = makeelem('input',[['type','button'],['value','Begin The Exam!']],null);
					beginExamButton.onclick = function(){
						facts = new Array();
						for(var i=0; i<factol.childNodes.length; i++){
							if(factol.childNodes[i].className){
								facts.push(factol.childNodes[i].firstChild.value);
							}
						}
						if(facts.length == 0){ facts[0] = "random"; }
						setstate(2); //Now that we have all the users preferences set, generate the quiz
					};
					form.appendChild( beginExamButton );
				}
				
				function pickoperator(){
					form.appendChild( makeelem('h2', [], 'What Type of Exercises?') );
					var opol = makeelem('ol', [['id','operator']],null);
					var options = new Array('+','-','x','÷');
					for(var i=0; i< options.length; i++){
						var li = makeelem('li', [[]], null);
						var a = makeelem('a', [['href','#' + i]], options[i]);
						a.onclick = function(e){
							var event = window.event || e;
							var elem = event.target || event.srcElement;
							var href = elem.href;
							href = href.split('#');
							operator = options[href[1]];
							form.innerHTML = '';
							picknumofqs();
						};
						li.appendChild( a );
						opol.appendChild( li );
					}
					form.appendChild(  opol );
				}
				pickoperator();
				
				break;
			case 2://present the questions for the user to answer.
				if(state==1){ //if we're coming from state 1, we need to generate a new set of questions and render them.
					form.innerHTML = ''; //erase the old html
					
					//Prepare the facttables
					facttables = new Array(); //clear previous data, if any
					for(var i=0; i<facts.length; i++){
						facttables[i] = new Array();
					}
					
					//Generate the elements
					var ol = makeelem('ol',[['id','questionlist']],null);
					for(var i=0; i<numofquestions; i++){
						questions[i] = new question(i);
						ol.appendChild(questions[i].get());
					}
					form.appendChild(ol);
					
					//Create user controls to switch the state.
					var checkansbutton = makeelem('input',[['type','submit'],['value','Check Answers']],null);
					form.appendChild(checkansbutton);
					form.onsubmit = function(){ setstate(3); return false; }; //suppress form submission; device independence (kybd or mouse) makes this more accessible
					var newexambutton = makeelem('input',[['type','button'],['value','Stop this exam']],null);
					form.appendChild(newexambutton);
					newexambutton.onclick = function(){ setstate(1); return false;};
					
					//Put the cursor in the first question.
					questions[0].input.focus();
				} else if(state==3) {
					//reset the page...
				} //we'll ignore the possibility that we're coming to state 2 from state 2.
				break;
			case 3: //check the answers
				var correctanswers = 0;
				var wronganswers = "";
				for(var i = 0; i<questions.length; i++){
					if(questions[i].check()){
						correctanswers++;
					} else if(wronganswers == ""){
						wronganswers = (i+1);
					} else {
						wronganswers += ", " + (i+1);
					}
				}
				if(wronganswers == ""){
					alert("Brilliant! You've answered all questions correctly.");
				} else {
					alert( correctanswers + " right answers out of " + numofquestions + " total answers.\nYou answered incorrectly on question" + ((isNaN(wronganswers))?  "s":"") + " " +wronganswers + ".");
				}
				break;
			default:
				break; //ignore all other states
		}
		
		//Update state
		if( !isNaN(newstate) && Math.round(newstate)== newstate && newstate <= 3 && 1 <= newstate ){
			state = newstate;
		}
	}
	
	//This anonymous function returns an object (everything within return { ... } ) whose methods and properties become the public interface of mathexam.
	return {
		init : function(settings){
			//if(typeof settings.defaultnumofqs != 'undefined'){ defaultnumofqs = settings.defaultnumofqs; numofquestions = settings.defaultnumofqs;  }
			setstate(1);
		},
		debug : function(settings){
			numofquestions = 3;
			operator = "-";
			facts = new Array('10');
			setstate(2);
		}
	}
}(); //The parentheses here execute the anonymous function, making mathexam module ready to work right away!

mathexam.init({});
