/** 
 * @fileoverview Quiz Engine
 * 
 * @author Ethan Poole
 */

/**
 * Quiz engine
 * 
 * @param {Function} callback Final callback upon quiz completition
 */
function quiz(callback)
{
	var self = this;
	
	this.callback = callback;
	this.questions = new Array;
	this.current_question = 1;
	
	// Editable messages
	this.next_question_message = '<p>Click for the next question.</p>';
	
	/**
	 * Add new question to quiz
	 * 
	 * @param {String} question_text
	 * @param {Array} answers
	 * @param {String} answer_file URL of PHP file to check answers
	 * @param {Function} correct_callback Callback upon wrong answer
	 * @param {Function} wrong_callback Callback upon correct answer
	 * @param {Function} before_question_callback Callback before displaying questions
	 * @param {Function} before_answers_callback Callback before displaying answers
	 */
	this.add_question = function(question_text, answers, answer_file, correct_callback, wrong_callback, before_question_callback, before_answers_callback)
	{
		self.questions.push(new question(
			question_text,
			answers,
			answer_file,
			correct_callback,
			wrong_callback,
			before_question_callback,
			before_answers_callback,
			self.next_question // Handler for moving between questions
		));
	}
	
	/**
	 * Commence quiz
	 */	
	this.start_quiz = function()
	{
		bigPhone.updateScreen('');
		smallPhone.updateScreen('');
		
		// Small screen settings
		smallPhone.fadeOut(true);
		$('div#phone-small-screen').show();
		
		// Start with the first question
		self.questions[0].display_question();
	}
	
	/**
	 * Handles moving to the next question or ending quiz
	 */
	this.next_question = function()
	{
		// End of quiz?
		if (self.current_question == self.questions.length)
		{
			if (typeof self.callback == 'function')
			{
				self.callback();
			}
		}
		// Continue to next question
		else
		{
			self.current_question++;
			
			show_direction(self.next_question_message);
			
			run_after_click(function ()
			{
				hide_direction();
				
				$('div#phone-big-screen').children().remove();
				self.questions[self.current_question - 1].display_question();
			});
		}
	}
}

/**
 * Quiz question
 * 
 * @param {String} question_text
 * @param {Array} answers
 * @param {String} answer_file URL of PHP file to check answers
 * @param {Function} correct_callback Callback upon wrong answer
 * @param {Function} wrong_callback Callback upon correct answer
 * @param {Function} before_question_callback Callback before displaying questions
 * @param {Function} before_answers_callback Callback before displaying answers
 * @param {Function} final_callback Callback upon completition of question
 */
function question(question_text, answers, answer_file, correct_callback, wrong_callback, before_question_callback, before_answers_callback, final_callback)
{
	/**
	 * Static question counter (because JS sucks)
	 * 
	 * @returns Value of static counter
	 * @type Number
	 */
	function question_counter()
	{
		if (typeof this.counter == 'undefined')
		{
			this.counter = 0;
		}
		
		return ++this.counter;
	}
	
	var self = this;
	
	this.question_text = question_text;
	this.answers = answers;
	this.answer_file = answer_file;
	
	// Editable messages
	this.progress_message = 'Checking answer...';
	this.answer_preface = 'You answered: ';
	this.try_again_message = '<p>Click to try again.</p>';
	this.error_message = '<p>Oops!  There was an error checking your answer.  Please try again!</p>';
	this.error_message_short = 'Error!';
	
	// Various callback options
	this.wrong_callback = wrong_callback;
	this.correct_callback = correct_callback;
	this.final_callback = final_callback;
	this.before_question_callback = before_question_callback;
	this.before_answers_callback = before_answers_callback;
	
	// Increments for each question
	this.unique_number = question_counter();
	
	// Increments for each attempt by the user
	this.attempts = 0;
	
	/**
	 * Display question in big phone and answers in small phone
	 *
	 * @param {Function} callback Ability to set final callback (useful for when not using the quiz engine)
	 */
	this.display_question = function(callback)
	{
		if (typeof self.final_callback != 'function' && typeof callback == 'function')
		{
			self.final_callback = callback
		}
		
		// Create and append list of answers
		var answer_list = '<form class="answers"><ul>';
		for (x = 0; x < self.answers.length; x++)
		{
			answer_list = answer_list + '<li><input type="radio" name="question_' + self.unique_number + '" id="answer_' + (x + 1) + '" value="' + self.answers[x] + '" /><label for="answer_' + (x + 1) + '">' + self.answers[x] + '</label></li>';
		}
		answer_list = answer_list + '</ul></form>';
		$('div#phone-small-screen').append(answer_list);
		
		// Add question into the DOM
		$('div#phone-big-screen').children().remove();
		$('div#phone-big-screen').append('<div id="phone-big-container"><p class="question">' + self.question_text + '</p></div>');
		
		// Show question
		if (typeof self.before_question_callback == 'function')
		{
			self.before_question_callback(self.unique_number, self.show_question);
		}
		else
		{
			self.show_question();	
		}
	}
	
	/**
	 * Show question (separated for before question callback)
	 */
	this.show_question = function()
	{
		bigPhone.show(function()
		{
			if (typeof self.before_answers_callback == 'function')
			{
				self.before_answers_callback(self.unique_number, self.show_answers);
			}
			else
			{
				self.show_answers();	
			}
		});
	}
	
	/**
	 * Show answers (separated for before answers callback)
	 */
	this.show_answers = function()
	{
		$('div#phone-small-screen form.answers').fadeIn();
		smallPhone.fadeIn();
		$('div#phone-small-screen li input').click(self.check_answer);
	}
	
	/**
	 * Check answer's validity via an AJAX request
	 */
	this.check_answer = function()
	{
		var answer_id = $(this).attr('id');
		var question_id = $(this).attr('name');
		var qtext = self.question_text;
		
		$.ajax({
			type: 'GET',
			url: self.answer_file,
			dataType: 'xml',
			data: { 'answer': answer_id, 'question': question_id, 'qtext': qtext },
			timeout: 8000,
			beforeSend: function()
			{
				// Hide answers and show progress screen
				$('div#phone-small-screen form.answers').hide();
				$('div#phone-small-screen').append('<div class="progress">' + self.progress_message + '</div>');
			},
			success: function(response_xml)
			{
				var correct = $('correct', response_xml).text();
				var message = $('message', response_xml).text();
				
				// Delay the response by a second to avoid choppy progress screen
				setTimeout(function()
				{
					if (correct == 'yes')
					{
						self.return_correct(answer_id, message);
					}
					else
					{
						$('div.progress').remove();
						
						if (typeof self.wrong_callback == 'function')
						{
							self.wrong_callback(answer_id, message, self.return_wrong);
						}
						else
						{
							self.return_wrong(answer_id, message);
						}
					}
				},
				1000);
			},
			error: function(xml_http_request, text_status, error_thrown)
			{
				// Make input reselectable
				$('input#' + answer_id).attr('checked', '');
				
				// Display error screen and message
				$('div.progress').remove();
				$('div#phone-small-screen').append('<div class="error">' + self.error_message_short + '</div>');
				show_direction(self.error_message);
				
				run_after_click(function() {
					hide_direction();
					$('div.error').remove();
					
					$('div#phone-small-screen form.answers').fadeIn();
				});
			}
		});
	}
	
	/**
	 * User answered correctly
	 * 
	 * @param {String} answer_id
	 * @param {String} message Correct answer message
	 */
	this.return_correct = function(answer_id, message)
	{
		// Success sound
		if ((self.unique_number % 2) == 0)
		{
			triggerSound('success');
		}
		else
		{
			triggerSound('success2');
		}
		
		var answer = $('input#' + answer_id).attr('value');
		
		// Fade out small phone
		smallPhone.fadeOut();
		$('div#phone-small-screen').children().remove();
		
		// Display success message in big phone
		$('div#phone-big-screen').children().remove();
		$('div#phone-big-screen').append('<div id="phone-big-container" class="correct_answer"><p class="answer">' + self.answer_preface + answer + '</p><p class="message">' + message + '</p></div>');
		
		if (typeof self.correct_callback == 'function')
		{
			if (typeof self.final_callback == 'function')
			{
				self.correct_callback(self.final_callback);
			}
			else
			{
				self.correct_callback();
			}
		}
		else
		{
			if (typeof self.final_callback == 'function')
			{
				self.final_callback();
			}
		}
	}
	
	/**
	 * User answered incorrectly
	 * 
	 * @param {String} answer_id
	 * @param {String} message Wrong answer message
	 */
	this.return_wrong = function(answer_id, message)
	{
		self.attempts++;
		
		// Failure sound
		if ((self.attempts % 2) == 0)
		{
			triggerSound('fail');
		}
		else
		{
			triggerSound('fail2');
		}
		
		var input = $('input#' + answer_id);
		var answer = $(input).attr('value');
		var label = $(input).siblings('label');
		var list_item = $(input).parent().get(0);
		
		// Disable answer
		$(input).attr('disabled', 'disabled');
		$(input).attr('checked', null);
		$(list_item).addClass('disabled');
		
		// Ghetto fix for all answers being marked wrong.  Feel free to get rid of this if you can find/fix the actual bug.
		if ($('div#phone-small-screen form.answers li.disabled').length == $('div#phone-small-screen form.answers li').length)
		{
			$('div#phone-small-screen').append('<div class="error">' + self.error_message_short + '</div>');
			show_direction(self.error_message_bug);
			
			$('a.refresh').click(function ()
			{
				window.location.reload(true);
			});
		}
		// Message is optional
		else if (!message)
		{
			// Reshow answers
			bigPhone.show();
			$('div#phone-small-screen form.answers').fadeIn();
		}
		else
		{
			$('div#phone-big-container').hide();
			$('div#phone-big-screen').append('<div class="wrong_answer"><p class="answer">' + self.answer_preface + answer + '</p><p class="message">' + message + '</p></div>');
			show_direction(self.try_again_message);
			
			run_after_click(function()
			{
				hide_direction();
				
				// Reshow question
				$('div.wrong_answer').remove();
				$('div#phone-big-container').show();
				bigPhone.show();
				
				// Reshow answers
				$('div#phone-small-screen form.answers').fadeIn();
			});
		}
	}
}

/**
 * Small phone helper class
 * 
 * @author Zachary Johnson
 */
var smallPhone = (function()
{
	/**
	 * @constructor
	 */
	function construct()
	{
		this.faded = false;
		var self = this;
		
		/**
		 * Fade out small phone
		 * 
		 * @param {Boolean} snapToItAlready Skip gradual fade out
		 */
		this.fadeOut = function(snapToItAlready)
		{
			if (!self.faded)
			{
				$('#phone-small').fadeTo((snapToItAlready ? 0 : 1000), 0.3);
				self.faded = true;
			}
		}
		
		/**
		 * Fade in small phone
		 */
		this.fadeIn = function()
		{
			if (self.faded)
			{
				$('#phone-small').fadeTo(1000, 1.0);
				self.faded = false;
			}
		}
		
		/**
		 * Show screen
		 * 
		 * @param {String} contents Content with which to update screen
		 */
		this.showScreen = function(contents)
		{
			$('#phone-small-screen').show();
			self.updateScreen(contents);
		}
		
		/**
		 * Hide screen
		 */
		this.hideScreen = function()
		{
			$('#phone-small-screen').hide();
		}
		
		/**
		 * Update screen contents
		 * 
		 * @param {String} contents Content with which to update screen
		 */
		this.updateScreen = function(contents)
		{
			$('#phone-small-screen').html(contents);
		}
		
		/**
		 * Show all notes
		 */
		this.showNotes = function()
		{
			self.hideScreen();
			$('#phone-small-notes').show();
		}
		
		/**
		 * Hide all notes
		 */
		this.hideNotes = function()
		{
			$('#phone-small-notes').hide();
		}
		
		/**
		 * Show new note
		 */
		this.newNote = function()
		{
			$('#phone-small-notes .new_note').slideDown();
		}
	}
	
	return new construct();
})();

/**
 * Big phone helper class
 * 
 * @author Zachary Johnson
 */
var bigPhone = (function ()
{
	/**
	 * @constructor
	 */
	function construct()
	{
		this.hidden = true;
		var self = this;
		
		/**
		 * Pop up big phone
		 * 
		 * @param {Function} callback
		 */
		this.show = function(callback)
		{
			if (self.hidden)
			{
				$('#phone-big').animate({top: 0}, 1000, 'swing', function ()
				{
					$('#screen-blur').fadeIn(500);
					
					if (typeof callback == 'function')
					{
						callback();
					}
				});
				
				self.hidden = false;
			}
			else
			{
				// Make sure the callback runs even if phone is visible
				if (typeof callback == 'function')
				{
					callback();
				}
			}
		}
		
		/**
		 * Slide down big phone
		 * 
		 * @param {Function} callback
		 */
		this.hide = function(callback)
		{
			if (!self.hidden)
			{
				$('#phone-big').animate({top: 430}, 1000, 'swing', function ()
				{
					$('#screen-blur').fadeOut(500);
					
					if (typeof callback == 'function')
					{
						callback();
					}
				});
				
				self.hidden = true;
			}
			else
			{
				// Make sure the callback runs even if phone is already hidden
				if (typeof callback == 'function')
				{
					callback();
				}
			}
		}
		
		/**
		 * Update big phone screen contents
		 * 
		 * @param {String} contents
		 */
		this.updateScreen = function(contents)
		{
			$('#phone-big-screen').html(contents);
		}
		
		/**
		 * New text message
		 * 
		 * @param {String} contents
		 */
		this.textMsg = function(contents)
		{
			self.updateScreen(contents);
		}
	}
	
	return new construct();
})();