// Constructor for Bank objects
function Bank(bankID, title) {
	this.ID = bankID;
	this.title = title;
	this.questions = new Array();
	this.questionCount = 0;

	this.addQuestion = function (questionObj) {
		this.questions[this.questionCount] = questionObj;
		this.questionCount++;
	};

	this.getQuestions = function () { return this.questions; };
	this.getTitle = function () { return this.title; }
	this.used = function () { return this.questionCount > 0 };
	this.getPercentage = Bank_GetPercentage;
}

function Bank_GetPercentage() {
	var result = calculateMarksAndPercentageForQuestionsInArray(this.questions);
	return result.percentage;
}

// Constructor for question objects

function Question(questionID, inputCount, maxMarks, bankID) {
	this.ID = questionID;
	this.answered = false;
	this.inputCount = inputCount;
	this.maxMarks = maxMarks;
	this.marksAwarded = 0;
	this.bankID = bankID;
	this.status = QUESTION_NOT_ATTEMPTED;
	this.flagged = false;
	this.response = null;
	this.questionArrayIndex = 0;

	this.getPracticeReviewText = Question_GetPracticeReviewText;
	this.getExamReviewText = function () { this.answered ? "Answered" : "Not Answered" };
}


function Question_GetPracticeReviewText () { 
	var result;
	result = this.answered ? "Answered : " : "Not Answered"
	switch (this.status) {
		case QUESTION_CORRECT:
			result += "Correct";
			break;
		case QUESTION_INCORRECT:
			result += "Incorrect";
			break;
		case QUESTION_PARTIALLY_CORRECT:
			result += "Partially Correct";
			break;
	}
	return result;
}

// Used by the clock timeout function
var timerAssessmentRef;

// Constructor for assessment objects
function Assessment(assessmentType, title, bankDefinitionArray, maximumTime, numberOfBanksToUse, randomiseBanks, groupQuestionsByBank, submitPage, scorePage, clockDirection, backArrowEnabled, forwardArrowEnabled, statusDisplayEnabled, flaggingEnabled, feedbackEnabled) {
	var i;
	var j;
	var bankID;
	var bankTitle;
	var numberOfQuestionsFromBank;
	var questionArray;
	var bankArray = new Array();
	var testArray = new Array();
	var testArrayIndex = 0;
	var assessmentMaxMarks= 0;
	var assessmentInputCount = 0;

	// First randomise banks if required
	if (randomiseBanks) {
		for (i = 0; i < bankDefinitionArray.length; i++) {
			bankDefinitionArray[i].randomValue = Math.random();
		}
		bankDefinitionArray.sort(sortByRandomValue);
	}

	// now pick the questions from the banks
	for (i = 0; i < numberOfBanksToUse; i++) {
		bankID = bankDefinitionArray[i][0];
		bankTitle = bankDefinitionArray[i][1];
		numberOfQuestionsFromBank = bankDefinitionArray[i][2];
		randomiseQuestionsInBank = bankDefinitionArray[i][3];
		questionArray = bankDefinitionArray[i][4];
		bankArray[i] = new Bank(bankID, bankTitle);

		if (randomiseQuestionsInBank) {
			for (j = 0; j < questionArray.length; j++) {
				questionArray[j].randomValue = Math.random();
			}
			questionArray.sort(sortByRandomValue);
		}

		for (j = 0; j < numberOfQuestionsFromBank; j++) {
			// For evaluate phase, max marks is the same as input count
			if (assessmentType == ASSESSMENT_EVALUATE) {
				questionMaxMarks = questionArray[j][1]; // Input count
			} else {
				questionMaxMarks = questionArray[j][2]; // Max Marks
			}
			testArray[testArrayIndex] = new Question(questionArray[j][0], questionArray[j][1], questionMaxMarks, bankID);
			bankArray[i].addQuestion(testArray[testArrayIndex]);
			assessmentMaxMarks += testArray[testArrayIndex].maxMarks;
			assessmentInputCount += testArray[testArrayIndex].inputCount;
			testArrayIndex++;
		}
	}

	if (groupQuestionsByBank == false) {
		for (i = 0; i < testArray.length; i++) {
			testArray[i].randomValue = Math.random();
		}
		testArray.sort(sortByRandomValue);
	}

	// setup array index data for easier code in UI
	for (i = 0; i < testArray.length; i++) {
		testArray[i].questionArrayIndex = i;
	}

	this.assessmentType = assessmentType;
	// remove (Pracise Mode) or (Exam Mode)
	title = title.replace(" (Practice Mode)", "");
	title = title.replace(" (Exam Mode)", "");
	this.title = title;
	this.questionArray = testArray;
	this.bankArray = bankArray;
	this.questionCount = testArray.length;
	this.inputCount = assessmentInputCount;
	this.maxMarks = assessmentMaxMarks;
	this.marks = 0;
	this.percentage = 0;
	this.submitPage = submitPage;
	this.scorePage = scorePage;
	this.clockDirection = clockDirection;
	this.backArrowEnabled = backArrowEnabled;
	this.forwardArrowEnabled = forwardArrowEnabled;
	this.statusDisplayEnabled = statusDisplayEnabled;
	this.flaggingEnabled = flaggingEnabled;
	this.feedbackEnabled = feedbackEnabled;
	this.atLeastOneQuestionAttempted = false;
	this.pageNumberOffset = 0;
	this.clockRunning = false;
	this.clockFrozen = false;
	this.assessmentStartTime = null; // will be set by startAssessment
	this.totalFrozenTime = 0;
	this.freezeTime = null;
	this.lastTimerFiredTime = null; // used to detect if the user tries to increase time by turning thier clock back
	this.finished = false;

	this.assessmentTotalTime = maximumTime * 60
	this.currentTime = 0; // current time will always be in seconds

	// Member Functions
	this.startAssessment = Assessment_StartAssessment;
	this.finishAssessment = Assessment_FinishAssessment;
	this.getAssessmentTimeInMinutes = Assessment_GetAssessmentTimeInMinutes;
	this.getFormattedTime = Assessment_GetFormattedTime;
	this.getMaxMarks = function () { return this.maxMarks; };
	this.getMarks = function () { return this.marks; };
	this.getPercentage = function () { return this.percentage;};
	this.setPageNumberOffset = Assessment_SetPageNumberOffset;
	this.setCurrentAssessmentPage = Assessment_SetCurrentAssessmentPage;
	this.getNextAssessmentPage = Assessment_GetNextAssessmentPage;
	this.getPreviousAssessmentPage = Assessment_GetPreviousAssessmentPage;
	this.getAssessmentEndPageNumber = Assessment_GetAssessmentEndPageNumber;
	this.getCurrentAssessmentPageNumber = Assessment_GetCurrentAssessmentPageNumber;
	this.getBanks = Assessment_GetBanks;
	this.recordMark = Assessment_RecordMark;
	this.getAssessmentTypeAsString = Assessment_GetAssessmentTypeAsString;
	this.getLastQuestionPage = Assessment_GetLastQuestionPage;
	this.getScorePage = function () { return this.scorePage }
	this.isPractice = function () { return this.assessmentType == ASSESSMENT_PRACTICE ? true : false; };
	this.isExam = function () { return this.assessmentType == ASSESSMENT_EXAM ? true : false; };
	this.isFeedbackEnabled = function () { return this.feedbackEnabled; };
	this.getCurrentQuestion = function () { return this.questionArray[this.currentAssessmentPageIndex]; };
	this.startClock = Assessment_StartClock;
	this.stopClock = Assessment_StopClock;
	this.freezeClock = Assessment_FreezeClock;
	this.unfreezeClock = Assessment_UnfreezeClock;
	this.setTimerFiredCallback = Assessment_SetTimerFiredCallback;
	this.timerFired = Assessment_TimerFired;
	this.setClockCoundownFinishedCallback = Assessment_SetClockCountdownFinishedCallback;
	this.clockCountdownFinished = Assessment_clockCountdownFinished;
	this.setClockTurnedBackCallback = Assessment_SetClockTurnedBackCallback;
	this.clockTurnedBack = Assessment_ClockTurnedBack;
	this.getClockTime = function () { return this.assessmentCurrentTime }
	this.getSummaryData = Assessment_GetSummaryData;

	timerAssessmentRef = this;
}

function Assessment_SetPageNumberOffset(pageNumberOffset) {
	this.pageNumberOffset = pageNumberOffset;
}

function sortByRandomValue(a, b) {
	var x = a.randomValue;
	var y = b.randomValue;

	return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}

function calculateMarksAndPercentageForQuestionsInArray(theArray) {
	var i;
	var currentQuestion;
	var marks = 0;
	var maxMarks = 0;
	var result = new Object();

	for (i = 0; i < theArray.length; i++) {
		currentQuestion = theArray[i];
		if (currentQuestion.status != QUESTION_NOT_ATTEMPTED) {
			marks += currentQuestion.marksAwarded;
		}
		maxMarks += currentQuestion.maxMarks;
	}
	result.marks = marks;
	result.maxMarks = maxMarks;
	result.percentage = Math.round((marks / maxMarks) * 100);

	return result;
}

function testArrayToString(testArray) {
var i;
var question;
var outputStr = ""
	for (i = 0; i < testArray.length; i++) {
		question = testArray[i]
		outputStr += 'QID: ' + question.ID + '\n'
	}

	return outputStr;
}

// Function: Assessment_StartAssessment()
// Parameters:  none
// Returns: nothing
// 
// Description: Initialise assessment variables
//
function Assessment_StartAssessment() {

	this.questionAnsweredCount = 0;
	this.currentAssessmentPageIndex = -1;
	if (DEBUG) { debug("Starting Evaluate phase. Total No Questions=" + this.questionCount +". Max Marks=" + this.maxMarks +"."); } 	

}

function Assessment_FinishAssessment() {
	SCOFind().SCOComputeScore(window, false); // Sets the pass / fail etc.
	var currentTime = new Date();
	var assessmentTime = Math.floor((currentTime.valueOf() - this.assessmentStartTime.valueOf() - this.totalFrozenTime) / 1000);
	SCOFind().SCOSetValue("cmi.core.session_time", SCOFind().convertSecondsToCMITimeSpan(assessmentTime));
	this.finished = true;
}

function Assessment_GetAssessmentTimeInMinutes() {
	return Math.floor(this.assessmentTotalTime / 60);
}

//
// Function: SetCurrentAssessmentPage()
// Parameters:  pageName - name of current assessment page e.g. lo_0001qPage21q
// Returns: nothing
//
// Description: Sets this.currentAssessmentPageIndex to the correct Index depending on the pageName
//		Called from page load function on every page in an assessment 
//

function Assessment_SetCurrentAssessmentPage(pageName)  {
	var i;
	var foundPage = false;
	var question;
	for (i = 0; i < this.questionArray.length; i++) {
		question = this.questionArray[i]
		if (question.ID.toLowerCase() == pageName.toLowerCase()) {
			this.currentAssessmentPageIndex = i;
			if (question.status == QUESTION_NOT_ATTEMPTED) {
				question.status = QUESTION_NOT_ANSWERED;
			}
			foundPage = true;
			break;
		}
	}

	// TODO: Is there a better way of doing this
	if (foundPage == false) {
		this.currentAssessmentPageIndex = -1;
	}
}

function Assessment_GetNextAssessmentPage() {
	// Test we're not going over the end of the array, 
	// to display a message before going to the score page
	// allowing users to go back and change answers if they want
	if (this.currentAssessmentPageIndex == this.questionArray.length - 1) {
		if (this.submitPage == '') { // No submit page, just return score page
			return this.scorePage;
		} else {
			return this.submitPage;
		}
	} else {
		return this.questionArray[this.currentAssessmentPageIndex + 1].ID + '.html';
	}
}

function Assessment_GetPreviousAssessmentPage() {
	// When finished back is always the score page
	if (this.finished) {
		return this.scorePage
	}

	if (this.currentAssessmentPageIndex == 0) {
		return '';
	} else {
		return this.questionArray[this.currentAssessmentPageIndex - 1].ID + '.html';
	}
}

function Assessment_GetAssessmentEndPageNumber()
{
	// Add one for the inital page, and one for the score page
	return this.questionArray.length + 2;
}


function Assessment_GetCurrentAssessmentPageNumber() {
	// add one because index is 0 based
	return this.pageNumberOffset + this.currentAssessmentPageIndex + 1;
}


function Assessment_GetBanks() {
	return this.bankArray;
}

function Assessment_RecordMark(marksAwarded) {
	var question = this.questionArray[this.currentAssessmentPageIndex];
	question.marksAwarded = marksAwarded;
	question.answered = true;
	if (question.marksAwarded == 0) {
		question.status = QUESTION_INCORRECT;
	} else if (question.marksAwarded < question.maxMarks) {
		question.status = QUESTION_PARTIALLY_CORRECT;
	} else {
		question.status = QUESTION_CORRECT;
	}
	this.questionsAttempted = true;

	var result = calculateMarksAndPercentageForQuestionsInArray(this.questionArray)
	this.marks = result.marks; 
	// Record the percentage score to date
	this.percentage = result.percentage;
	this.atLeastOneQuestionAttempted = true;

	SCORecordAssessmentMark(this.percentage);
}


function Assessment_GetAssessmentTypeAsString() {
	switch (this.assessmentType.valueOf()) {
		case ASSESSMENT_PRACTICE:
			return 'practice';
			break;
		case ASSESSMENT_EXAM:
			return 'exam';
			break;
	}
}

// Function: Assessment_GetLastQuestionPage()
// Parameters:  none
// Returns: Last question page the user was on in an assessment
// 
// Description: This function is called from the submitassessment page, and 
//	provides the information needed to go back to the last question page the user was on
//	we can't just use the last page in the array as the user is able to jump around some types 
//	of assessment. If they press the submit button accidentally, this provides a mechanism for
//	for them to jump back.
//

function Assessment_GetLastQuestionPage() {
	return this.questionArray[this.currentAssessmentPageIndex].ID + '.html';
}

function Assessment_SetAssessmentTime(time_in_seconds) {
	this.assessmentTotalTime = time_in_seconds;
}

function Assessment_StartClock() {
	if (this.clockRunning) {
		// clock already started
		return;
	}

	if (this.assessmentStartTime == null) {
		this.assessmentStartTime = new Date();
		this.lastTimerFiredTime = this.assessmentStartTime;
	}
	this.timerID = setInterval(Assessment_TimerFired, 1000);
	this.clockRunning = true;
}

function Assessment_StopClock() {
	if (this.clockRunning == false) {
		// clock not started
		return;
	}
	clearInterval(this.timerID);
	this.clockRunning = false;
}

function Assessment_FreezeClock() {
	if ((this.clockRunning == false) || (this.clockFrozen == true)) {
		return;
	}
	this.freezeTime = new Date();
	this.stopClock();
	this.clockFrozen = true;
}

function Assessment_UnfreezeClock() {
	if (this.clockFrozen == false) {
		return;
	}
	var currentTime = new Date()
	var frozenDuration = Math.floor(currentTime.valueOf() - this.freezeTime.valueOf());
	this.freezeTime = null;
	this.clockFrozen = false;
	this.totalFrozenTime += frozenDuration;
	this.startClock();
}

function Assessment_SetTimerFiredCallback(func) {
	this.timerFiredCallback = func;
}

// This function needs to use timerAssessmentRef rather than 'this'
// as it's called from setInterval
function Assessment_TimerFired() {
	var currentTime = new Date();
	if (currentTime.valueOf() < timerAssessmentRef.lastTimerFiredTime.valueOf()) {
		timerAssessmentRef.clockTurnedBack();
		return;
	} else {
		if (timerAssessmentRef.clockDirection == "down") {
			timerAssessmentRef.assessmentCurrentTime = timerAssessmentRef.assessmentTotalTime - Math.floor((currentTime.valueOf() - timerAssessmentRef.assessmentStartTime.valueOf()) / 1000);
			if (timerAssessmentRef.assessmentCurrentTime < 0) {
				timerAssessmentRef.clockCountdownFinished();
				return;
			}
		} else {
			timerAssessmentRef.assessmentCurrentTime = Math.floor((currentTime.valueOf() - timerAssessmentRef.assessmentStartTime.valueOf() - timerAssessmentRef.totalFrozenTime) / 1000);
		} 
		timerAssessmentRef.timerFiredCallback();
	}
	timerAssessmentRef.lastTimerFiredTime = currentTime;
}

function Assessment_SetClockCountdownFinishedCallback(func) {
	this.clockCountdownFinishedCallback = func;
}

function Assessment_clockCountdownFinished() {
	this.stopClock();
	this.clockCountdownFinishedCallback();
}

function Assessment_SetClockTurnedBackCallback(func) {
	this.clockTurnedBackCallback = func;
}

function Assessment_ClockTurnedBack() {
	this.stopClock();
	this.clockTurnedBackCallback();
}

function Assessment_GetSummaryData() {
	var question;
	var result = new Object();
	result.questionCount = this.questionCount;
	result.maxMarks = this.maxMarks;
	result.skippedCount = 0;
	result.skippedMarks = 0;
	result.answeredCount = 0;
	result.answeredMarks = 0;
	for (var i = 0; i < this.questionArray.length; i++) {
		question = this.questionArray[i];
		if (question.answered == false) {
			result.skippedCount++;
			result.skippedMarks += question.maxMarks;
		} else {
			result.answeredCount++;
			result.answeredMarks += question.marksAwarded;
		}
	}

	result.correctMarks = result.answeredMarks;
	result.incorrectMarks = this.maxMarks - result.answeredMarks;

	return result;
}

function Assessment_GetFormattedTime()
{
  var formatted_time = '';
  var hours = new Number;
  var mins = new Number;
  var timetoformat = new Number;

  timetoformat = this.assessmentTotalTime;

  if (timetoformat == null)
      return ("0 mins");

  hours = Math.floor(timetoformat/3600);
  mins = Math.round((timetoformat - (hours * 3600))/60);

  if (hours > 0 )
  {
      formatted_time = hours + " hour";
			if (hours > 1) { formatted_time += "s" }
			formatted_time += " ";
  }
  
	if (mins > 0) {
		formatted_time += mins + " minute";
		if (mins > 1) { formatted_time += "s" };
	}

  return(formatted_time);
}

