/*
//jQuizMe 1.0 First release 
//Documenation at www.bateru.com/jquery/jquizme/
//Please give me feedback at blarry@bateru.com
//Copyright (c) 2009 Larry Battle 3/6/2009
//Dual licensed under the MIT and GPL licenses.
//http://www.opensource.org/licenses/mit-license.php
//http://www.gnu.org/licenses/gpl.html
// quizData =[ { ques: "", ans : ""},  {ques: "", ans : ""}, etc ];
// quiz( quizData, options ) # wordsList is an arrary of objects with the following format, 
*/
// Sees if an array1 and array2 are the same.
Array.prototype.compare = function(testArr) {
    if (this.length != testArr.length){ return false; }
	var i = this.length;	
	while( i-- ) {
        if (this[i].compare) { 
            if (!this[i].compare(testArr[i])){ return false;}
        }
        if (this[i] !== testArr[i]){ return false;}
    }
    return true;
};

(function($){
$.fn.jQuizMeDefaultLayOut = $("<div/>").attr("class","quiz-el").append( 
		$("<div/>").attr("class","q-header").append(
				$("<div/>").attr("class","q-title"),
				$("<div/>").attr("class","q-counter")
			),
		$("<div/>").attr("class","q-help").append( 
				$("<div/>").attr( "class", "q-help-menu" ).append(
						$("<input type='button'/>").attr( { "class" : "help-btn", "title" : " Click to open & close", "value" : "Help"}),									
						$( "<input type = 'button' />" ).attr( { "class" : "quit-btn", "title" : "End quit", "value" : "Quit"}),									
						$("<div/>").attr( "class", "q-help-info" ).hide() 
				)
		),
		$("<div/>").attr("class", "q-intro" ).hide()	,
		$("<div/>").attr("class", "q-prob" ).append( 
			$("<div/>").attr("class","q-ques"),
			$("<div/>").attr("class","q-ans"),
			$("<div/>" ).attr( "class", "q-result" ).hide() 
		),
		$("<div/>").attr( "class", "q-gameOver" ).append(
			$("<div/>").attr("class","stat"),
			$("<div/>").attr("class","options").append( 
				$("<input type='button'/>").attr( { "class" : "restartBtn", "value": "Restart" } ),
				$("<input type='button'/>").attr( { "class" : "deleteBtn", "value": "Delete" } ) 
			)
		).hide()
	);
/*Everything is defined then called. So the starting functions are at the end.*/			
$.fn.jQuizMe = function( wordList, options ){

	var settings = jQuery.extend( {}, {
		addToEnding: " = ?",
		activeClass: "q-ol-active",
		numOfQuizQues: 0,
		help: 'None', 
		hoverClass: "q-ol-hover",
		intro : '',
		multiplyChoiceLength : 3,
		showWrongAns : false,
		random: true,
		right : 'Great Job. Right!',
		quizType : 'fillInTheBlank', 
		title: 'jQuizMe'
	}, options );
	var isMSIE = $.browser.msie;

	return this.each( function(){
		/* currQuiz is the output file.(this is what the user sees)*/
		var currQuiz = 0, currIndex = 0, stickyEl = this, quit = false, userAns = '';
		var wList = wordList || 0;
		var wListLen = wList.length;	
		var numOfQQ = settings.numOfQuizQues;
		if( settings.multiplyChoiceLength > wListLen ){ settings.multiplyChoiceLength = wListLen;}
		if( !numOfQQ || numOfQQ > wListLen ){ numOfQQ = wListLen; }
		/*The q object is where the quiz data is stored. Everything is made before the quiz starts.*/
		var q = {
			ans: [], ansSel: [], ques: []
		};
		var stats = { 
			numOfQues: numOfQQ,
			quesTried: 0,
			/* rightAns[] and wrongAns[] keeps an index of the q's questions and answer index. So at the end the user can review their choses. */
			rightAns: [], 
			wrongAns : [],
			perc : function(){ 
				var x = Math.round( this.rightAns.length / this.numOfQues * 10000 );
				return ( x / 100); 
			},
			reset : function(){ 
				this.numOfQues = numOfQQ;
				this.quesTried = 0;
				this.rightAns = [];
				this.wrongAns = [];
			}
		};
		/* Just for no confusion: r is for random. */
		var rNum = function( len ){
			return Math.floor( Math.random() * len );
		};
		var rBool = function(){
			return ( rNum( 100 ) % 2 !== 0 );
		};
		var rAns = function( len ){
			return wList[ rNum( len || wListLen ) ].ans;
		};
		var makeArrayRandom = function( arr ){
			var j, x, i = arr.length;
			while( i ){
				j = parseInt(Math.random() * i);
				x = arr[--i]; 
				arr[i] = arr[j];
				arr[j] = x;
			}
			return arr;
		};		
		var createQuizEl = function(){
		/* Rememeber currQuiz is shown to the user, so hide until ready*/
			currQuiz = $.fn.jQuizMeDefaultLayOut.clone( true ).hide();	
			$( ".help-btn", currQuiz).toggle( 
				function(){ $( ".q-help-info", currQuiz).fadeIn( "slow" );}
				, function(){ $( ".q-help-info", currQuiz).fadeOut( "slow" );} 
			);
			$( ".quit-btn", currQuiz).click( function(){ quitQuiz(); }); 
			$( stickyEl ).append( currQuiz );	
		};
		var cont = function(){ 
			return ( !quit && ( currIndex < numOfQQ ) );
		};
		var display = function(){
			$( currQuiz ).show();
		};		
		var gameOver = function(){ 
			$( ".stat", currQuiz).text( 
				(stats.perc() + '% were right.')
			);
			$( ".restartBtn", currQuiz).one( "click", function(){
				reStartQuiz();
			});
			$( ".deleteBtn", currQuiz).one( "click", function(){
				deleteQuiz();
			});
			$( ".q-prob, .q-intro", currQuiz).hide();
			$( ".q-gameOver", currQuiz).show();
		};
		var changeProb = function(){
			$( ".q-counter", currQuiz).text( (currIndex + 1) + '/' + numOfQQ );
			$( ".q-ques", currQuiz).html( q.ques[ currIndex ] );
			$( ".q-ans", currQuiz).html( 
			/* Checks to see if one answer selection (ansSel) element was created. This was done to save space and time in making quizTypes. */
				( q.ansSel[ currIndex ] ) ? q.ansSel[ currIndex ].clone(true) : q.ansSel[0].eq(0).clone(true) 
			);
			$( ".q-result", currQuiz ).hide();
			$( ".focusThis", currQuiz ).focus();
		};	
		var changeQuizInfo = function(){
			$( ".q-title", currQuiz).text( settings.title );
			$( ".q-help-info", currQuiz).html( settings.help );
			if( settings.intro ){
				$( ".q-prob", currQuiz ).hide();
				$( ".q-intro", currQuiz ).html( settings.intro ).show()
				.append( $( "<br/>" ) )
				.append( $( '<input type="button"/>' ).attr( "value", "Begin Quiz" )
					.click( function(){
						$( ".q-intro", currQuiz ).hide();
						$( ".q-prob", currQuiz ).show();
					})
				);
			}
		};
		var nextMove = function(){
			currIndex++;
			cont() ? changeProb() : gameOver();
		};
		var cAns = function( i ){ 
			return q.ans[ i || currIndex ]; 
		};		
		var checkAns = function(){
			var ans = cAns(), isAnsCorr = false;
			if( typeof ans == "object" ){
				var i = ans.length;	
				while( i-- ){
					if( userAns == ans[i] ){
						isAnsCorr = true;
					}
				}
				if( userAns == ans.toString() ){ isAnsCorr = true; }
			}
			else{
				isAnsCorr = ( userAns == ans || userAns.toString().toLowerCase() == ans.toString().toLowerCase() );
			}
			stats.quesTried++;
			( isAnsCorr ) ? stats.rightAns.push( currIndex ) : stats.wrongAns.push( currIndex );
			return isAnsCorr;
		};
		var ansResult = function(){
			var currAns = cAns();
			var isUserAnsCorr = checkAns();
			var isLongArr = (typeof currAns == "object") && (currAns.length != 1);
			var show = ( isUserAnsCorr ) ? ( settings.right ) :
							( 'Correct answer(s)<br/>' + (( isLongArr ) ? currAns.join( '<br/>' ) : currAns) );						
			if( !isUserAnsCorr && settings.showWrongAns ){ show = "Your Answer: <br/>" + userAns + "<br/>" + show; }
			
			$( ".q-result", currQuiz ).html( show ).fadeIn( "slow" );
		};
		var hasAnsSel = function( i ){
			return ( !!wList[ i || currIndex ].ansSel );
		};
		/*The quizes are made this way. The q object is filled with the questions, answers and answer selections(what the user can choose from). 
		The user's input gets assigned to userAns ( global inside quizMe ). 
		Then nextMove() is called. Which checks to see if the user's Input is contained in the answer, or is the array.*/
		var quizTypes = {
			'fillInTheBlank' : function( fCVer ){
				/* fillInTheBlank Quiz:	ques = ques,	ansSel = Input text[ ans ].*/
				var makeQuesAndAns = function(){
					var d;
					if( fCVer ){
						d = $( '<div><input type="button" class="check-btn focusThis" value="Check"/></div>' );
						$( ".check-btn", d ).one( "click", function(){ 					
							userAns = "";
							stats.rightAns.push( currIndex ); // The user is review. So they get points.
							$( this ).attr("value", "Next").one( "click", function(){ 
								nextMove(); 
							});
							ansResult();
						});
					}
					else{
						d = $( '<div><input type="text" class="q-quesInput focusThis"/><br/><input type="button" class="check-btn" value="Check"/></div>' );
						$( ".check-btn", d ).one( "click", function(){ 					
							userAns = $( ".q-quesInput", currQuiz ).attr("disabled", "true").val();
							userAns = jQuery.trim( userAns );
							$( this ).attr("value", "Next").one( "click", function(){ 
								nextMove(); 
							});
							ansResult();
						});
					}
					q.ansSel[ 0 ] = d.clone( true );	/* If you only set one ansSel, it will be used through the quiz.*/
					var i = numOfQQ;
					while( i-- ){
						q.ans[ i ] = wList[ i ].ans;
						q.ques[ i ] = ( wList[ i ].ques + settings.addToEnding );
					}
				};
				makeQuesAndAns();
			},
			'flashCard' : function(){
				/*flashCard Quiz:  ques = ques, ansSel =null .*/
				this.fillInTheBlank(true);
			},			
			'trueOrFalse' : function(){
				/* trueOrFalse Quiz:
				ques  = (typeof ans != bool) ? (real or fake ans)  : ques;
				ansSel =  T / F ( radio );
				If the answer is a bool, then there is no creation of a question, 
				by combining a right or wrong answers, to make a true or false statement.
				*/
				var makeQuesAndAns = function(){
					var d = $( '<div><input type="radio" value="1" class="true-radio"/><label>True</label><input type="radio" value="0" class="false-radio"/><label>False</label><br/><input class="check-btn" type="button" value="Check"/></div>' );
					$( ".true-radio, .false-radio, label", d).click( function(){
						$( ".check-btn", currQuiz).attr( "disabled", false );
					});
					$("label:eq(0), .true-radio", d).click( function(){
						$( ".true-radio", currQuiz ).attr( "checked", true ); 
						$( ".false-radio", currQuiz ).attr( "checked", false ); 
						userAns = true;
					});
					$("label:eq(1), .false-radio", d).click( function(){
						$( ".true-radio", currQuiz ).attr( "checked", false ); 
						$( ".false-radio", currQuiz ).attr( "checked", true ); 
						userAns = false;
					});
					$( ".check-btn", d ).attr( "disabled", true ).one( "click", function(){ 
						$( this ).attr("value", "Next").one( "click", function(){ 
							nextMove(); 
						});
						ansResult();
					});
					q.ansSel[ 0 ] = d.clone( true );	/* If you only set one ansSel, it will be reused through the quiz.*/
					
					var i = numOfQQ;
					while( i-- ){
						var currAns = wList[ i ].ans;						
						q.ques[ i ] = wList[ i ].ques;
						
						if( typeof currAns != "boolean" ){
							q.ans[ i ] = rBool();
							var result = ( q.ans[ i ] ) ? currAns : rAns();
							var a = (typeof currAns != "object" || typeof result != "object"  )? result == currAns : result.compare( currAns );
							if( a ){ 
								q.ans[ i ] = true;
							}
							q.ques[ i ] += " <br/>=<br/> " + result;
						}
						else{
							q.ans[ i ] = currAns;
						}
					}
				};
				makeQuesAndAns();
			},
			'multiplyChoice' : function( olVer ){
				/* multiplyChoice Quiz: 
				ques = ques;
				ansSel = <select>[ <option> is the answer + <option> * settings.multiplyChoiceLength]*/
				var makeQuesAndAns = function(){
					//var oV = olVer; // faster. Because
					var d;
					if( olVer ){
						d = $( '<div><ol class="q-ol"></ol><input type="button" class="check-btn" value="Check"/></div>' );
						$( ".q-ol-li", d )
							.live( "click", function( e ){
								$( ".check-btn", currQuiz).attr( "disabled", false );
								var val = parseInt( $( e.target ).attr( "val" ), 10 );
								/* If the user's answer(userAns) is array, only one of the elements is needed for checkAns to return true.*/
								userAns = ( isNaN( val ) ) ? $( e.target ).text() :
											( typeof wList[ val ].ans == "object") ? wList[ val ].ans[0] :
												wList[ val ].ans;
								$( "." + settings.activeClass, currQuiz ).removeClass( settings.activeClass );
								$( e.target ).addClass( settings.activeClass );
							})
							.live( "mouseover", function( e ){ 
								$( e.target ).addClass( settings.hoverClass );
							})
							.live( "mouseout", function( e ){
								$( e.target ).removeClass( settings.hoverClass );
							});
					}
					else{
						d = $( '<div><select class="q-select"></select><input type="button" class="check-btn" value="Check"/></div>' );
						$( ".q-select", d ).bind( "click keypress", function( e ){
							$( ".check-btn", currQuiz).attr( "disabled", false );
							var val = parseInt( $(e.target).val(), 10 );
							/* If the user's answer(userAns) is array, only one of the elements is needed for checkAns to return true.*/
							userAns = ( isNaN( val ) ) ? $( "option:selected", this ).text() : 
										( typeof wList[ val ].ans == "object") ? wList[ val ].ans[0] :
											wList[ val ].ans;
						});
					}
					$( ".check-btn", d ).attr( "disabled", true ).one( "click", function(){ 
						$( this ).attr("value", "Next").one( "click", function(){ 
							nextMove(); 
						});
						ansResult();
					});
					var setLen = settings.multiplyChoiceLength;
					var i = numOfQQ;
					var ansPos, optHtml, val, len;
					var wListRange = [], j = wListLen;
					while( j-- ){
						wListRange[ j ] = j;
					}
					
					while( i-- ){
						optHtml = "";
						if( hasAnsSel( i ) ){
							val = [];
							if( typeof wList[ i ].ansSel == "object" ){ 
								val = wList[ i ].ansSel.concat();
								len = val.length + 1;
							}
							else{
								val[ 0 ] = wList[ i ].ansSel;
								len = 2;
							}
							val[ val.length ] = wList[ i ].ans;
							makeArrayRandom( val );
							
							while( len-- ){
								optHtml += ( olVer ) ? ["<li class = 'q-ol-li' >", val[ len ], "</li> "].join("") :
												[ "<option value='n", i ,"'>", val[ len ], "</option> " ].join("");
							}
						}
						else{
							var randAns = wListRange.concat();
							randAns.splice( i, 1 ); // From the answer Index from randAns. 
							len = setLen;
							ansPos = rNum( len );
							while( len-- ){
								var randIndex = rNum( randAns.length );
								val = ( len == ansPos ) ? i : randAns.splice( randIndex, 1 ) || 0;
								optHtml += ( olVer ) ? ["<li class = 'q-ol-li' val ='", val, "'>", wList[ val ].ans, "</li> "].join("") :
												[ "<option value =' ", val, " '>", wList[ val ].ans, "</option> " ].join("");
							}
						}
						/*below: innerHTML is 2x faster than $.html() but IE generates corrrupt option html*/
						( olVer ) ? $( ".q-ol", d )[0].innerHTML = optHtml :
						( isMSIE ) ? $( "select", d ).html( optHtml ) : $( "select", d )[0].innerHTML = optHtml;
						q.ansSel[ i ] = d.clone( true );
						q.ques[ i ] = wList[ i ].ques;
						q.ans[ i ] = wList[ i ].ans;
					}
				};
				makeQuesAndAns();
			},
			'multiplyChoiceOl' : function(){
				/* multiplyChoiceOlQuiz: ques = ques, ansSel = ol [ <list> real ans + <list> * settings.multipleChooseLength] */
				this.multiplyChoice(true);
			}
		};
		var makeQuizType = function(){ 
				( quizTypes[ settings.quizType ] ) ? quizTypes[ settings.quizType ]() : quizTypes.fillInTheBlank();
		};
		var quitQuiz = function(){ 
			quit = true; 
			nextMove();
		};	
		var createNewQuiz = function(){
			if( settings.random ){ makeArrayRandom( wList ); }
			stats.reset();
			makeQuizType();
			changeQuizInfo();
			changeProb();
			display();
		};
		var deleteQuiz = function(){
			$( currQuiz ).remove();
			q = {};
		};
		var reStartQuiz = function(){
			$( currQuiz ).hide();
			$( ".q-gameOver", currQuiz ).hide();
			$( ".q-prob", currQuiz ).show();
			quit = false;
			currIndex = 0;
			createNewQuiz();
		};
		var start = function( el ){
			if( !el ){ return false; }
			stickyEl = el;
			if( !wListLen ){ 
				currQuiz = $( "<p/>" ).text( "Error: Empty word list." ); 
				display();
				return currQuiz;
			}
			createQuizEl();
			createNewQuiz();
			return currQuiz;
		};		
		start( this );	//This is where it all starts.
	});
};
})(jQuery);
