/**
 * @package JProQuiz
 * @author Nick Bartkowiak <nickbart@gmail.com>
 * @copyright Copyright (c) 2008, Nick Bartkowiak
 *
 * A powerful, configurable, piece of quiz software built for the web in Javascript.
 */

/**
 * Main quiz engine.
 */
var Quiz = Class.create(
{
	/**
	 * @var Comprehensive array of objects containing the questions for the quiz.
	 */
	__questions: {},
	
	/**
	 * @var The current question in the question array that is being answered
	 * by the user. We start it's value at -1 so that when the "getNextQuestion"
	 * function is fist called, it will start at 0, the first element in the
	 * questions array.
	 */
	__currentQuestion: -1,
	
	/**
	 * @var The number of questions correctly answered by the user. The gets updated
	 * by this class's __score() function.
	 */
	__correctAnswers: 0,
	
	/**
	 * @var When set to "true," this boolean flag tells the quiz engine to randomize
	 * the order of the questions, rather than present them in the order they are
	 * set in the JSON file.
	 */
	__randomize: true,
	
	/**
	 * When the Quiz object is instantiated, this function populates the global
	 * questions variable with the list of question objects.
	 *
	 * @param boolean randomize Option flag that will turn on or off the quiz
	 * question randomizing
	 * 
	 * @uses Quiz::__questions
	 */
	initialize: function(randomize)
	{
		if (randomize != undefined)
		{
			this.__randomize = randomize;
		}
		
		var quesFile = './inc/quiz.json';
		var _this = this;
		
		new Ajax.Request(quesFile,
		{
			method: 'get',
			onComplete: function(success)
			{
				var jsonString = success.responseText.replace(/[\r\n\t]+/g, "");
				_this.__questions = eval('(' + jsonString + ')');
				$('total').update(_this.__questions.size());
			}
		});
	},
	
	/**
	 * Finalizes the quiz, ends all output, and tallies the results for the user.
	 *
	 * @uses Quiz::__totalScore()
	 * @uses Quiz::__correctAnswers
	 * @uses Quiz::__questions
	 */
	__end: function()
	{
		var score = Math.round((this.__correctAnswers / this.__questions.size()) * 100);
		
		var status = Builder.node('div',
		{
			className: 'end'
		},
		[
			Builder.node('h2', 'Congratulations!'),
			Builder.node('p', 'You have reached the end of the quiz.'),
			Builder.node('p', {className: 'final_score'}, 'You scored ' + this.__correctAnswers + ' out of ' + this.__questions.size() + ' (' + score  + '%)')
		]);
		
		$('main_panel').update(status);
	},
	
	/**
	 * Handy little function that shuffles the questions array. Mimics the
	 * "array_shuffle()" function in PHP.
	 *
	 * @uses Quiz:__questions
	 */
	__randomizeQuestions: function()
	{
		for(var j, x, i = this.__questions.size(); i; j = parseInt(Math.random() * i), x = this.__questions[--i], this.__questions[i] = this.__questions[j], this.__questions[j] = x);
	},
	
	/**
	 * Using the current question as a base, returns the next question in the
	 * questions array.
	 *
	 * @uses Quiz::__questions
	 * @uses Quiz::__currentQuestion
	 *
	 * @return object The next question in the questions array.
	 */
	__getNextQuestion: function()
	{
		// Check to see if the current question is the last question, and if so, end the quiz.
		if (this.__currentQuestion + 1 == this.__questions.size())
		{
			this.__end();
			return false;
		}
		// If it was not the last question, move on the next question.
		else
		{
			return this.__questions[this.__currentQuestion + 1];
		}
	},
	
	/**
	 * Alters the user interface to display the called question.
	 *
	 * @param object The object of the question that is to be displayed.
	 *
	 * @uses Quiz::_answer()
	 */
	__displayQuestion: function(question)
	{
		var listNodes = [];
		
		question.options.each(function(o,i)
		{
			listNodes.push(Builder.node('li',
			{
				className: 'option_item',
				id: o.id
			},
			[
				Builder.node('a', 
				{
					href: 'javascript:void(0)'
				},
					 '(' + o.id  + ') ' + o.text
				)
			]))
		});
		
		var questionContainer = Builder.node('div',
		{
			className: 'question'
		},
		[
			// At this point the current question number is still -1, because we don't increment it
			// until a question is correct, so we need to add 2 to it, so it displays the correct
			// number to the user.
			Builder.node('h2', (this.__currentQuestion + 2)  + '. ' + question.text),
			Builder.node('ul',
			{
				className: 'options_list'
			},
				listNodes
			),
		]);
		
		$('main_panel').update(questionContainer);
		
		$$('li.option_item').each(function(o)
		{	
			var answer = o.identify();
			o.observe('click', function(){ quiz._answer(answer, question) });
		});
	},
	
	/**
	 * Scores each question as correct or incorrect as the user answers them and stores
	 * the result in a global variable for later calculation.
	 * 
	 * @uses Quiz::__correctAnswers
	 */
	__score: function(score)
	{
		this.__correctAnswers = this.__correctAnswers + score;
		$('correct').update(this.__correctAnswers);
	},
	
	/**
	 * Called when a user selects and an answer to a question. It compares the user's
	 * response to the correct answer and notifies the user if they are correct or
	 * not. If correct, it will move the user on to the next question, if not, it will
	 * redisplay the current question.
	 * 
	 * @param string answer The user's selected answer for the current question
	 * @param object question The current question's object, which contains the correct
	 * answer
	 *
	 * @uses Quiz::__currentQuestion
	 * @uses Quiz::__questions
	 * @uses Quiz::__getNextQuestion
	 * @uses Quiz::__displayQuestion
	 */
	_answer: function(answer, question)
	{
		// Increment the current question when the user submits and answer
		this.__currentQuestion = this.__currentQuestion + 1;

		// Alter the user interace to inform the user
		var h2 = (question.answer == answer) ? 'Correct!' : 'Incorrect.';
		
		// Score the user's answer
		if (question.answer == answer)
		{
			this.__score(1);
		}
		
		/* ================================ */
		
		// This little piece of code figures out if the question answered was the last question
		// of the quiz and if so, does not display the "next question" text.
		
		var response = [];
		
		response.push(Builder.node('h2', h2));
		
		if ((this.__currentQuestion + 1) != this.__questions.size())
		{
			response.push(Builder.node('p', 'Next question:'));
		}
		
		/* ================================ */
		
		var status = Builder.node('div',
		{
			className: 'status'
		},
			response
		);
		
		$('result').update(status);
		
		var question = this.__getNextQuestion();
		
		if (question)
		{
			this.__displayQuestion(question);
		}
	},
	
	/**
	 * Starts the quiz engine after initialization. Vroom!
	 *
	 * @uses Quiz::__randomize
	 * @uses Quiz::_randomizeQuestions()
	 * @uses Quiz::_getNextQuestion()
	 * @uses Quiz::_displayQuestion()
	 *
	 * @todo This function could possible be called inside the Quiz engines'
	 * initialization, rather than from user action. This would start
	 * the quiz automatically and display the first question to the user
	 * without the user being prompted. I might end up writing a toggle so,
	 * if desired, the quiz can start automatically.
	 */
	start: function()
	{
		if (this.__randomize == true)
		{
			this.__randomizeQuestions();
		}
		
		var question = this.__getNextQuestion();
		
		if (question)
		{
			this.__score(0);
			this.__displayQuestion(question);
		}
	}
});