var QuizMod = Class.create({
  /**
	 * Instantiate QuizMod instance, configured with the lens module id,
	 * the hash (of the secret key, lens module id, and timestamp) and
	 * the timestamp.
	 */
	initialize: function(id, hash, ts) {
		this.id = id;
		this.nextHash = hash;
		this.nextTs = ts;
		this.questions = {};
		this.index = 0;
		this.answers = {};
    this.test = "foo!";
		this.cb = null;
	},

	/**
	 * @return Reference to element with attribute id equal to name + the
	 * stored lens module id.
	 */
	get: function(name) {
		return $(name + this.id);
	},

	/**
	 * Executes loadScoreCount in the helper, retrieving the number of
	 * questions in this quiz and a count of the number of people who
	 * have taken the quiz.
	 * @param callback A function to execute once data has been loaded
	 */
	loadScoreCount: function() {
		this.request({action: 'loadScoreCount'}, function(json) {
			this.get('loadingScoreCount').innerHTML = json.html;
		}.bind(this));
	},
	
	loadSocialBurst: function() {
		this.request({action: 'loadSocialBurst'}, function(json) {
			this.get('mod_head_right_').innerHTML = json.html;
			this.get('mod_head_right_').toggle();
			this.get('mod_head_right_').addClassName('ss_quiz');
		}.bind(this));
	},

/**
	 * Executes loadFrontEnd in the helper.
	 *
	 * If the module has been properly configured with at least one
	 * question, then the HTML for presenting the quiz is loaded and
	 * the quiz is initiated.  Otherwise, a default message is
	 * loaded and no action is taken.
	 *
	 * This method also triggers loadScoreCount(callback), performing
	 * much of its functionality only after the score count has been
	 * loaded.
	 */
	loadFrontEnd: function() {
		this.viewWait(true);
		this.get('quiz').innerHTML = '';
		this.request({action: 'loadFrontEnd'}, function(json) {
			//this.get('quiz').innerHTML = json.html;
			jQuery('#quiz' + this.id).html(json.html);
      this.viewWait(false);
			if (json.ready) {
				this.questions = json.questions;
				this.get('questionList').setStyle({width: (590*this.countQuestions())+'px'});
        jQuery('#quiz' + this.id).easySlider2();
				this.index = 0;
			}
		}.bind(this));
	},

  /**
	 * Executes averages in the helper, retrieving the HTML to present
	 * a Google Chart with the statistics for this quiz.
	 */
	loadStats: function() {
		this.formWait(true);
		this.request({action: 'averages', bgcolor: 'F7F7F7', width:500, height: 175}, function(json) {
			this.get('stats').innerHTML = json.averages.graphImg;
			this.formWait(false);
		}.bind(this));
	},

	/**
	 * Loads the HTML for presenting the question editing component of the
	 * back-end GUI.
	 */
	loadBackEnd: function(cb) {
		if (cb) {
			this.cb = cb;
		}
		
		this.request({action: 'loadBackEnd'}, function(json) {
			this.get('questions').innerHTML = json.html;

			if (this.cb) {
				this.cb();
			}

			this.questions = json.questions;
			this.get('questionCount').setValue(this.countQuestions());

			this.formWait(false);
			this.loadStats();
			
		}.bind(this));
	},

	/**
	 * @return A count of the questions queued locally
	 */
	countQuestions: function() {
		if (this.questions.length === 0) // only true before request() is called the first time
			return 0;
		else
			return new Hash(this.questions).keys().length;
	},

/**
	 * @return The current time on the client, assumed to be used for
	 * sorting a new question to the end of the list
	 */
	nextSort: function() {
		return this.countQuestions();
	},

	/**
	 * Executes a given action in the PHP helper script.  The
	 * onSuccess handler ensures that the JSON in the response
	 * is properly converted into an object.  This object is
	 * then returned to the calling method via the callback
	 * function (parameter #2).
	 *
	 * @param params A hash of values to be passed as parameters
	 * to the helper - the most important being "action," which
	 * tells the helper script which command to execute
	 *
	 * @param callback A function be executed after the request
	 * has been completed successfully.
	 *
	 * @param method The HTTP method to use in the request -
	 * defaults to "get"
	 */
	request: function(params, callback, method) {
		if (!params)
			params = { action: 'default' }
    if (!method)
      method = 'get';

		params.hash = this.nextHash;
		params.ts = this.nextTs;
		params.lens_module_id = this.id;

		new Ajax.Request('/utility/module/quiz', {
			method: method,
			parameters: params,
			onSuccess: function(transport) {
				var json = transport.responseJSON;

				this.nextHash = json.nextHash;
				this.nextTs = json.nextTs;

				if (json.error) {
					this.onError(params.action, transport);
				}
				else if (json.debug) {
					//this.get('quiz').innerHTML = '<pre>'+json.html+'</pre>';
					console.debug(json.html);
				}
				else if (callback) {
					callback(json);
				}

			}.bind(this),
			onFailure: this.onError.bind(this, params.action)
		});
	},

	/**
	 * Toggles the spinner icon hiding in an element id'd "loadingView"
	 * @param show When true, display the spinner; otherwise, hide it.
	 */
	viewWait: function(show) {
		(show ? this.get('loadingView').show() : this.get('loadingView').hide());
	},

	/**
	 * Toggles the spinner icon hiding in an element id'd "loadingForm"
	 * @param show When true, display the spinner; otherwise, hide it.
	 */
	formWait: function(show) {
		(show ? this.get('loadingForm').show() : this.get('loadingForm').hide());
	},

	/**
	 * Alerts an error message, presumambly caught and thrown from the
	 * request(params, callback, method) function.
	 */
	onError: function(action, transport) {
		var json = transport.responseJSON;
		//alert('An error occurred: '+json.message);
	},

	/**
	 * Alerts the transport response text of an AJAX-request gone
	 * horribly wrong.
	 */
	onFailure: function(action, transport) {
		alert(transport.responseText);
	},

	addAnother: function() {
		this.saveQuestion(function() {
			this.get('theQuestion').focus();
		}.bind(this));
	},

	addNewQuestion: function() {
		if (!this.sorting) {
			this.editing = true;
			this.get('addNewQuestion').toggle();
			this.get('editQuestion').toggle();
			this.get('sort').setValue(this.nextSort());
			this.get('theQuestion').focus();
		}
	},

	doneAdding: function() {
		this.scrollToQs();
		this.saveQuestion(function() {
			this.get('editQuestion').toggle();
			this.get('addNewQuestion').toggle();
			this.editing = false;
		}.bind(this));
	},

	removeQuestion: function(id) {
		if (id && confirm('Are you sure you want to remove this question?')) {
			params = {
				action: 'removeQuestion',
				id: id
			};
			this.request(params, function(json) {
				this.get('questions').innerHTML = '<p><b>Question deleted!</b></p>';
				this.loadBackEnd();
			}.bind(this));
		}
	},

	removeQuestions: function() {
		if (confirm('Are you sure you want to remove all questions?')) {
			params = {
				action: 'removeQuestions'
			};
			this.request(params, function(json) {
				this.get('questions').innerHTML = '<p><b>All questions deleted!</b></p>';
				this.loadBackEnd();
			}.bind(this));
		}
	},

	cancelEdit: function() {
		this.clearEditQuestion();
		this.get('editQuestion').toggle();
		this.get('addNewQuestion').toggle();
		this.editing = false;
		this.scrollToQs();
	},
	
	scrollToQs: function() {
      var question_el = jQuery('#questions' + this.id);
      
      var off = question_el.offset();
      var y = off.top - 0;
      
      //el.focus();
      jQuery('html,body').animate({scrollTop: y}, 250);
  },

	reorder: function() {
		this.sorting = true;
		if (this.get("questionEditList") != null) {
			Sortable.create("questionEditList"+this.id);
			this.get('btnReorder').hide();
			this.get('btnAddAnother').hide();
			this.get('btnSaveOrder').show();
			this.get('questionEditList').addClassName('reorder');
		}
	},

	saveOrder: function() {
		if (this.get("questionEditList") != null && !this.savingOrder) {
			this.savingOrder = true;
			this.get("questionEditList").childElements().each(function(li, i) {
				this.questions[li.firstDescendant().getValue()].sort = i;
			}.bind(this));


			var questions = [];
			new Hash(this.questions).keys().each(function(id) {
				questions.push({
					id: id,
					sort: this.questions[id].sort
				});
			}.bind(this));

			params = {
				action: 'saveOrder',
				questions: questions.toJSON()
			}

			this.formWait(true);
			this.request(params, function(json) {
				Sortable.destroy("questionEditList"+this.id);
				this.get('btnSaveOrder').hide();
				this.get('btnAddAnother').show();
				this.get('btnReorder').show();
				this.sorting = false;
				this.formWait(false);
				this.savingOrder = false;
				this.get('questionEditList').removeClassName('reorder');
			}.bind(this));
		}
	},

	editQuestion: function(id, force) {
		if (force) {
			this.editing = false;
		}
		if (!this.sorting && !this.editing) {
			this.editing = true;
			this.get('questionId').setValue(id);
			this.get('theQuestion').setValue(this.questions[id].the_question);
			this.get('theAnswers[0]').setValue(this.questions[id].answer_0);
			this.get('theAnswers[1]').setValue(this.questions[id].answer_1);
			this.get('theAnswers[2]').setValue(this.questions[id].answer_2);
			this.get('theAnswers[3]').setValue(this.questions[id].answer_3);
			this.get('theAnswers[4]').setValue(this.questions[id].answer_4);
			this.get('theCorrectAnswer').setValue(this.questions[id].correct);
			this.get('sort').setValue(this.questions[id].sort);
			this.get('addNewQuestion').show();
			this.get('editQuestion').show();
			this.get('theQuestion').focus();
		}
	},

	saveQuestion: function(callback) {
		if (!this.get('theQuestion').getValue()) {
			alert('Well come on now, ask a question.');
			return false;
		}
		var atLeastTwo = 0;
		for(i=0; i<5; i++) {
			if (this.get('theAnswers['+i+']').getValue())
				atLeastTwo++;
			if (atLeastTwo == 2)
				break;
		}

		if (atLeastTwo < 2) {
			alert('You have to provide at least two answers, otherwise it\'s not any fun.');
			return false;
		}

		var correct = this.get('theCorrectAnswer').getValue();
		if (!this.get('theAnswers['+correct+']').getValue()) {
			alert('The correct answer must not be blank. Those poor quiz takers of yours, how will they ever pass?');
			return false;
		}

		params = {
			action: 'saveQuestion',
			id: this.get('questionId').getValue(),
			the_question: this.get('theQuestion').getValue(),
			answer_0: this.get('theAnswers[0]').getValue(),
			answer_1: this.get('theAnswers[1]').getValue(),
			answer_2: this.get('theAnswers[2]').getValue(),
			answer_3: this.get('theAnswers[3]').getValue(),
			answer_4: this.get('theAnswers[4]').getValue(),
			correct: this.get('theCorrectAnswer').getValue(),
			sort: this.get('sort').getValue()
		};

		/*
		alert(new Hash(params).toJSON());
		return;
		*/

		this.formWait(true);
		this.request(params, function(json) {
			this.clearEditQuestion();

			this.get('questions').innerHTML = '<p><b>Question saved!</b></p>';
			setTimeout(function() {
				this.loadBackEnd();
			}.bind(this), 2500);

			callback();
		}.bind(this));

		return true;
	},

	clearEditQuestion: function() {
		this.get('questionId').setValue('');
		this.get('theQuestion').setValue('');
		for(i=0; i<5; i++) {
			this.get('theAnswers['+i+']').setValue('');
		}
		this.get('theCorrectAnswer').setValue(0);
		this.get('sort').setValue('');
	},

	getSelectedAnswer: function() {
		var id = this.questions[this.index].id;

		var answer = this.get('quizmod').elements['question'+id];

		for (var i=0; i<answer.length; i++) {
			if (answer[i].checked)
				return answer[i].value;
		}

		return false;
	},

	next: function() {
		if (!this.evaluating) {
			this.evaluating = true;
			var selected = this.getSelectedAnswer();
			if (selected) {
				this.answers[this.questions[this.index].id] = selected;
				jQuery('#questionList' + this.id + ' li.question:first').remove();
				
				this.index++;
			}
			else {
				alert('Please choose an answer.');
			}
			this.evaluating = false;
		}
	},

	finish: function() {
		if (!this.finishing) {
			var selected = this.getSelectedAnswer();
			if (selected) {
				this.answers[this.questions[this.index].id] = selected;
				this.get('loadingFinish').show();

				var params = {
					action: 'finish',
					answers: new Hash(this.answers).toJSON()
				}

				this.finishing = true;
				this.request(params, function(json) {
					jQuery('#quiz' + this.id).html(json.html);
					this.finishing = false;

					this.request({action: 'averages', highlightRange: json.range}, function(json) {
						if (json.averages.total <= 1) {
							this.get('averageScores').innerHTML = "You're the first take the quiz!";
              this.get('graph').innerHTML = '';
            }
						else {
							this.get('averageScores').innerHTML = json.averages.averageScore + '%';
							this.get('graph').innerHTML = json.averages.graphImg;
						}
					}.bind(this));
				}.bind(this));
			}
			else {
				alert('Please choose an answer.');
			}
		}
	},

	resetDomain: function() {
		if (confirm('Are you sure you want to reset the domain?  This will delete all data stored there.')) {
			this.formWait(true);
			this.request({action: 'resetDomain'}, function(json) {
				if (!json.error) {
					alert('Domain reset.');
				}
				else {
					alert(json.message);
				}
				this.formWait(false);
			}.bind(this));
		}
	},

	clearStats: function() {
		if (confirm('Are you sure you want to clear all recorded scores?')) {
			this.formWait(true);
			this.request({action: 'clearStats'}, function(json) {
				if (!json.error) {
					this.loadStats();
					alert('Stats cleared.');
				}
				else {
					alert(json.message);
					this.formWait(false);
				}
			}.bind(this));
		}
	},

	averages: function() {
		this.formWait(true);
		this.request({action: 'averages'}, function(json) {
			if (!json.error) {
				alert(new Hash(json.averages).toJSON());
			}
			else {
				alert(json.message);
			}
			this.formWait(false);
		}.bind(this));
	},

  sendEmail: function() {
		  var elementsBody = '';
			this.get('recommend_errors').innerHTML = '';
			try {
			  var theForm = this.get('recommend_email_form_');

			  $$('#recommend_email_form_' + this.id + ' input[type=text]').each(function(element) {
			    elementsBody += $(element).value + ',';
			  });
			} catch (err) {
			  alert(err);
			}
      var postForm = eval('document.recommend_email_form_' + this.id);
      
      if (!postForm._squidcap_i) {
        alert('Please enter the email address and security word before continuing');
        return false;
      }
      var params = { action: 'email', from: this.get('from').value, recipient_emails: elementsBody, message: this.get('message').value, _squidcap_i: postForm._squidcap_i.value, _squidcap_e: postForm._squidcap_e.value };

      this.request(params, function(json) {
        this.get('recommend_email_').innerHTML = json.html; //'Your email has been sent. Thanks!';
      }.bind(this), 'post');
		},

		addRecipient: function() {
		  var elInsert = this.get('recommend_email_insert_pt_');
		  var elNew = document.createElement('input');
		  var elParent = elInsert.parentNode;
		  elNew.type = 'text';
		  elNew.size = '50';
		  elParent.insertBefore(elNew, elInsert);
		}

});

QuizModFactory = Class.create({
	instances: new Hash(),

	get: function(lens_module_id, hash, ts) {
		if (lens_module_id) {
			var key = "quizmod-"+lens_module_id;
			if (this.instances.keys().indexOf(key) == -1)
				return this.instances.set(key, new QuizMod(lens_module_id, hash, ts));
			else
				return this.instances.get(key);
		}
	}
});

QuizModFactory = Class.create({
	instances: new Hash(),

	get: function(lens_module_id, hash, ts) {
		if (lens_module_id) {
			var key = "quizmod-"+lens_module_id;
			if (this.instances.keys().indexOf(key) == -1)
				return this.instances.set(key, new QuizMod(lens_module_id, hash, ts));
			else
				return this.instances.get(key);
		}
	}
});

quizModFactory = new QuizModFactory();
