//////////////////////////////////////////////////////////////
//															//
//	© 2005 - 2007 Vereyon									//
//  http://www.vereyon.nl									//
//	All rights reserved										//
//															//
//////////////////////////////////////////////////////////////

RepeatMethodEnum = function() {}
//RepeatMethodEnum.__type = "RepeatMethodEnum";
RepeatMethodEnum.PIT = 0;
RepeatMethodEnum.INFINITE = 1;
RepeatMethodEnum.UNTIL_KNOWN = 2;
RepeatMethodEnum.NONE = 3;

QuestionDirectionEnum = function() {}
//QuestionDirectionEnum.__type = "QuestionDirectionEnum";
QuestionDirectionEnum.PRIMARY_SECONDARY = 0;
QuestionDirectionEnum.SECONDARY_PRIMARY = 1;
QuestionDirectionEnum.MIXED = 2;

QuestionSequenceEnum = function() {}
QuestionSequenceEnum.ASCENDING = 0;
QuestionSequenceEnum.DESCENDING = 1;
QuestionSequenceEnum.RANDOM = 2;

TestStyleEnum = function() {}
TestStyleEnum.OPEN_QUESTION = 0;
TestStyleEnum.MULTIPLE_CHOICE_2 = 1;
TestStyleEnum.MULTIPLE_CHOICE_3 = 2;
TestStyleEnum.MULTIPLE_CHOICE_4 = 3;
TestStyleEnum.MULTIPLE_CHOICE_5 = 4;
TestStyleEnum.MULTIPLE_CHOICE_6 = 5;
TestStyleEnum.THOUGHT_EXERCICE = 6;
TestStyleEnum.WRITING_EXERCICE = 7;
TestStyleEnum.DICTATION = 8;

CheckTypeEnum = function() {}
CheckTypeEnum.EXACT = 0;
CheckTypeEnum.IGNORE_CASE = 1;
CheckTypeEnum.IGNORE_PUNCTUATION = 2;
CheckTypeEnum.IGNORE_WORD_SEQUENCE = 3;
CheckTypeEnum.IGNORE_CASE_PUCTUATION = 4;
CheckTypeEnum.IGNORE_CASE_PUCTUATION_WORD_SEQUENCE = 5;

//LearningEngine.prototype.
LearningEngine.prototype.__type = "LearningEngine";
LearningEngine.prototype.punctuationRegEx = null;

LearningEngine.prototype.wordList = null;
LearningEngine.prototype.bRunning = false;
LearningEngine.prototype.bFinished = false;

LearningEngine.prototype.bShowRemarks = true;
LearningEngine.prototype.bEnableSpeech = false;
LearningEngine.prototype.repeatMethod = null;
LearningEngine.prototype.questionDirection = null;
LearningEngine.prototype.questionSequence = null;
LearningEngine.prototype.testStyle = null;
LearningEngine.prototype.answerCheckType = null;
LearningEngine.prototype.iAnswerTime = 0;
LearningEngine.prototype.iAnswerTimeRemaining = 0;

LearningEngine.prototype.arrQuestions = null;
LearningEngine.prototype.arrFaultedQuestions = null;
LearningEngine.prototype.iQuestionCount = 0;
LearningEngine.prototype.iErrorCount = 0;
LearningEngine.prototype.iScore = 0;
LearningEngine.prototype.testStartTime = null;

LearningEngine.prototype.strCurrentQuestion = null;
LearningEngine.prototype.strCurrentChoices = null;
LearningEngine.prototype.strCurrentKey = null;
LearningEngine.prototype.strCurrentRemark = null;
LearningEngine.prototype.iCurrentPool = null;
LearningEngine.prototype.currentQuestionDirection = null;
LearningEngine.prototype.iPreviousWord = null;

LearningEngine.prototype.sourcePool = null;
LearningEngine.prototype.primaryPool = null;
LearningEngine.prototype.secondaryPool = null;
LearningEngine.prototype.tertiaryPool = null;
LearningEngine.prototype.quaternaryPool = null;
LearningEngine.prototype.quinaryPool = null;


function LearningEngine() {

	this.Initialize = function LearningEngine$Initialize() {
	
		this.repeatMethod = RepeatMethodEnum.PIT;
		this.questionDirection = QuestionDirectionEnum.PRIMARY_SECONDARY;
		this.questionSequence = QuestionSequenceEnum.ASCENDING;
		this.testStyle = TestStyleEnum.OPEN_QUESTION;
		this.answerCheckType = CheckTypeEnum.IGNORE_CASE;
		this.strCurrentChoices = new Array();
		this.bShowRemarks = true;
		
		this.sourcePool = new Array();
		this.primaryPool = new Array();
		this.secondaryPool = new Array();
		this.tertiaryPool = new Array();
		this.quaternaryPool = new Array();
		this.quinaryPool = new Array();
		
		this.arrQuestions = new Array();
		
		// Enable full interpunction stripping by stripping diacritics from unicode string
		//this.punctuationRegEx = /\pM+/g;
		//this.punctuationRegEx = /[\.,ï¿½ï¿½;!ï¿½#\$\/:\?ï¿½'"ï¿½ï¿½ï¿½\(\)\[\]_\-\\]/g;
		this.punctuationRegEx = /[-!"#$%&'()*+,.\/:;<=>?@[\\\]_`{|}~]/g;

	}
	
	this.StartNewTest = function LearningEngine$StartNewTest() {
	
		this.sourcePool = new Array();
		this.primaryPool = new Array();
		this.secondaryPool = new Array();
		this.tertiaryPool = new Array();
		this.quaternaryPool = new Array();
		this.quinaryPool = new Array();
		
		this.arrQuestions = new Array();
		this.arrFaultedQuestions = new Array();
		
		this.iQuestionCount = 0;
		this.iErrorCount = 0;
	
		// Declare variables
		var newQuestion;
		var arrTempPool;
		var iIndex;
		
		// Generate source question pool
		// Iterate trough words in wordlist
		for(i = 0; i < this.wordList.Count(); i++) {
		
			// Check if word is valid
			if(this.wordList.GetValue(i, 1).trim() == "" || this.wordList.GetValue(i, 2).trim() == "")
				continue;
		
			newQuestion = new LearningEngineQuestion();
			newQuestion.wordIndex = i;
			
			this.sourcePool.push(newQuestion);
		}
		
		// Reverse pool
		if(this.questionSequence == QuestionSequenceEnum.DESCENDING) {
			this.sourcePool.reverse();
		}
		
		// Shuffle pool
		if(this.questionSequence == QuestionSequenceEnum.RANDOM) {
		
			// Copy source pool contents to temporary pool and clear the source pool
			arrTempPool = new Array();
			while(this.sourcePool.length > 0)
				arrTempPool.push(this.sourcePool.pop());
			this.sourcePool = new Array();
			
			// Rebuild source pool in random order
			while(arrTempPool.length > 0) {
			
				iIndex = Math.round(Math.random() * (arrTempPool.length - 1));
				this.sourcePool.push(arrTempPool[iIndex]);
				arrTempPool.splice(iIndex, 1);
			}
		}
		
		// Fill primary pool
		switch(this.repeatMethod) {
			case RepeatMethodEnum.INFINITE:
			case RepeatMethodEnum.UNTIL_KNOWN:
			case RepeatMethodEnum.NONE:
				
				// Move all questions to the primary pool
				for(i = 0; i < this.sourcePool.length; i++) {
					this.primaryPool.push(this.sourcePool[i]);
				}
				
				// Clear the source pool
				this.sourcePool = new Array();
				break;
		}
		
		this.bRunning = true;
		this.bFinished = false;
	}
	
	this.StopTest = function LearningEngine$StopTest() {
	
		this.bRunning = false;
		this.bFinished = true;
		
		this.sourcePool = new Array();
		this.primaryPool = new Array();
		this.secondaryPool = new Array();
		this.tertiaryPool = new Array();
		this.quaternaryPool = new Array();
	}
	
	this.TestIsFinished = function LearningEngine$TestIsFinished() {
	
		// Check if any questions remain in the question pools
		if(this.sourcePool.length == 0 && this.primaryPool.length == 0 && this.secondaryPool.length == 0 && this.tertiaryPool.length == 0 && this.quaternaryPool.length == 0)
			return true;
			
		return false;
	}
	
	this.NextQuestion = function LearningEngine$NextQuestion() {
	
		// Declare variables
		var iWord, iCount, iMultipleChoiceWord, iIndex;
		var hWord;
		var questionDirection;
		var arrWordIndexes;
		
		// Pick question direction
		if(this.questionDirection == QuestionDirectionEnum.MIXED) {
			this.currentQuestionDirection = Math.round(Math.random());
		} else {
			this.currentQuestionDirection = this.questionDirection;
		}
		
		// Fill primary pool if appropriate
		switch(this.repeatMethod) {
			case RepeatMethodEnum.PIT:
				
				// Make sure there are at least 2 words in the primary pool but don't increase this number if the other pools are far full
				while(this.primaryPool.length < 2 && this.sourcePool.length > 0 && this.secondaryPool.length < 4 && this.tertiaryPool.length < 8 && this.quaternaryPool.length < 16) {
					this.primaryPool.push(this.sourcePool[0]);
					this.sourcePool.splice(0, 1);
				}
				break;
		}
		
		// Pick the next word
		iWord = this.SelectNextQuestion();
		if(iWord === false)
			return false;
		
		// Store the selcted word index for re checking when fetching the next question
		this.iPreviousWord = iWord;
		
		// Load the question, key and remark strings
		if(this.currentQuestionDirection == QuestionDirectionEnum.PRIMARY_SECONDARY) {
			this.strCurrentQuestion = this.wordList.GetValue(iWord, 1);
			this.strCurrentKey = this.wordList.GetValue(iWord, 2);
			this.strCurrentRemark = this.wordList.GetValue(iWord, 3);
		} else if(this.currentQuestionDirection == QuestionDirectionEnum.SECONDARY_PRIMARY) {
			this.strCurrentQuestion = this.wordList.GetValue(iWord, 2);
			this.strCurrentKey = this.wordList.GetValue(iWord, 1);
			this.strCurrentRemark = this.wordList.GetValue(iWord, 4);
		}
		
		///////////////////////////////////////////////////////////////////////////////////////
		// Generate multiple choice options if appropriate
		if(this.testStyle == TestStyleEnum.MULTIPLE_CHOICE_2 || this.testStyle == TestStyleEnum.MULTIPLE_CHOICE_3 || this.testStyle == TestStyleEnum.MULTIPLE_CHOICE_4 || this.testStyle == TestStyleEnum.MULTIPLE_CHOICE_5 || this.testStyle == TestStyleEnum.MULTIPLE_CHOICE_6) {
			this.strCurrentChoices = new Array();
			
			switch(this.testStyle) {
				case TestStyleEnum.MULTIPLE_CHOICE_2:
					iCount = 1;
					break;
				case TestStyleEnum.MULTIPLE_CHOICE_3:
					iCount = 2;
					break;
				case TestStyleEnum.MULTIPLE_CHOICE_4:
					iCount = 3;
					break;
				case TestStyleEnum.MULTIPLE_CHOICE_5:
					iCount = 4;
					break;
				case TestStyleEnum.MULTIPLE_CHOICE_6:
					iCount = 5;
					break;
			}
			
			// Copy all word id's
			arrWordIndexes = new Array();
			for(i = 0; i < this.wordList.Count(); i++) {
			
				// Check if word is valid
				if(this.wordList.GetValue(i, 1) != "" && this.wordList.GetValue(i, 2) != "" && i != iWord) {
					arrWordIndexes.push(i)
				}
			}
			
			// Fetch the words at random from the wordlist
			for(i = 0; this.strCurrentChoices.length < iCount && arrWordIndexes.length > 0; i++) {
			
				iMultipleChoiceWord = Math.round(Math.random() * (arrWordIndexes.length - 1));
				
				switch(this.currentQuestionDirection) {
					case QuestionDirectionEnum.PRIMARY_SECONDARY:
						this.strCurrentChoices.push(this.wordList.GetValue(arrWordIndexes[iMultipleChoiceWord], 2));
						break;
					case QuestionDirectionEnum.SECONDARY_PRIMARY:
						this.strCurrentChoices.push(this.wordList.GetValue(arrWordIndexes[iMultipleChoiceWord], 1));
						break;
				}
				
				arrWordIndexes.splice(iMultipleChoiceWord, 1);
			}
			
			// Insert the correct answer at a random position in the options array
			iIndex = Math.round(Math.random() * iCount);
			this.strCurrentChoices.splice(iIndex, 0, this.strCurrentKey);
		}
		//
		///////////////////////////////////////////////////////////////////////////////////////
		
		return true;
	}
	
	this.SelectNextQuestion = function LearningEngine$SelectNextQuestion() {
	
		// Declare variables
		var iWord;
		var hFirstWord, hSecondWord;
	
		iRandom = Math.round(Math.random() * 100)
	
		if((iRandom >= 50 && this.primaryPool.length > 0) || (this.secondaryPool.length == 0 && this.tertiaryPool.length == 0 && this.quaternaryPool.length == 0 && this.primaryPool.length > 0)) {
		
			// Check if the current question is not equal to the previous question
			if(this.iPreviousWord == this.primaryPool[0].wordIndex) {
			
				// Flip the first two questions in this pool in order to prevent quick repeating of questions
				if(this.primaryPool.length >= 2) {
					
					hFirstWord = this.primaryPool[0];
					hSecondWord = this.primaryPool[1];
					
					this.primaryPool[0] = hSecondWord;
					this.primaryPool[1] = hFirstWord;
				}
			}
			
			// Get the next question from the primary pool
			this.iCurrentPool = 1;
			iWord = this.primaryPool[0].wordIndex;
			
		} else if((iRandom >= 25 && this.secondaryPool.length > 0) || (this.tertiaryPool.length == 0 && this.quaternaryPool.length == 0 && this.secondaryPool.length > 0)) {
		
			// Check if the current question is not equal to the previous question
			if(this.iPreviousWord == this.secondaryPool[0].wordIndex) {
				
				// Attempt to get a question from the primary pool
				if(this.primaryPool.length > 0) {
				
					this.iCurrentPool = 1;
					iWord = this.primaryPool[0].wordIndex;
					return iWord;
				} else {
				
					// Attempt to flip the first two questions in order to prevent quick repeating of questions
					if(this.secondaryPool.length >= 2) {
					
						hFirstWord = this.secondaryPool[0];
						hSecondWord = this.secondaryPool[1];
					
						this.secondaryPool[0] = hSecondWord;
						this.secondaryPool[1] = hFirstWord;
					}
				}
			}
			
			// Get the next question from the secondary pool
			this.iCurrentPool = 2;
			iWord = this.secondaryPool[0].wordIndex;
			
		} else if((iRandom >= 7 && this.tertiaryPool.length > 0) || (this.quaternaryPool.length == 0 && this.tertiaryPool.length > 0)) {
		
			// Get the next question from the tertiary pool
			this.iCurrentPool = 3;
			iWord = this.tertiaryPool[0].wordIndex;
			
		} else if(this.quaternaryPool.length > 0) {
		
			// Get the next question from the quaternaryPool pool
			this.iCurrentPool = 4;
			iWord = this.quaternaryPool[0].wordIndex;
			
		} else {
			return false;
		}
		
		return iWord;
	}
	
	this.CheckAnswer = function LearningEngine$CheckAnswer(strAnswer) {
	
		// Declare variables
		var strTrimmedAnswer;
		var strTrimmedKey;
		var strAnswerPieces, strKeyPieces;
		
		strTrimmedAnswer = strAnswer.trim();
		strTrimmedKey = this.strCurrentKey.trim();
		
		// Make test case insensitive
		if(this.answerCheckType == CheckTypeEnum.IGNORE_CASE || this.answerCheckType == CheckTypeEnum.IGNORE_CASE_PUCTUATION || this.answerCheckType == CheckTypeEnum.IGNORE_CASE_PUCTUATION_WORD_SEQUENCE) {
			strTrimmedAnswer = strTrimmedAnswer.toLowerCase();
			strTrimmedKey = strTrimmedKey.toLowerCase();
		}
		
		// Make test punctuation insensitive
		if(this.answerCheckType == CheckTypeEnum.IGNORE_PUNCTUATION || this.answerCheckType == CheckTypeEnum.IGNORE_CASE_PUCTUATION || this.answerCheckType == CheckTypeEnum.IGNORE_CASE_PUCTUATION_WORD_SEQUENCE) {
			strTrimmedAnswer = strTrimmedAnswer.replace(this.punctuationRegEx, "");
			strTrimmedKey = strTrimmedKey.replace(this.punctuationRegEx, "");
		}
		
		if(this.answerCheckType == CheckTypeEnum.EXACT || this.answerCheckType == CheckTypeEnum.IGNORE_CASE || this.answerCheckType == CheckTypeEnum.IGNORE_PUNCTUATION || this.answerCheckType == CheckTypeEnum.IGNORE_CASE_PUCTUATION) {
			if(strTrimmedAnswer == strTrimmedKey) {
				this.HandleCorrectAnswer();
				return true;
			} else {
				this.HandleIncorrectAnswer();
				return false;
			}
		} else if(this.answerCheckType == CheckTypeEnum.IGNORE_WORD_SEQUENCE || this.answerCheckType == CheckTypeEnum.IGNORE_CASE_PUCTUATION_WORD_SEQUENCE) {
			
			// Split answer and key in single words
			strAnswerPieces = strTrimmedAnswer.split(" ");
			strKeyPieces = strTrimmedKey.split(" ");
			
			// Trim strings and remove empty strings
			for(var x in strAnswerPieces) {
				strAnswerPieces[x] = strAnswerPieces[x].trim();
				if(strAnswerPieces[x].length == 0)
					strAnswerPieces.splice(x, 1);
			}
			for(var x in strKeyPieces) {
				strKeyPieces[x] = strKeyPieces[x].trim();
				if(strKeyPieces[x].length == 0)
					strKeyPieces.splice(x, 1);
			}
			
			// Compare answer to key
			for(var x in strAnswerPieces) {
				bFound = false;
				for(var y in strKeyPieces) {
					if(strKeyPieces[y] == strAnswerPieces[x]) {
						strKeyPieces.splice(y, 1);
						bFound = true;
						break;
					}
				}
				
				if(!bFound) {
					this.HandleIncorrectAnswer();
					return false;
				}
			}
			
			// Check if any elements reside in the answer strings array
			if(strKeyPieces.length > 0) {
				this.HandleIncorrectAnswer();
				return false;
			}
				
			this.HandleCorrectAnswer();
			return true;
		}
	}
	
	this.HandleIncorrectAnswer = function LearningEngine$HandleIncorrectAnswer() {
	
		this.iErrorCount++;
		this.iQuestionCount++;
		
		// Store the current word as a faulted question
		this.arrFaultedQuestions.push(this.iPreviousWord);
		
		// Work on the right pool
		switch(this.iCurrentPool) {
			
			case 1:
				if(this.repeatMethod == RepeatMethodEnum.INFINITE || this.repeatMethod == RepeatMethodEnum.UNTIL_KNOWN) {
				
					// Push question to the back of the pool
					this.primaryPool.push(this.primaryPool[0]);
					this.primaryPool.splice(0, 1);
				} else if(this.repeatMethod == RepeatMethodEnum.PIT) {
					
					// Advance question to the back of the pool
					this.secondaryPool.push(this.primaryPool[0]);
					this.primaryPool.splice(0, 1);
				} else {
				
					// Remove question from the pool
					this.primaryPool.splice(0, 1);
				}
				break;
			
			case 2:
				if(this.repeatMethod == RepeatMethodEnum.INFINITE || this.repeatMethod == RepeatMethodEnum.UNTIL_KNOWN) {
				
					// Push question to the back of the pool
					this.secondaryPool.push(this.secondaryPool[0]);
					this.secondaryPool.splice(0, 1);
				} else if(this.repeatMethod == RepeatMethodEnum.PIT) {
					
					// Advance question to the previous pool
					this.primaryPool.push(this.secondaryPool[0]);
					this.secondaryPool.splice(0, 1);
				} else {
				
					// Remove question from the pool
					this.secondaryPool.splice(0, 1);
				}
				break;
				
			case 3:
				if(this.repeatMethod == RepeatMethodEnum.INFINITE || this.repeatMethod == RepeatMethodEnum.UNTIL_KNOWN) {
				
					// Push question to the back of the pool
					this.tertiaryPool.push(this.tertiaryPool[0]);
					this.tertiaryPool.splice(0, 1);
				} else if(this.repeatMethod == RepeatMethodEnum.PIT) {
					
					// Advance question to the previous pool
					this.secondaryPool.push(this.tertiaryPool[0]);
					this.tertiaryPool.splice(0, 1);
				} else {
				
					// Remove question from the pool
					this.tertiaryPool.splice(0, 1);
				}
				break;
				
			case 4:
				
				if(this.repeatMethod == RepeatMethodEnum.PIT) {
					
					// Advance question to the previous pool
					this.tertiaryPool.push(this.quaternaryPool[0]);
					this.quaternaryPool.splice(0, 1);
				} else {
				
					// Remove question from the pool
					this.quaternaryPool.splice(0, 1);
				}
				
				break;
		}
	}
	
	this.HandleCorrectAnswer = function LearningEngine$HandleCorrectAnswer() {
	
		// Declare variables
		
		this.iQuestionCount++;
		
		// Work on the right pool
		switch(this.iCurrentPool) {
			
			case 1:
				if(this.repeatMethod == RepeatMethodEnum.INFINITE) {
				
					// Push question to the back of the pool
					this.primaryPool.push(this.primaryPool[0]);
					this.primaryPool.splice(0, 1);
				} else if(this.repeatMethod == RepeatMethodEnum.PIT) {
					
					// Advance question to the last pool
					this.secondaryPool.push(this.primaryPool[0]);
					this.primaryPool.splice(0, 1);
				} else {
				
					// Remove question from the pool and park it in the quinary pool
					this.quinaryPool.push(this.primaryPool[0]);
					this.primaryPool.splice(0, 1);
				}
				break;
			
			case 2:
				if(this.repeatMethod == RepeatMethodEnum.INFINITE) {
				
					// Push question to the back of the pool
					this.secondaryPool.push(this.secondaryPool[0]);
					this.secondaryPool.splice(0, 1);
				} else if(this.repeatMethod == RepeatMethodEnum.PIT) {
					
					// Advance question to the last pool
					this.tertiaryPool.push(this.secondaryPool[0]);
					this.secondaryPool.splice(0, 1);
				} else {
				
					// Remove question from the pool and park it in the quinary pool
					this.quinaryPool.push(this.secondaryPool[0]);
					this.secondaryPool.splice(0, 1);
				}
				break;
				
			case 3:
				if(this.repeatMethod == RepeatMethodEnum.INFINITE) {
				
					// Push question to the back of the pool
					this.tertiaryPool.push(this.tertiaryPool[0]);
					this.tertiaryPool.splice(0, 1);
				} else if(this.repeatMethod == RepeatMethodEnum.PIT) {
					
					// Advance question to the last pool
					this.quaternaryPool.push(this.tertiaryPool[0]);
					this.tertiaryPool.splice(0, 1);
				} else {
				
					// Remove question from the pool and park it in the quinary pool
					this.quinaryPool.push(this.tertiaryPool[0]);
					this.tertiaryPool.splice(0, 1);
				}
				break;
				
			case 4:
				
				// Remove question from the pool and park it in the quinary pool
				this.quinaryPool.push(this.quaternaryPool[0]);
				this.quaternaryPool.splice(0, 1);
				break;
		}
		
		
	}
	
	
	
	this.Initialize();
}

//LearningEngineQuestion.prototype.
function LearningEngineQuestion() {};
LearningEngineQuestion.prototype.__type = "LearningEngineLogItem";
LearningEngineQuestion.prototype.strQuestion = null;
LearningEngineQuestion.prototype.strUserAnswer = null;
LearningEngineQuestion.prototype.strCorrectAnswer = null;
LearningEngineQuestion.prototype.iCumelativeErrors = 0;
LearningEngineQuestion.prototype.iCumelativeScore = 0;
LearningEngineQuestion.prototype.time = null;

function LearningEngineQuestion() {};
LearningEngineQuestion.prototype.__type = "LearningEngineQuestion";
LearningEngineQuestion.prototype.wordIndex = null;


