<?php


/**
 * 
 */
class EfrontTestException extends Exception 
{
	const INVALID_ID          = 801;
	const QUESTION_NOT_EXISTS = 802;
	const TEST_NOT_EXISTS     = 803;
	const NOT_DONE_TEST       = 804;
	const INVALID_LOGIN       = 805;
	const DONE_QUESTION_NOT_EXISTS = 806;
	const INVALID_TYPE        = 807;
}

/**
 * 
 */
class EfrontTest
{
	/**
	 * The test fields
	 * 
	 * @var array
	 * @access public
	 * @since 3.5.0
	 */
	public $test = array();
	
	/**
	 * The content unit representing the test 
	 * 
	 * @var EfrontUnit
	 * @access protected
	 * @since 3.5.0
	 */
	protected $unit = false;
	
	/**
	 * The questions order
	 *
	 * @var array
	 * @access protected
	 * @since 3.5.0
	 */
	protected $questionsOrder = false;
	
	/**
	 * The questions in this test
	 * 
	 * @var array
	 * @access protected
	 * @since 3.5.0
	 */
	protected $questions = false;
	
	/**
	 * Information for done test
	 * 
	 * @var array
	 * @access protected
	 * @since 3.5.0
	 */
	public $doneInfo = false;
	
	/**
	 * Class constructor
	 * 
	 * This function is used to instantiate a test  object.
	 * If an id is used, then the test is instantiate based on
	 * database information. Alternatively, the test array itself
	 * may be provided, thus overriding database query. 
	 * <br/>Example:
	 * <code>
	 * $test   = new EfrontTest(4);							//Instantiate test using test id
	 * 
	 * $result = eF_getTableData("tests", "*", "id=4");	
	 * $test   = new EfrontTest($result[0]);				//Instantiate test using test array
	 * 
	 * $test = new EfrontTest(54, true);					//Instantiate test, only this time specify content id		
	 * </code>
	 * 
	 * @param mixed $test Either a test id, a content id or a test array
	 * @param boolean $isContentId Whether the id specified is actually a content id and not a test id
	 * @since 3.5.0
	 * @access public 
	 */
	function __construct($test, $isContentId = false) {
		if (is_array($test)) {
			$this -> test = $test;
		} elseif (!eF_checkParameter($test, 'id')) {
			throw new EfrontTestException(_INVALIDID.': '.$test, EfrontTestException :: INVALID_ID);
		} else {
		    if ($isContentId){
		        $result = eF_getTableData("tests t, content c", "t.*, c.name", "c.id = t.content_ID and content_ID=".$test);
		    } else {
			    $result = eF_getTableData("tests t, content c", "t.*, c.name", "c.id=t.content_ID and t.id=".$test);
		    }
		    
			if (sizeof($result) == 0) {
				throw new EfrontTestException(_INVALIDID.': '.$test, EfrontTestException :: TEST_NOT_EXISTS);
			} else {
				$this -> test = $result[0];
			}
		}
	}
	
	/**
	 * Delete test
	 * 
	 * This function is used to delete the current
	 * test. All data associated with this test will 
	 * be erased.
	 * <br/>Example:
	 * <code>
	 * </code>
	 * 
	 * @return boolean true if the test was deleted successfully
	 * @since 3.5.0
	 * @access public
	 */
	public function delete() {
		eF_deleteTableData("tests_to_questions", "tests_ID=".$this -> test['id']);
		eF_deleteTableData("done_tests", "tests_ID=".$this -> test['id']);
		eF_deleteTableData("content", "id=".$this -> test['content_ID']);
		eF_deleteTableData("tests", "id=".$this -> test['id']);
		
		return true;
	}

	/**
	 * Activate test
	 * 
	 * This function is used to activate the current test. 
	 * The function also activates the corresponding content
	 * unit, if it is not already activated
	 * <br/>Example:
	 * <code>
	 * $test = new EfrontTest(32);			//Instantiate test object
	 * $test -> activate();					//Activate test
	 * </code>
	 */
	public function activate() {
	    $this -> test['active'] = 1;
	    $this -> persist();
	    
	    $unit = new EfrontUnit($this -> test['content_ID']);
	    if (!$unit['active']) {
	        $unit -> activate();
	    }
	    $this -> unit = new EfrontUnit($this -> test['content_ID']);
	}
	
	/**
	 * Deactivate test
	 * 
	 * This function is used to deactivate the current test. 
	 * The function also deactivates the corresponding content
	 * unit, if it is not already deactivated
	 * <br/>Example:
	 * <code>
	 * $test = new EfrontTest(32);			//Instantiate test object
	 * $test -> deactivate();				//Deactivate test
	 * </code>
	 */
	public function deactivate() {
	    $this -> test['active'] = 0;
	    $this -> persist();
	    
	    $unit = new EfrontUnit($this -> test['content_ID']);
	    if ($unit['active']) {
	        $unit -> deactivate();
	    }
	    $this -> unit = new EfrontUnit($this -> test['content_ID']);
	}	
	
	/**
	 * Persist test changes
	 * 
	 * This function is used to persist changes made to the 
	 * current test object.
	 * <br/>Example:
	 * <code>
	 * $test -> test['duration'] = 100;			//Update test duration
	 * $test -> persist();						//Persist changed value with database. 
	 * </code>
	 * 
	 * @return boolean true if everything is ok
	 * @since 3.5.0
	 * @access public
	 */
	public function persist() {
		$fields = array('active' 			=> $this -> test['active'],
						'content_ID' 		=> $this -> test['content_ID'],
						'duration' 			=> $this -> test['duration'],
						'redoable' 			=> $this -> test['redoable'],
						'onebyone' 			=> $this -> test['onebyone'],
						'answers' 			=> $this -> test['answers'],
						'description' 		=> $this -> test['description'],
						'shuffle_questions' => $this -> test['shuffle_questions'],
						'shuffle_answers'   => $this -> test['shuffle_answers'],
						'given_answers' 	=> $this -> test['given_answers']);
		return eF_updateTableData("tests", $fields, "id=".$this -> test['id']);
		
	}
	
	/**
	 * Get test questions
	 * 
	 * This function returns a list with all the test's questions. If $returnObjects is true, 
	 * then Question objects are returned. 
	 * Note that this function returns the questions in the order specified for the specific test. 
	 * <br/>Example:
	 * <code>
	 * $questions = $this -> getQuestions();
	 * </code>
	 * 
	 * @param boolean $returnObjects Whether to return Question objects
	 * @return array An array of questions
	 * @since 3.5.0
	 * @access public
	 */
	public function getQuestions($returnObjects = false) {
		if ($this -> questions === false) {
			$result = eF_getTableData("tests_to_questions tq, questions q", "q.*, tq.weight, tq.previous_question_ID", "tq.questions_ID=q.id and tq.tests_ID=".$this -> test['id']);
			if (sizeof($result) > 0) {
				foreach ($result as $value) {
				    $value['type_icon']      = Question :: $questionTypesIcons[$value['type']];
					$questions[$value['id']] = $value;
					$previousQuestions[$value['previous_question_ID']] = $value;
				}

				//Sorting algorithm, based on previous_question_ID. the algorithm is copied from EfrontContentTree :: reset() an is the same with the one applied for content
				$node  = 0;
				$count = 0;
				$nodes = array();                                                                          //$count is used to prevent infinite loops
				while (sizeof($previousQuestions) > 0 && isset($previousQuestions[$node]) && $count++ < 1000) {
					$nodes[$previousQuestions[$node]['id']] = $previousQuestions[$node];
					$newNode = $previousQuestions[$node]['id'];
					unset($previousQuestions[$node]);
					$node    = $newNode;
				}
				$this -> questions 		= $nodes;
			} else {
				$this -> questions = array();
			}
		} 

		if ($returnObjects) {
			$questions = array();
			foreach ($this -> questions as $key => $value) {
				$questions[$key] = QuestionFactory :: factory($value); 
			}
			return $questions;
		} else {
			return $this -> questions;
		}
	}
	
	/**
	 * Get potential test questions
	 * 
	 * This function is used to return all questions that could be, but are not
	 * part of this test.
	 * <br/>Example:
	 * <code>
	 * $nonQuestions = $test -> getNonQuestions();
	 * </code>
	 *  
	 * @param boolean $returnObjects Whether to return an array of Question objects 
	 * @return array The questions that could, but don't belong to the test
	 * @since 3.5.0
	 * @access public
	 */
	public function getNonQuestions($returnObjects = false) {
	    $lesson       = $this -> getLesson();

	    $result       = eF_getTableDataFlat("content", "id", "lessons_ID=".key($lesson)." and active=1");	    
	    $result       = eF_getTableData("questions", "*", "content_ID in (".implode(",", $result['id']).") and id not in (".implode(",", (array_keys($this -> getQuestions()))).")");
	    $nonQuestions = array(); 
	    foreach ($result as $value) {
	        $returnObjects ? $nonQuestions[$value['id']] = QuestionFactory :: factory($value) : $nonQuestions[$value['id']] = $value;
	    }
	    
	    return $nonQuestions;
	}

	/**
	 * Assign questions to test
	 * 
	 * This function is used to add questions to the current 
	 * test. The $questions array has question ids as keys
	 * and question weights as values.
	 * <br/>Example:
	 * <code>
	 * $questions = array(54 => 5, 62 => 1, 76 => 1, 85 => 10);			//question with id 54 will have weight 5, id 62 will have weight 1 etc
	 * $test -> addQuestions($questions);
	 * </code>
	 * 
	 * @param $questions The questions list
	 * @return boolean True if everything is ok
	 * @since 3.5.0
	 * @access public 
	 */
	public function addQuestions($questions) {
	    $testQuestions    = $this -> getQuestions();
	    $nonTestQuestions = $this -> getNonQuestions();
	    
	    $previousId = 0;
	    foreach ($questions as $id => $weight) {
	        $fields = array("tests_ID"             => $this -> test['id'],
	                        "questions_ID"         => $id,
	                        "weight"               => $weight,
	                        "previous_question_ID" => $previousId);
	        eF_insertTableData("tests_to_questions", $fields);
	        $previousId = $id;
	    }
	}

	/**
	 * Remove questions from test
	 * 
	 * This function is used to remove questions from the test
	 * The question ids are specified in the $questionIds array.
	 * If this parameter is ommited, all questions are deleted.
	 * <br/>Example:
	 * <code>
	 * $questionIds = array(54, 2, 7);
	 * $test -> $removeQuestions($questionIds);							//Remove questions with ids 54, 2 and 7 from test
	 * $test -> $removeQuestions();										//Remove all questions from test
	 * </code>
	 * 
	 * @param array $questionIds The question ids
	 * @return array The new questions list for the test
	 * @since 3.5.0
	 * @access public
	 */
	public function removeQuestions($questionIds = false) {
	    if ($questionIds === false) {
	        eF_deleteTableData("tests_to_questions", "tests_ID = ".$this -> test['id']);    
		    $this -> questions = false;                //Reset questions information
		    return array();
	    } else {
		    $testQuestions = array_keys($this -> getQuestions());
		    foreach ($questionIds as $id) {
		        if (in_array($id, $testQuestions)) {
		            eF_deleteTableData("tests_to_questions", "tests_ID = ".$this -> test['id']." and questions_ID=$id");
		        }
		    }
		    $this -> questions = false;                //Reset questions information
		    return $this -> getQuestions();            //Return new questions list
	    }
	}
	
	/**
	 * Get the content unit corresponding to this test
	 * <br/>Example:
	 * <code>
	 * $unit = $test -> getUnit();
	 * </code>
	 * 
	 * @return EfrontUnit The unit of this test
	 * @since 3.5.0
	 * @access public
	 */
	public function getUnit() {
		if ($this -> unit === false) {
			$this -> unit = new EfrontUnit($this -> test['content_ID']);
		}
		return $this -> unit;
	}

	/**
	 * Return this test's lesson
	 * 
	 * This function returns the lesson that the current test
	 * belongs to.
	 * <br/>Example:
	 * <code>
	 * $test -> getLesson();			//returns something like array(3 => 'A lesson')
	 * $test -> getLesson(true);		//returns the EfrontLesson object
	 * </code>
	 * 
	 * @param $returnObjects Whether to return a simple id => name array, or the full lesson object
	 * @return mixed Either an array with an $id => $name pair, or an EfrontLesson object
	 * @since 3.5.0
	 * @access public
	 */
	public function getLesson($returnObjects = false) {
	    $result = eF_getTableData("lessons, content", "lessons.id, lessons.name", "lessons.id=content.lessons_ID and content.id=".$this -> test['content_ID']);

	    if ($returnObjects) {
	        $lesson = new EfrontLesson($result[0]['id']);
	    } else {
	        $lesson = array($result[0]['id'] =>  $result[0]['name']);
	    }
	    
	    return $lesson;
	}
	
	/**
	 * Create test
	 * 
	 * This function is used to create a new test.
	 * In order to create the test, it firsts creates 
	 * a unit to hold it.
	 * <br/>Example:
	 * <code>
	 * $contentFields = new array('name' => 'new unit');
	 * $testFields    = new array('duration' => 100);
	 * $test = EfrontTest :: createTest($contentFields, $testFields);
	 * </code>
	 * 
	 * @param array $content The content unit fields or an existing unitObject
	 * @param array $test The test fields
	 * @return EfrontTest the new test object
	 * @since 3.5.0
	 * @access public
	 * @static
	 */
	public static function createTest($content, $test) {
	    if (! ($content instanceof EfrontUnit)){
    		$unit = EfrontUnit :: createUnit($content);
    	}
    	else{
            $unit = $content;
        }
		$test['content_ID'] = $unit['id'];
		unset($test['id']);
		unset($test['name']);
		if ($newId = eF_insertTableData("tests", $test)) {
			return new EfrontTest($newId); 
		} else {
			return false;
		}		
	}

	/**
	 * Get the question weight
	 * 
	 * This function returns the weighted factor for the specified
	 * question in this test context. For example, If there are 4
	 * questions in the test, all with the same weight (for example 1),
	 * then the function retunrs 0.25
	 * <br/>Example:
	 * <code>
	 * $test -> getQuestionWeight(6);				//Rreturns the weight factor for this question, which is a number between 0 and 1 (excluding 0) 
	 * </code>
	 * 
	 * @param int $questionId The question id
	 * @return float The question weight
	 * @since 3.5.0
	 * @access public
	 */
	public function getQuestionWeight($questionId) {
	   $testQuestions = $this -> getQuestions();
	   if (!in_array($questionId, array_keys($testQuestions))) {
	       throw new EfrontTestException(_INVALIDID.': '.$questionId, EfrontTestException :: INVALID_ID);
	   }
	   
	   foreach ($testQuestions as $id => $question) {
	       $weights[$id] = $question['weight'];
	   }
	   
	   $questionWeight = $weights[$questionId] / array_sum($weights);
	   return $questionWeight;
	}
	
	/**
	 * Get the absolute question weight
	 * 
	 * This function returns the absolute (integer) factor for the specified
	 * question in this test context. 
	 * <br/>Example:
	 * <code>
	 * $test -> getAbsoluteQuestionWeight(6);				//Rreturns the weight factor for this question, which is a number between 0 and 1 (excluding 0) 
	 * </code>
	 * 
	 * @param int $questionId The question id
	 * @return int The question weight
	 * @since 3.5.0
	 * @access public
	 */
	public function getAbsoluteQuestionWeight($questionId) {
	   $testQuestions = $this -> getQuestions();
	   if (!in_array($questionId, array_keys($testQuestions))) {
	       throw new EfrontTestException(_INVALIDID.': '.$questionId, EfrontTestException :: INVALID_ID);
	   }
	   
	   foreach ($testQuestions as $id => $question) {
	       $weights[$id] = $question['weight'];
	   }
	   
	   $questionWeight = $weights[$questionId];
	   return $questionWeight;
	}
	
	
	/**
	 * Get done questions
	 * 
	 * This function is used to get the done questions results for the
	 * current test. The test should be already have its done information,
	 * using setDone().
	 * <br/>Example:
	 * <code>
	 * $test = new EfrontTest(1);								//Instantiate test object
	 * $test -> setDone('jdoe');								//Get the done information for user 'jdoe';
	 * $doneQuestions = $test -> getDoneQuestions();			//Get done questions info
	 * $doneQuestions = $test -> getDoneQuestions(true);		//Get done questions info, returning Question objects
	 * </code>
	 * 
	 * @param string $returnObjects Whether to return question objects
	 * @return array The done questions
	 * @since 3.5.0
	 * @access public
	 * @see setDone()
	 */
	public function getDoneQuestions($returnObjects = false) {
	    if ($this -> doneInfo === false) {
	        throw new EfrontTestException(_TESTISNOTDONE, EfrontTestException :: NOT_DONE_TEST); 
	    }
	    $result = eF_getTableData("done_questions dq, questions q", "q.*, dq.answer as done_answer, dq.score", "q.id=dq.questions_ID and dq.done_tests_ID=".$this -> doneInfo['id']);

	    foreach ($result as $value) {
	        unserialize($value['done_answer']) !== false ? $value['done_answer'] = unserialize($value['done_answer']) : null;
	        if ($returnObjects) {
	            $questions[$value['id']] = QuestionFactory :: factory($value);
	            $questions[$value['id']] -> setDone($value['done_answer'], $value['score'], $this -> doneInfo['answers_order'][$value['id']]);
	        } else {
	            $questions[$value['id']] = $value;
	        }
	    }
	    
	    if ($this -> questionsOrder) {							//Rearrange questions according to original order
		    foreach ($this -> questionsOrder as $value) {
		    	$temp[$value] = $questions[$value];
		    }
	    	return $temp;
	    } else {
	    	return $questions;
	    }
	}

	/**
	 * Set test done information
	 * 
	 * If this test is done by some student, say 'jdoe', using this function
	 * the relevant information is retrieved and stored at the $doneInfo class
	 * member.
	 * <br/>Example:
	 * <code>
	 * $test     = new EfrontTest(1);					//Instantiate test object
	 * $doneInfo = $test -> setDone('jdoe');			//Retrieve the done test information for user 'jdoe';
	 * </code>  
	 * 
	 * @param mixed $user The user login to get information for, or an EfrontUser object
	 * @return array The done information
	 * @since 3.5.0
	 * @access public
	 */
	public function setDone($user) {
	    if ($user instanceof EfrontUser) {
	        $login = $user -> user['login'];
	    } elseif (!eF_checkParameter($user, 'login')) {
	        throw new EfrontTestException(_INVALIDLOGIN.': '.$user, EfrontTestException :: INVALID_LOGIN);
	    } else {
	        $login = $user;
	    }

	    $result = eF_getTableData("done_tests dt, users_to_done_tests udt, users u", "dt.*, u.name as user_name, u.surname as user_surname, udt.times, udt.answers_order, udt.questions_order", "u.login = udt.users_LOGIN and udt.users_LOGIN=dt.users_LOGIN and dt.users_LOGIN = '$login' and dt.tests_ID = udt.tests_ID and dt.tests_ID=".$this -> test['id']."");
	    
	    if (sizeof($result) > 0) {                  //Get the done information for this test
	        $this -> doneInfo = $result[0];
	        $this -> doneInfo['score'] = round(100 * ($this -> doneInfo['score']), 2) / 100;
	        unserialize($this -> doneInfo['answers_order'])   !== false ? $this -> doneInfo['answers_order']   = unserialize($this -> doneInfo['answers_order'])   : null; 
	        unserialize($this -> doneInfo['questions_order']) !== false ? $this -> doneInfo['questions_order'] = unserialize($this -> doneInfo['questions_order']) : null;

	        $result         = eF_getTableDataFlat("done_questions", "distinct questions_ID", "score = -1 and done_tests_ID=".$this -> doneInfo['id']);
	        if (sizeof($result) > 0) {
		        foreach ($result['questions_ID'] as $id) {
		            $potentialScore += $this -> getQuestionWeight($id);
		        }
		        $this -> doneInfo['potential_score'] = round(100 * ($this -> doneInfo['score'] + $potentialScore), 2) / 100;
	        }
	    } else {                                    //Otherwise, just find out how many times the user has done this test, which is an information kept always (even if a test is reset)        
	        $result = eF_getTableData("users_to_done_tests", "times", "users_LOGIN = '$login' and tests_ID=".$this -> test['id']);	        
	        if (sizeof($result) > 0) {
	            $this -> doneInfo = $result[0];
	        } else {
	            throw new EfrontTestException(_USERHASNOTDONETEST.': '.$login, EfrontTestException :: NOT_DONE_TEST);
	        }
	    }
	    return $this -> doneInfo;
	}

	/**
	 * Correct test
	 * 
	 * This function is used to correct the current test
	 * The $userAnswers variable contains the user's answers
	 * in the test's questions, with keys being the question ids
	 * and values the user answers. Based on them, the test is
	 * corrected, calling correct() on each question.
	 * <br/>Example:
	 * <code>
	 * $test 	= new EfrontTest(3);					//Instantiate new test
	 * $testData = array('login' => 'jdoe', 'duration' => 324, 'times' => 3);
	 * $results = $test -> correct($testData, $userAnswers);		//Correct test based on some user answers
	 * </code>
	 * 
	 * @param array $userAnswers The user answers array, where keys are question ids and values are the answers
	 * @return array The test results
	 * @since 3.5.0
	 * @access public
	 */
	public function correct($testData, $userAnswers, $uploadedFiles = array()) {
	    $testQuestions = $this -> getQuestions(true);
   
	    $fields = array('users_LOGIN' => $testData['login'],
	                    'tests_ID'    => $this -> test['id'],
	                    'timestamp'   => time(),
	                    'score'       => 0,
	                    'comments'    => '',
	                    'duration'   => $testData['duration']);
	    eF_deleteTableData("done_tests", "users_LOGIN='".$testData['login']."' and tests_ID=".$this -> test['id']);
	    $doneTestId = eF_insertTableData("done_tests", $fields);

	    $results['overall_score'] = 0;
	    foreach ($testQuestions as $id => $question) {
	        $question                  -> setDone($userAnswers[$id]);
	        $results[$id]               = $question -> correct();
	        if ($question -> question['type'] == 'raw_text') {
	            $results[$id]['test_score'] = 0;
	            if (isset($uploadedFiles[$id])) {
	            	$userAnswers[$id] .= '<file id = "'.$uploadedFiles[$id]['id'].'">';
	            }
	        } else {
    	        $results[$id]['test_score'] = $results[$id]['score'] * $this -> getQuestionWeight($id);
	        }
	        $results['overall_score']  += $results[$id]['test_score'];
	        
	        $fields = array('done_tests_ID' => $doneTestId,
	                        'questions_ID'  => $question -> question['id'],
	                        'answer'        => serialize($userAnswers[$id]),
	                        'score'         => $results[$id]['score'],
	                        'timestamp'     => time());
	        eF_insertTableData("done_questions", $fields);
	    }
	    
	    eF_updateTableData("done_tests", array('score' => $results['overall_score']), "id=$doneTestId");
	    $fields = array('tests_ID'        => $this -> test['id'],
	                    'users_LOGIN'     => $testData['login'],
	                    'times'           => $testData['times'] + 1,
	                    'questions_order' => $testData['questions_order'] ? $testData['questions_order'] : false,
	                    'answers_order'   => $testData['answers_order']   ? $testData['answers_order']   : false);
	    eF_deleteTableData("users_to_done_tests", "users_LOGIN='".$testData['login']."' and tests_ID=".$this -> test['id']);
	    eF_insertTableData("users_to_done_tests", $fields);
	    
	    $this -> setDone($testData['login']);
	    
	    return $results;
	}
	
	/**
	 * Reset done test information 
	 * 
	 * This function is used to reset the done information for the specified
	 * user. 
	 * <br/>Example:
	 * <code>
	 * $test -> reset('jdoe');							//Reset test information for user jdoe
	 * </code>
	 * 
	 * @param mixed $user The user to reset test for, either a user login or an EfrontUser instance
	 * @return boolean true if everything is ok
	 * @since 3.5.0
	 * @access public
	 */
	public function reset($user) {
	    if ($user instanceof EfrontUser) {
	        $login = $user -> user['login'];
	    } elseif (eF_checkParameter($user, 'login')) {
	        $login = $user;
	    } else {
	        throw new EfrontTestException(_INVALIDLOGIN.': '.$user, EfrontTestException :: INVALID_LOGIN);
	    }
	    $doneInfo = $this -> setDone($user);
	    
	    eF_deleteTableData("done_questions", "done_tests_ID=".$doneInfo['id']);
	    eF_deleteTableData("logs", "action = 'tests' AND comments = ".$this -> test['content_ID']." AND users_LOGIN = '".$login."'");
	    eF_deleteTableData("logs", "action = 'test_begin' AND comments = ".$this -> test['id']." AND users_LOGIN = '".$login."'");
	    if (is_dir(G_UPLOADPATH.$login.'/tests/'.$this -> test['id'])) {
	        try {
	            $directory  = new EfrontDirectory(G_UPLOADPATH.$$login.'/tests/'.$this -> test['id'].'/');
	            $directory -> delete();
	        } catch (EfrontFileException $e) {}
	    }
	    $fields_update = array('questions_order' => '',
                               'answers_order'   => '');
	    eF_updateTableData("users_to_done_tests", $fields_update, "tests_ID=".$this -> test['id']." and users_LOGIN='".$login."'");
        eF_deleteTableData("done_tests", "id=".$doneInfo['id']);
        
        //Set the unit as "not seen"
        if (!($user instanceof EfrontUser)) {
            $user = EfrontUserFactory :: factory($login, false, 'student'); 
        }       
        $user -> setSeenUnit($this -> test['content_ID'], key($this -> getLesson()), 0);
        
        
	    return true;
	}
	
	/**
	 * Order test questions
	 * 
	 * This function is used to order randomly (Shuffle) the test questions and return the
	 * order used. If the ordering array is provided, then the questions are
	 * rearranged based on the specified order.
	 * <br/>Example:
	 * <code>
	 * $test = new EfrontTest(1);										//Instantiate test form
	 * $questionsOrder = $test -> orderQuestions();						//Shuffle the test questions and return order
	 * $questionsOrder = $test -> orderQuestions($order);				//Rearrange test questions based on $order, which is an array of question ids
	 * </code>
	 *
	 * @param array $order The order to apply to the questions. If omitted, the questions are ordered randomly
	 * @return array The order applied (useful only when $order is ommitted)
	 * @since 3.5.0
	 * @access protected
	 */
	protected function orderQuestions($order = false) {
		if (!$order) {
		    $order = array_keys($this -> questions);
		    shuffle($order);
		}
		
		foreach ($order as $value) {
			$temp[$value] = $this -> questions[$value];
		}
		$this -> questions 		= $temp;		
		$this -> questionsOrder = array_keys($this -> questions);
		
		return $this -> questionsOrder;
	}
	
	/**
	 * Populate the test form
	 * 
	 * This function is used to populate the test form and create the 
	 * test html code. 
	 * <br/>Example:
	 * <code>
	 * $test = new EfrontTest(1);								//Instantiate test form
	 * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create the test form
	 * echo $test -> toHTMLQuickForm($form);					//Populates the form and returns the equivalent HTML code
	 * echo $test -> toHTMLQuickForm($form, 2);					//Populates the form and returns the equivalent HTML code, but displays only question with id 2
	 * $test -> setDone('jdoe');								//Get the done test information for user 'jdoe';
	 * echo $test -> toHTMLQuickForm($form, false, true);		//Populates the form and returns the equivalent HTML code, but the mode is set to display the done test
	 * </code>
	 * 
	 * @param HTML_QuickForm $form The form to populate
	 * @param int $questionId If set, it displays only the designated question
	 * @param boolean $done If set to true and the test has done information (previously acquired with setDone()), then it displays the done test
	 * @since 3.5.0
	 * @access public
	 */
	public function toHTMLQuickForm(& $form = false, $questionId = false, $done = false) {
		$this -> getQuestions();																//Initialize questions information, it case it isn't
		if (!$form) {
	        $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a sample form 
	    }
	    if ($this -> doneInfo === false) {            //If $doneInfo is not set, always print the unsolved version of the test
	        $done = false;
	    }
                
	    if ($done) {
	    	$this -> doneInfo['questions_order'] ? $this -> orderQuestions($this -> doneInfo['questions_order']) : null;
	    	$testQuestions = $this -> getDoneQuestions(true);
	    } else {
	    	if ($this -> test['shuffle_questions'] && !$form -> isSubmitted()) {
	    		$this -> orderQuestions(); 																	//Shuffle questions if the test setting is set to, but only if the test is not submitted!
	    		$form -> addElement("hidden", "questions_order", serialize($this -> questionsOrder));		//The questions order is hold at a hidden element, so that it can be stored when the test is complete
	    	}
	    	$testQuestions = $this -> getQuestions(true);
	    }

	    $questionId && in_array($questionId, array_keys($testQuestions)) ? $questions[$questionId] = $testQuestions[$questionId] : $questions = $testQuestions;    //If $questionId is specified, keep only this question 

	    $testString = '';
	    $count      = 1;
	    
	    foreach ($questions as $id => $question) {
	        if ($done) {
		        $results = $question -> correct($question -> score);
		        switch ($results['score']) {
		            case '-1': $image = 'unknown.png'; break;	            
		            case ''  : $image = 'delete.png';  break;
		            case '1' : $image = 'checks.png';  break;
		            default  : $image = 'check.png';   break;
		        }
	        } else {
	            $results = false;
	            $this -> test['shuffle_answers'] ? $shuffleOrder[$id] = $question -> shuffle() : null;
	        }
            
	        $weight = round(100 * $this -> getQuestionWeight($question -> question['id']), 2);
	        $weightImageString = '';
/*
	        for ($i = 0; $i < round($weight / 10); $i++) {
	            if ($i < 2) {
	                $icon = "star_grey.png";
	            } else if ($i < 4) {
	                $icon = "star_green.png";
	            } else if ($i < 6) {
	                $icon = "star_blue.png";
	            } else if ($i < 8) {
	                $icon = "star_yellow.png";
	            } else {
	                $icon = "star_red.png";
	            }
	            $weightImageString .= '<img src = "images/16x16/'.$icon.'" style = "vertical-align:middle" alt = "'._WEIGHT.': '.$weight.'" title = "'._WEIGHT.': '.$weight.'">';
	        }
*/

	        $testString .= '
	        		<div id = "question_'.$count.'" onclick = "checkedQuestions['.($count - 1).'] = 1;" style = "'.(!$done && $this -> test['onebyone'] ? 'display:none' : '').'">
	        		<table width = "100%">
        		        <tr><td class = "questionWeight" style = "vertical-align:middle">
        		        		<img src = "images/16x16/'.($done ? $image : 'text.png').'" style = "vertical-align:middle"/>&nbsp;
        		        		<span style = "vertical-align:middle"><b>'._QUESTION.'&nbsp;'. ($count++).'</b>&nbsp;&nbsp;('._WEIGHT.'&nbsp;'.$weight.'%)&nbsp;</span>'.$weightImageString.'
        		        	</td></tr>
        		    </table>'.($done ? $question -> toHTMLSolved($form, $this -> test['answers'], $this -> test['given_answers']) : $question -> toHTML($form)).'<br/></div>';
	        if ($done) {
	            if ($question -> question['type'] == 'raw_text' && $results['score'] == -1) {
		            $testString .= '
	                	<table>
		            		<tr><td>
		            			'._SCORE.': '._THISQUESTIONCORRECTEDPROFESSOR.'
		            		</td></tr>
		            	</table><br/>';
	                
	            } else {
		            $score            = round(100 * $results['score'] * $this -> getQuestionWeight($id), 2);
		            $scoreExplanation = _PERCENTCORRECT.': '.($results['score'] * 100).'% * '._WEIGHT.': '.(round(100 * $this -> getQuestionWeight($id) , 2) / 100).' = '.$score.'%';
		            $testString .= '
		            	<table>
		            		<tr><td>
		            			'._SCORE.': '.$score.'% <img src = "images/16x16/about.png" title = "'.$scoreExplanation.'" alt = "'.$scoreExplanation.'" />
		            		</td></tr>
		            	</table><br/>';
	            }
	        }
	    }
	    if (!$done && $this -> test['onebyone']) {
	    	$testString .= '
	    				<table width = "100%">
                            <tr><td style = "text-align:center;vertical-align:middle;padding-top:50px">
                                <img src = "images/24x24/arrow_left_blue.png"  alt = "'._PREVIOUSQUESTION.'" title = "'._PREVIOUSQUESTION.'" border = "0" id = "previous_question_button" onclick = "showTestQuestion(\'previous\')" style = "vertical-align:middle;margin-right:10px"/>
                                <select id = "goto_question" name = "goto_question" style = "vertical-align:middle" onchange = "showTestQuestion(this.options[this.selectedIndex].value)">';
			for ($i = 1; $i <= sizeof($questions); $i++) {
		    	$testString .= '	
		    						<option value = "'.$i.'">'.$i.'</option>';				
			}			
	    	$testString .= '
                                </select>&nbsp;
                                <img src = "images/24x24/arrow_right_blue.png" alt = "'._NEXTQUESTION.'" title = "'._NEXTQUESTION.'" border = "0" id = "next_question_button" onclick = "showTestQuestion(\'next\')" style = "vertical-align:middle"/>
                            </td></tr>
	    				</table>';
	    	
	    	$testString .= "
	    		    				
	    				<script>
                        <!--
                            var total_questions  = ".sizeof($questions).";
                            var current_question = 1;
                            showTestQuestion(current_question);                            
                            
                            function showTestQuestion(question_num) {
                                if (question_num == 'next') {
                                    current_question < total_questions ? question_num = parseInt(current_question) + 1 : question_num = current_question;
                                } else if (question_num == 'previous') {
                                    current_question > 1 ? question_num = parseInt(current_question) - 1 : question_num = current_question;
                                }
                                
                                $('question_' + current_question).hide();
                                $('question_' + question_num).show();
                                current_question = question_num;
                                current_question <= 1               ? $('previous_question_button').hide() : $('previous_question_button').show();
                                current_question >= total_questions ? $('next_question_button').hide() 	   : $('next_question_button').show();
                                $('goto_question').selectedIndex = current_question - 1;
                            }
                        //-->
                        </script>";
	    }
	    $testString .= "
	    				<script>
                        <!--
	    					var checkedQuestions = new Array(".sizeof($questions).");
                            function checkQuestions() {
                                var unfinished = new Array();
                                var sum = 0;
                                for (var i = 0; i < checkedQuestions.length; i++) {
                                    if (checkedQuestions[i]) {
                                        sum++;
                                    } else {
                                        unfinished.push(i + 1);
                                    }
                                }
                                if (sum < checkedQuestions.length) {
                                    return unfinished;
                                } else {
                                    return false;
                                }
                            }
                        //-->
                        </script>";
                            
	    if ($this -> test['shuffle_questions'] && !$form -> isSubmitted()) {
	    	$form -> addElement("hidden", "answers_order", serialize($shuffleOrder));		//The questions' answers order is hold at a hidden element, so that it can be stored when the test is complete
	    }
	    
	    return $testString;
	}
}

/**
 * MultipleOneQuestion Class
 * 
 * This class is used to manipulate a multiple choice / single answer question
 */
class MultipleOneQuestion extends Question implements iQuestion
{
    
    /**
     * Convert question to HTML_QuickForm
     * 
     * This function is used to convert the question to HTML_QuickForm fields.
     * <br/>Example:
     * <code>
     * $question = new MultipleOneQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> toHTMLQuickForm($form);											//Add fields to form
     * </code>
     * 
     * @param HTML_QuickForm $form The form to add fields to
     * @since 3.5.0
     * @access public
     */
	public function toHTMLQuickForm(&$form) {
	    for ($k = 0; $k < sizeof($this -> options); $k++) {
	        $index      = $this -> order[$k];                                                               //$index is used to reorder question options, in case it was shuffled 
	        $elements[] = $form -> createElement("radio", "question[".$this -> question['id']."]", $this -> options[$index], $this -> options[$index], $index);    //Add a radio for each option
	    }
	    $form -> addGroup($elements, "question[".$this -> question['id']."]", null, "<br/>", false);        //Create a group with the above radio buttons
	}
	
	/**
	 * Create HTML version of unsolved question
	 * 
	 * This function is used to create the HTML code corresponding
	 * to the question. The HTML is created using the question form
	 * fields, so the proper form must be specified. A form renderer 
	 * is used to output the fields. The function calls internally 
	 * toHTMLQuickForm()
	 * <br/>Example:
	 * <code>
     * $question = new MultipleOneQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * echo $question -> toHTML($form);												//Output question HTML code
     * </code> 
     * 
     * @param HTML_QuickForm $form The form to add fields to and display
     * @return string The HTML code for the question
     * @since 3.5.0
     * @access public
	 */
	public function toHTML(&$form) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form 
	    $renderer =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    
	    $form          -> accept($renderer);                                       //Render the form
	    $formArray      = $renderer -> toArray();                                  //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$formArray['question'][$this -> question['id']]['html'].'
	            			</td></tr>
	            	</table>';
	    
        return $questionString;
	}

	/**
	 * Display solved question
	 * 
	 * This function is used to display the solved version of the
	 * question. In order to display it, setDone() must have been
	 * called before.
	 * <br/>Example:
	 * <code>
     * $question = new MultipleOneQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> setDone($answer, $score, $order);								//Set question to be done
     * echo $question -> toHTMLSolved($form);										//Output solved question HTML code  
	 * </code>
	 * 
	 * @param HTML_QuickForm $form The form to add fields to and display
	 * @param boolean $showCorrectAnswers Whether to show the correct answers
	 * @param boolean $$showGivenAnswers Whether to show the given answers
	 * @return string The HTML code of the solved question
	 * @since 3.5.0
	 * @access public
	 */
	public function toHTMLSolved(&$form, $showCorrectAnswers = true, $showGivenAnswers = true) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form

	    $results = $this -> correct();                                             //Correct question
	    if ($showGivenAnswers) {                                                   //If the user's given answers should be shown, assign them as defaults in the form
	        $form -> setDefaults(array("question[".$this -> question['id']."]" => $this -> userAnswer));
	    } else {
	        $form -> setDefaults(array("question[".$this -> question['id']."]" => null));
	    }
        $showGivenAnswers && $showCorrectAnswers ? $style = '' : $style = "color:black";                                          //The question color must not change in case the user's answers should not display
	    
        $form               -> freeze();                                           //Freeze the form elements
	    $renderer           =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    $form               -> accept($renderer);                                  //Render the form
	    $formArray           = $renderer -> toArray();                             //Get the rendered form fields
	    $innerQuestionString = '';
	    for ($k = 0; $k < sizeof($this -> options); $k++) {                        //Display properly each option. The group can't be used, since we will display each option differently, depending on whether it is correct or not
	        $index = $this -> order[$k];                                           //$index is used to recreate the answers order, for a done test, or to apply the answers shuffle, for an unsolved test
	        if ($results['correct']) {
	            $innerQuestionString .= '<span class = "correctAnswer" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][$index]['html'].'</span><br/>';
	        } else {
	            $innerQuestionString .= '<span class = "wrongAnswer" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][$index]['html'].'</span>';
	            if ($showCorrectAnswers && $this -> answer[0] == $index) {
	                $innerQuestionString .= '<span class = "correctAnswer">&nbsp;&nbsp;&nbsp;&larr;&nbsp;'._RIGHTANSWER."</span>"; 
	            }
	            $innerQuestionString .= '<br/>';
	        }
	    }
	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$innerQuestionString.'
	            			</td></tr>
		            	'.($this -> question['explanation'] ? '<tr><td class = "questionExplanation">'._EXPLANATION.': '.$this -> question['explanation'].'</td></tr>' : '').'	            			
	            	</table>';
	    
        return $questionString;
	}
	
	/**
	 * Shuffle question options
	 * 
	 * This function is used to shuffle the question options,
	 * so that they are displayed in a random order.
	 * <br/>Example:
	 * <code>
	 * $question = new MultipleOneQuestion(3);										//Instantiate question
	 * $newOrder = $question -> shuffle();											//Shuffle question options
	 * </code>
	 * 
	 * @return array The new question options order
	 * @since 3.5.0
	 * @access public
	 */
	public function shuffle() {
	    $shuffleOrder = range(0, sizeof($this -> options) - 1);
	    shuffle($shuffleOrder);
	    $this -> order = $shuffleOrder;
	    
	    return $shuffleOrder;
	}
	
	/**
	 * Correct question
	 * 
	 * This function is used to correct the question. In order to correct it,
	 * setDone() must already have been called, so that the user answer
	 * is present.
	 * <br/>Example:
	 * <code>
	 * $question = new MultipleOneQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * $results = $question -> correct();											//Correct question
	 * </code>
	 * 
	 * @return array The correction results
	 * @since 3.5.0
	 * @access public
	 */
	public function correct() {
	    $this -> answer[0] == $this -> userAnswer ? $results = array('correct' => true, 'score' => 1) : $results = array('correct' => false, 'score' => 0);
	    return $results;
	}
	
	/**
	 * Set question done information
	 * 
	 * This function is used to set its done information. This information consists of
	 * the user answer, the score and the answers order.
	 * <br/>Example:
	 * <code>
	 * $question = new MultipleOneQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * </code>
	 * 
	 * @param array $userAnswer The user answer
	 * @param float score The user's score in this question
	 * @param array $order the question options order
	 * @since 3.5.0
	 * @access public 
	 */
	public function setDone($userAnswer, $score = false, $order = false) {
	    $this -> userAnswer = $userAnswer;
	    $score !== false ? $this -> score = $score : null;
	    $order !=  false ? $this -> order = $order : null;
	}    
}

/**
 * MultipleManyQuestion Class
 * 
 * This class is used to manipulate a multiple choice / many answers question
 */
class MultipleManyQuestion extends Question implements iQuestion
{
    
    /**
     * Convert question to HTML_QuickForm
     * 
     * This function is used to convert the question to HTML_QuickForm fields.
     * <br/>Example:
     * <code>
     * $question = new MultipleManyQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> toHTMLQuickForm($form);											//Add fields to form
     * </code>
     * 
     * @param HTML_QuickForm $form The form to add fields to
     * @since 3.5.0
     * @access public
     */
	public function toHTMLQuickForm(&$form) {
	    for ($k = 0; $k < sizeof($this -> options); $k++) {
	        $index      = $this -> order[$k];                                                               //$index is used to reorder question options, in case it was shuffled 
	        $elements[] = $form -> createElement("advcheckbox", "question[".$this -> question['id']."][".$index."]", $this -> options[$index], $this -> options[$index], 'class = "inputCheckbox"', array(0, 1));
	    }
	    $form -> addGroup($elements, "question[".$this -> question['id']."]", null, "<br/>", false);        //Create a group with the above radio buttons
	}
	
	/**
	 * Create HTML version of unsolved question
	 * 
	 * This function is used to create the HTML code corresponding
	 * to the question. The HTML is created using the question form
	 * fields, so the proper form must be specified. A form renderer 
	 * is used to output the fields. The function calls internally 
	 * toHTMLQuickForm()
	 * <br/>Example:
	 * <code>
     * $question = new MultipleManyQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * echo $question -> toHTML($form);												//Output question HTML code
     * </code> 
     * 
     * @param HTML_QuickForm $form The form to add fields to and display
     * @return string The HTML code for the question
     * @since 3.5.0
     * @access public
	 */
	public function toHTML(&$form) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form 
	    $renderer =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    
	    $form          -> accept($renderer);                                       //Render the form
	    $formArray      = $renderer -> toArray();                                  //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$formArray['question'][$this -> question['id']]['html'].'
	            			</td></tr>
	            	</table>';
	    
        return $questionString;
	}

	/**
	 * Display solved question
	 * 
	 * This function is used to display the solved version of the
	 * question. In order to display it, setDone() must have been
	 * called before.
	 * <br/>Example:
	 * <code>
     * $question = new MultipleManyQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> setDone($answer, $score, $order);								//Set question to be done
     * echo $question -> toHTMLSolved($form);										//Output solved question HTML code  
	 * </code>
	 * 
	 * @param HTML_QuickForm $form The form to add fields to and display
	 * @param boolean $showCorrectAnswers Whether to show the correct answers
	 * @param boolean $$showGivenAnswers Whether to show the given answers
	 * @return string The HTML code of the solved question
	 * @since 3.5.0
	 * @access public
	 */
	public function toHTMLSolved(&$form, $showCorrectAnswers = true, $showGivenAnswers = true) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form

	    $results = $this -> correct();                                             //Correct question

	    for ($k = 0; $k < sizeof($this -> options); $k++) {                        
	        if ($showGivenAnswers) {                                               //If the user's given answers should be shown, assign them as defaults in the form
		        $form -> setDefaults(array("question[".$this -> question['id']."][$k]" => $this -> userAnswer[$k]));
		    } else {
		        $form -> setDefaults(array("question[".$this -> question['id']."][$k]" => null));
		    }
	    }
	    
	    $renderer           =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    $form               -> freeze();                                           //Freeze the form elements
	    $form               -> accept($renderer);                                  //Render the form
	    $formArray           = $renderer -> toArray();                             //Get the rendered form fields
	    $innerQuestionString = '';

	    for ($k = 0; $k < sizeof($this -> options); $k++) {                        //Display properly each option. The group can't be used, since we will display each option differently, depending on whether it is correct or not
	        $showGivenAnswers && $showCorrectAnswers ? $style = '' : $style = "color:black";                                          //The question color must not change in case the user's answers should not display
	        $index = $this -> order[$k];                                           //$index is used to recreate the answers order, for a done test, or to apply the answers shuffle, for an unsolved test
	        if ($results['correct'][$index]) {
	            $innerQuestionString .= '<span class = "correctAnswer" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][$index]['html'].'</span><br/>';
	        } else {
	            $innerQuestionString .= '<span class = "wrongAnswer" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][$index]['html'].'</span>';
	            if ($showCorrectAnswers) {
	                $innerQuestionString .= '<span class = "correctAnswer">&nbsp;&nbsp;&nbsp;&larr;&nbsp;'._RIGHTANSWER.": "._TRUE."</span>"; 
	            }
	            $innerQuestionString .= '<br/>';
	        }
	    }
	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$innerQuestionString.'
	            			</td></tr>
		            	'.($this -> question['explanation'] ? '<tr><td class = "questionExplanation">'._EXPLANATION.': '.$this -> question['explanation'].'</td></tr>' : '').'	            			
	            	</table>';
	    
        return $questionString;
	}
	
	/**
	 * Shuffle question options
	 * 
	 * This function is used to shuffle the question options,
	 * so that they are displayed in a random order.
	 * <br/>Example:
	 * <code>
	 * $question = new MultipleManyQuestion(3);										//Instantiate question
	 * $newOrder = $question -> shuffle();											//Shuffle question options
	 * </code>
	 * 
	 * @return array The new question options order
	 * @since 3.5.0
	 * @access public
	 */
	public function shuffle() {
	    $shuffleOrder = range(0, sizeof($this -> options) - 1);
	    shuffle($shuffleOrder);
	    $this -> order = $shuffleOrder;
	    
	    return $shuffleOrder;
	}
	
	/**
	 * Correct question
	 * 
	 * This function is used to correct the question. In order to correct it,
	 * setDone() must already have been called, so that the user answer
	 * is present.
	 * <br/>Example:
	 * <code>
	 * $question = new MultipleManyQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * $results = $question -> correct();											//Correct question
	 * </code>
	 * 
	 * @return array The correction results
	 * @since 3.5.0
	 * @access public
	 */
	public function correct() {
	    $nc = 0; $nf = 0;
	    for ($i = 0; $i < sizeof($this -> userAnswer); $i++) {
	        $results['correct'][$i] = true;                                                //Use this variable in order for the template to know how to color the answers (green/red)
	        if (isset($this -> answer[$i]) && $this -> userAnswer[$i] == 1) {
	            $nc++;
	        } elseif (!isset($this -> answer[$i]) && $this -> userAnswer[$i] == 1) {
	            $results['correct'][$i] = false;                                                //Use this variable in order for the template to know how to color the answers (green/red)
	            $nf++;
	        } elseif (isset($this -> answer[$i]) && $this -> userAnswer[$i] == 0) {
	            $results['correct'][$i] = false;                                                //Use this variable in order for the template to know how to color the answers (green/red)
	        }
	    }
	    $c = sizeof($this -> answer);
	    $f = sizeof($this -> userAnswer) - sizeof($this -> answer);
	     
	    $results['score'] = max(0, $nc / $c - $nf / max($c, $f));
	     
	    return $results;
	}
	
	/**
	 * Set question done information
	 * 
	 * This question is used to set its done information. This information consists of
	 * the user answer, the score and the answers order.
	 * <br/>Example:
	 * <code>
	 * $question = new MultipleManyQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * </code>
	 * 
	 * @param array $userAnswer The user answer
	 * @param float score The user's score in this question
	 * @param array $order the question options order
	 * @since 3.5.0
	 * @access public 
	 */
	public function setDone($userAnswer, $score = false, $order = false) {
	    $this -> userAnswer = $userAnswer;
	    $score !== false ? $this -> score = $score : null;
	    $order !=  false ? $this -> order = $order : null;
	}    
}

/**
 * TrueFalseQuestion Class
 * 
 * This class is used to manipulate a true / false answers question
 */
class TrueFalseQuestion extends Question implements iQuestion
{
    /**
     * Convert question to HTML_QuickForm
     * 
     * This function is used to convert the question to HTML_QuickForm fields.
     * <br/>Example:
     * <code>
     * $question = new TrueFalseQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> toHTMLQuickForm($form);											//Add fields to form
     * </code>
     * 
     * @param HTML_QuickForm $form The form to add fields to
     * @since 3.5.0
     * @access public
     */
	public function toHTMLQuickForm(&$form) {
	    $elements[] = $form -> createElement("radio", "question[".$this -> question['id']."]", _FALSE, _FALSE, 0);
	    $elements[] = $form -> createElement("radio", "question[".$this -> question['id']."]", _TRUE, _TRUE, 1);
	    $form -> addGroup($elements, "question[".$this -> question['id']."]", null, "<br/>", false);
	}
	
	/**
	 * Create HTML version of unsolved question
	 * 
	 * This function is used to create the HTML code corresponding
	 * to the question. The HTML is created using the question form
	 * fields, so the proper form must be specified. A form renderer 
	 * is used to output the fields. The function calls internally 
	 * toHTMLQuickForm()
	 * <br/>Example:
	 * <code>
     * $question = new TrueFalseQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * echo $question -> toHTML($form);												//Output question HTML code
     * </code> 
     * 
     * @param HTML_QuickForm $form The form to add fields to and display
     * @return string The HTML code for the question
     * @since 3.5.0
     * @access public
	 */
	public function toHTML(&$form) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form 
	    $renderer =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    
	    $form          -> accept($renderer);                                       //Render the form
	    $formArray      = $renderer -> toArray();                                  //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$formArray['question'][$this -> question['id']]['html'].'
	            			</td></tr>
	            	</table>';
	    
        return $questionString;
	}

	/**
	 * Display solved question
	 * 
	 * This function is used to display the solved version of the
	 * question. In order to display it, setDone() must have been
	 * called before.
	 * <br/>Example:
	 * <code>
     * $question = new TrueFalseQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> setDone($answer, $score, $order);								//Set question to be done
     * echo $question -> toHTMLSolved($form);										//Output solved question HTML code  
	 * </code>
	 * 
	 * @param HTML_QuickForm $form The form to add fields to and display
	 * @param boolean $showCorrectAnswers Whether to show the correct answers
	 * @param boolean $$showGivenAnswers Whether to show the given answers
	 * @return string The HTML code of the solved question
	 * @since 3.5.0
	 * @access public
	 */
	public function toHTMLSolved(&$form, $showCorrectAnswers = true, $showGivenAnswers = true) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form
	    
	    $results = $this -> correct();                                             //Correct question
	    $results['correct'] ? $class = 'correctAnswer' : $class = 'wrongAnswer';
	    
	    $form     -> freeze();                                           //Freeze the form elements
	    if ($showGivenAnswers) {                                               //If the user's given answers should be shown, assign them as defaults in the form
	        $form -> setDefaults(array("question[".$this -> question['id']."]" => $this -> userAnswer));
	    } else {
	        $form -> setDefaults(array("question[".$this -> question['id']."]" => null));
	    }
        $showGivenAnswers && $showCorrectAnswers ? $style = '' : $style = "color:black";                                          //The question color must not change in case the user's answers should not display
        
	    $renderer =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    $form     -> accept($renderer);                                  //Render the form
	    $formArray = $renderer -> toArray();                             //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
						    	<span class = "'.$class.'" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][0]['html'].'</span>'.($showCorrectAnswers && $this -> answer == 0 ? '<span class = "correctAnswer">&nbsp;&nbsp;&nbsp;&larr;&nbsp;'._RIGHTANSWER.'</span>' : '').'<br/>
						    	<span class = "'.$class.'" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][1]['html'].'</span>'.($showCorrectAnswers && $this -> answer == 1 ? '<span class = "correctAnswer">&nbsp;&nbsp;&nbsp;&larr;&nbsp;'._RIGHTANSWER.'</span>' : '').'<br/>
	            			</td></tr>
		            	'.($this -> question['explanation'] ? '<tr><td class = "questionExplanation">'._EXPLANATION.': '.$this -> question['explanation'].'</td></tr>' : '').'	            			
	            	</table>';
	    
        return $questionString;
	}
	
	/**
	 * Shuffle question options
	 * 
	 * This function is not used for this type of question
	 * 
	 * @since 3.5.0
	 * @access public
	 */
	public function shuffle() {
	    return true;
	}
	
	/**
	 * Correct question
	 * 
	 * This function is used to correct the question. In order to correct it,
	 * setDone() must already have been called, so that the user answer
	 * is present.
	 * <br/>Example:
	 * <code>
	 * $question = new TrueFalseQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * $results = $question -> correct();											//Correct question
	 * </code>
	 * 
	 * @return array The correction results
	 * @since 3.5.0
	 * @access public
	 */
	public function correct() {
	    $this -> answer == $this -> userAnswer ? $results = array('correct' => true, 'score' => 1) : $results = array('correct' => false, 'score' => 0);	     
	    return $results;
	}
	
	/**
	 * Set question done information
	 * 
	 * This question is used to set its done information. This information consists of
	 * the user answer, the score and the answers order.
	 * <br/>Example:
	 * <code>
	 * $question = new TrueFalseQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score);									    //Set done question information
	 * </code>
	 * 
	 * @param array $userAnswer The user answer
	 * @param float score The user's score in this question
	 * @param array $order the question options order, not applicable to this question type
	 * @since 3.5.0
	 * @access public 
	 */
	public function setDone($userAnswer, $score = false, $order = false) {
	    $this -> userAnswer = $userAnswer;
	    $score !== false ? $this -> score = $score : null;
	    //$order !== false ? $this -> order = $order : null;
	}    
}

/**
 * EmptySpacesQuestion Class
 * 
 * This class is used to manipulate a empty spaces question
 */
class EmptySpacesQuestion extends Question implements iQuestion
{
    
    /**
     * Convert question to HTML_QuickForm
     * 
     * This function is used to convert the question to HTML_QuickForm fields.
     * <br/>Example:
     * <code>
     * $question = new EmptySpacesQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> toHTMLQuickForm($form);											//Add fields to form
     * </code>
     * 
     * @param HTML_QuickForm $form The form to add fields to
     * @since 3.5.0
     * @access public
     */
	public function toHTMLQuickForm(&$form) {
	    $inputLabels  = explode('###', $this -> question['text']);
	    $questionText = '';
	    for ($k = 0; $k < sizeof($this -> answer); $k++) {
	        $elements[] = $form -> addElement("static", null, null, $inputLabels[$k]);
	        $elements[] = $form -> addElement("text", "question[".$this -> question['id']."][$k]", null, 'class = "inputText"');
	    }
	    $elements[] = $form -> addElement("static", null, null, $inputLabels[$k]);
	    
	    $form -> addGroup($elements, "question[".$this -> question['id']."]", $inputLabels[0], null, false);
	}
	
	/**
	 * Create HTML version of unsolved question
	 * 
	 * This function is used to create the HTML code corresponding
	 * to the question. The HTML is created using the question form
	 * fields, so the proper form must be specified. A form renderer 
	 * is used to output the fields. The function calls internally 
	 * toHTMLQuickForm()
	 * <br/>Example:
	 * <code>
     * $question = new EmptySpacesQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * echo $question -> toHTML($form);												//Output question HTML code
     * </code> 
     * 
     * @param HTML_QuickForm $form The form to add fields to and display
     * @return string The HTML code for the question
     * @since 3.5.0
     * @access public
	 */
	
	
	public function toHTML(&$form) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form 
	    $renderer =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    
	    $form          -> accept($renderer);                                       //Render the form
	    $formArray      = $renderer -> toArray();                                  //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
	            		<tr><td style = "vertical-align:middle">
	            				'.$formArray['question'][$this -> question['id']]['html'].'
	            			</td></tr>
	            	</table>';
	    
        return $questionString;
	}

	/**
	 * Display solved question
	 * 
	 * This function is used to display the solved version of the
	 * question. In order to display it, setDone() must have been
	 * called before.
	 * <br/>Example:
	 * <code>
     * $question = new EmptySpacesQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> setDone($answer, $score, $order);								//Set question to be done
     * echo $question -> toHTMLSolved($form);										//Output solved question HTML code  
	 * </code>
	 * 
	 * @param HTML_QuickForm $form The form to add fields to and display
	 * @param boolean $showCorrectAnswers Whether to show the correct answers
	 * @param boolean $$showGivenAnswers Whether to show the given answers
	 * @return string The HTML code of the solved question
	 * @since 3.5.0
	 * @access public
	 */
	public function toHTMLSolved(&$form, $showCorrectAnswers = true, $showGivenAnswers = true) {
	    $inputLabels  = explode('###', $this -> question['text']);
	    
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form

	    $results = $this -> correct();                                             //Correct question

	    for ($k = 0; $k < sizeof($this -> answer); $k++) {
	        if ($showGivenAnswers) {                                               //If the user's given answers should be shown, assign them as defaults in the form
		        $form -> setDefaults(array("question[".$this -> question['id']."][$k]" => $this -> userAnswer[$k]));
		    } else {
		        $form -> setDefaults(array("question[".$this -> question['id']."][$k]" => null));
		    }
	    }
	    $renderer           =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    $form               -> freeze();                                           //Freeze the form elements
	    $form               -> accept($renderer);                                  //Render the form
	    $formArray           = $renderer -> toArray();                             //Get the rendered form fields
	    $innerQuestionString = $inputLabels[0];
	    for ($k = 0; $k < sizeof($this -> answer); $k++) {
	        $showGivenAnswers && $showCorrectAnswers ? $style = '' : $style = "color:black";                                          //The question color must not change in case the user's answers should not display
	        if ($results['correct'][$k]) {
	            $innerQuestionString .= '<span class = "correctAnswer" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][$k]['html'].'</span>'.$inputLabels[$k + 1].'<br/>';
	        } else {
	            $innerQuestionString .= '<span class = "wrongAnswer" style = "'.$style.'">'.$formArray['question'][$this -> question['id']][$k]['html'].'</span>'.$inputLabels[$k + 1].'<br/>';
	        }
	    }

	    if ($showCorrectAnswers) {
	        $innerQuestionString .= '<br/><br/><span class = "correctAnswer">'._RIGHTANSWER.':</span><br/>'.$inputLabels[0];
	        for ($k = 0; $k < sizeof($this -> answer); $k++) {
	            $innerQuestionString .= '<span class = "correctAnswer">'.$this -> answer[$k].'</span>'.$inputLabels[$k + 1];
	        }
	    }
	    $questionString = '
	    			<table width = "100%">
	            		<tr><td style = "vertical-align:middle">
	            				'.$innerQuestionString.'
	            			</td></tr>
		            	'.($this -> question['explanation'] ? '<tr><td class = "questionExplanation">'._EXPLANATION.': '.$this -> question['explanation'].'</td></tr>' : '').'	            			
	            	</table>';
	    
        return $questionString;
	}
	
	/**
	 * Shuffle question options
	 * 
	 * This function is not used for this type of question
	 * 
	 * @since 3.5.0
	 * @access public
	 */
	public function shuffle() {
	    return true;
	}
	
	/**
	 * Correct question
	 * 
	 * This function is used to correct the question. In order to correct it,
	 * setDone() must already have been called, so that the user answer
	 * is present.
	 * <br/>Example:
	 * <code>
	 * $question = new EmptySpacesQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * $results = $question -> correct();											//Correct question
	 * </code>
	 * 
	 * @return array The correction results
	 * @since 3.5.0
	 * @access public
	 */
	public function correct() {
	    $results['score'] = 0;
	    $factor           = 1 / sizeof($this -> userAnswer);                                        //If the question has 4 options, then the factor is 1/4.
	    for ($i = 0; $i < sizeof($this -> userAnswer); $i++) {
	        $this -> answer[$i] = explode("|", mb_strtolower($this -> answer[$i]));

	        array_walk($this -> answer[$i], create_function('&$v, $k', '$v = trim($v);'));

	        if (isset($this -> answer[$i]) && in_array(mb_strtolower($this -> userAnswer[$i]), $this -> answer[$i])) {
	            $results['score']      += $factor;
	            $results['correct'][$i] = true;                                                //Use this variable in order for the template to know how to color the answers (green/red)
	        } else {
	            $results['correct'][$i] = false;
	        }
	        $this -> answer[$i] = implode(" "._OR." ", $this -> answer[$i]);
	    }
	     
	    return $results;
	}
	
	/**
	 * Set question done information
	 * 
	 * This question is used to set its done information. This information consists of
	 * the user answer, the score and the answers order.
	 * <br/>Example:
	 * <code>
	 * $question = new EmptySpacesQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score);										//Set done question information
	 * </code>
	 * 
	 * @param array $userAnswer The user answer
	 * @param float score The user's score in this question
	 * @param array $order the question options order, not applicable for this question type
	 * @since 3.5.0
	 * @access public 
	 */
	public function setDone($userAnswer, $score = false, $order = false) {
	    $this -> userAnswer = $userAnswer;
	    $score !== false ? $this -> score = $score : null;
	    //$order !== false ? $this -> order = $order : null;
	}    
}

/**
 * MatchQuestion Class
 * 
 * This class is used to manipulate a match question
 */
class MatchQuestion extends Question implements iQuestion
{
    
    /**
     * Convert question to HTML_QuickForm
     * 
     * This function is used to convert the question to HTML_QuickForm fields.
     * <br/>Example:
     * <code>
     * $question = new MatchQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> toHTMLQuickForm($form);											//Add fields to form
     * </code>
     * 
     * @param HTML_QuickForm $form The form to add fields to
     * @since 3.5.0
     * @access public
     */
	public function toHTMLQuickForm(&$form) {
	    $random = range(0, sizeof($this -> answer) - 1);                                                   //$random is a temporary array used only for creating a random ordering
	    for ($k = 0; $k < sizeof($this -> options); $k++) {
		    shuffle($random);                                                                              //Shuffle an array with the same size as the answers
		    $answers = array();                                                                            //$answers array will be used in place of $this -> answer
		    foreach ($random as $value) {                                                                  //Populate $answers array, so that it is a permutated version of this -> answer array
		        $answers[$value] = $this -> answer[$value];
		    }
	        $index      = $this -> order[$k];                                                               //$index is used to reorder question options, in case it was shuffled 
	        $elements[] = $form -> addElement("static", null, null, $this -> options[$index]);
	        $elements[] = $form -> addElement("select", "question[".$this -> question['id']."][".$index."]", $this -> options, $answers); 
	    }
	    $form -> addGroup($elements, "question[".$this -> question['id']."]", null, array("&nbsp;&rarr;&nbsp;", "<br/>"), false);	    
	}
	
	/**
	 * Create HTML version of unsolved question
	 * 
	 * This function is used to create the HTML code corresponding
	 * to the question. The HTML is created using the question form
	 * fields, so the proper form must be specified. A form renderer 
	 * is used to output the fields. The function calls internally 
	 * toHTMLQuickForm()
	 * <br/>Example:
	 * <code>
     * $question = new MatchQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * echo $question -> toHTML($form);												//Output question HTML code
     * </code> 
     * 
     * @param HTML_QuickForm $form The form to add fields to and display
     * @return string The HTML code for the question
     * @since 3.5.0
     * @access public
	 */
	public function toHTML(&$form) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form 
	    $renderer =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    
	    $form          -> accept($renderer);                                       //Render the form
	    $formArray      = $renderer -> toArray();                                  //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$formArray['question'][$this -> question['id']]['html'].'
	            			</td></tr>
	            	</table>';
	    
        return $questionString;
	}

	/**
	 * Display solved question
	 * 
	 * This function is used to display the solved version of the
	 * question. In order to display it, setDone() must have been
	 * called before.
	 * <br/>Example:
	 * <code>
     * $question = new MatchQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> setDone($answer, $score, $order);								//Set question to be done
     * echo $question -> toHTMLSolved($form);										//Output solved question HTML code  
	 * </code>
	 * 
	 * @param HTML_QuickForm $form The form to add fields to and display
	 * @param boolean $showCorrectAnswers Whether to show the correct answers
	 * @param boolean $$showGivenAnswers Whether to show the given answers
	 * @return string The HTML code of the solved question
	 * @since 3.5.0
	 * @access public
	 */
	public function toHTMLSolved(&$form, $showCorrectAnswers = true, $showGivenAnswers = true) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form

	    $results = $this -> correct();                                             //Correct question
	    for ($k = 0; $k < sizeof($this -> options); $k++) {                        //Display properly each option. The group can't be used, since we will display each option differently, depending on whether it is correct or not
	        if ($showGivenAnswers) {                                               //If the user's given answers should be shown, assign them as defaults in the form
		        $form -> setDefaults(array("question[".$this -> question['id']."][$k]" => $this -> userAnswer[$k]));
		    }  else {
		        $form -> setDefaults(array("question[".$this -> question['id']."][$k]" => null));
		    }
	    }
	    $renderer           =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    $form               -> freeze();                                           //Freeze the form elements
	    $form               -> accept($renderer);                                  //Render the form
	    $formArray           = $renderer -> toArray();                             //Get the rendered form fields
	    $innerQuestionString = '';

	    for ($k = 0; $k < sizeof($this -> options); $k++) {                        //Display properly each option. The group can't be used, since we will display each option differently, depending on whether it is correct or not
	        $showGivenAnswers && $showCorrectAnswers ? $style = '' : $style = "color:black";                                          //The question color must not change in case the user's answers should not display
	        $index = $this -> order[$k];                                           //$index is used to recreate the answers order, for a done test, or to apply the answers shuffle, for an unsolved test
	        if ($results['correct'][$index]) {
	            $innerQuestionString .= '<span class = "correctAnswer" style = "'.$style.'">'.$this -> options[$index].'&nbsp;&rarr;&nbsp;'.$formArray['question'][$this -> question['id']][$index]['html'].'</span>';
	        } else {
	            $innerQuestionString .= '<span class = "wrongAnswer" style = "'.$style.'">'.$this -> options[$index].'&nbsp;&rarr;&nbsp;'.$formArray['question'][$this -> question['id']][$index]['html'].'</span>';
	        }
	        if ($showCorrectAnswers) {
	        	$innerQuestionString .= '<span class = "correctAnswer">&nbsp;&nbsp;&nbsp;&larr;&nbsp;'._RIGHTANSWER.": ".$this -> answer[$index]."</span>";
	        }
	        $innerQuestionString .= '<br/>';
	    }
	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$innerQuestionString.'
	            			</td></tr>
		            	'.($this -> question['explanation'] ? '<tr><td class = "questionExplanation">'._EXPLANATION.': '.$this -> question['explanation'].'</td></tr>' : '').'	            			
	            	</table>';
	    
        return $questionString;
	}

	/**
	 * Shuffle question options
	 * 
	 * This function is used to shuffle the question options,
	 * so that they are displayed in a random order.
	 * <br/>Example:
	 * <code>
	 * $question = new MultipleManyQuestion(3);										//Instantiate question
	 * $newOrder = $question -> shuffle();											//Shuffle question options
	 * </code>
	 * 
	 * @return array The new question options order
	 * @since 3.5.0
	 * @access public
	 */
	public function shuffle() {
	    $shuffleOrder = range(0, sizeof($this -> options) - 1);
	    shuffle($shuffleOrder);
	    $this -> order = $shuffleOrder;
	    
	    return $shuffleOrder;
	}
	
	/**
	 * 	
	 * Correct question
	 * 
	 * This function is used to correct the question. In order to correct it,
	 * setDone() must already have been called, so that the user answer
	 * is present.
	 * <br/>Example:
	 * <code>
	 * $question = new MatchQuestion(3);											//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * $results = $question -> correct();											//Correct question
	 * </code>
	 * 
	 * @return array The correction results
	 * @since 3.5.0
	 * @access public
	 */
	public function correct() {
	    $results['score'] = 0;
	    $factor            = 1 / sizeof($this -> userAnswer);                                        //If the question has 4 options, then the factor is 1/4.
	    $answerKeys        = array_keys($this -> answer);
	    for ($i = 0; $i < sizeof($this -> userAnswer); $i++) {
	        if ($this -> userAnswer[$i] == $answerKeys[$i] || $this -> answer[$this -> userAnswer[$i]] == $this -> answer[$i]) {
	            $results['score']      += $factor;
	            $results['correct'][$i] = true;                                                //Use this variable in order for the template to know how to color the answers (green/red)
	        } else {
	            $results['correct'][$i] = false;
	        }
	    }
	    	
	    return $results;
	}
	
	/**
	 * Set question done information
	 * 
	 * This question is used to set its done information. This information consists of
	 * the user answer, the score and the answers order.
	 * <br/>Example:
	 * <code>
	 * $question = new MatchQuestion(3);										//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * </code>
	 * 
	 * @param array $userAnswer The user answer
	 * @param float score The user's score in this question
	 * @param array $order the question options order
	 * @since 3.5.0
	 * @access public 
	 */
	public function setDone($userAnswer, $score = false, $order = false) {
	    $this -> userAnswer = $userAnswer;
	    $score !== false ? $this -> score = $score : null;
	    $order !=  false ? $this -> order = $order : null;
	}    
}

/**
 * RawTextQuestion Class
 * 
 * This class is used to manipulate a raw text question
 */
class RawTextQuestion extends Question implements iQuestion
{
    
    /**
     * Convert question to HTML_QuickForm
     * 
     * This function is used to convert the question to HTML_QuickForm fields.
     * <br/>Example:
     * <code>
     * $question = new RawTextQuestion(3);										//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> toHTMLQuickForm($form);											//Add fields to form
     * </code>
     * 
     * @param HTML_QuickForm $form The form to add fields to
     * @since 3.5.0
     * @access public
     */
	public function toHTMLQuickForm(&$form) {
	    $elements[] = $form -> createElement("textarea", "question[".$this -> question['id']."]", null, 'class = "simpleEditor" style = "width:100%;height:100px;"');
	    $elements[] = $form -> createElement("file",     "file_".$this -> question['id'], null, 'class = "inputText" id = "file_'.$this -> question['id'].'" style = "display:none"');
	    $form -> addGroup($elements, "question[".$this -> question['id']."]", null, "<br/>", false);
	}
	
	/**
	 * Create HTML version of unsolved question
	 * 
	 * This function is used to create the HTML code corresponding
	 * to the question. The HTML is created using the question form
	 * fields, so the proper form must be specified. A form renderer 
	 * is used to output the fields. The function calls internally 
	 * toHTMLQuickForm()
	 * <br/>Example:
	 * <code>
     * $question = new RawTextQuestion(3);											//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * echo $question -> toHTML($form);												//Output question HTML code
     * </code> 
     * 
     * @param HTML_QuickForm $form The form to add fields to and display
     * @return string The HTML code for the question
     * @since 3.5.0
     * @access public
	 */
	public function toHTML(&$form) {
	    
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form 
	    $renderer =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    
	    $form          -> accept($renderer);                                       //Render the form
	    $formArray      = $renderer -> toArray();                                  //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
		            	<tr><td style = "vertical-align:middle">
	            				'.$formArray['question'][$this -> question['id']]['html'].'
	            				<br/><a href = "javascript:void(0)" onClick = "this.style.display = \'none\';document.getElementById(\'file_'.$this -> question['id'].'\').style.display = \'\'">('._SENDFILEASANSWER.')</a>
	            			</td></tr>
	            	</table>';
	    
        return $questionString;
	}

	/**
	 * Display solved question
	 * 
	 * This function is used to display the solved version of the
	 * question. In order to display it, setDone() must have been
	 * called before.
	 * <br/>Example:
	 * <code>
     * $question = new RawTextQuestion(3);											//Instantiate question
     * $form = new HTML_QuickForm("questionForm", "post", "", "", null, true);		//Create a form
     * $question -> setDone($answer, $score, $order);								//Set question to be done
     * echo $question -> toHTMLSolved($form);										//Output solved question HTML code  
	 * </code>
	 * 
	 * @param HTML_QuickForm $form The form to add fields to and display
	 * @param boolean $showCorrectAnswers Whether to show the correct answers
	 * @param boolean $$showGivenAnswers Whether to show the given answers
	 * @return string The HTML code of the solved question
	 * @since 3.5.0
	 * @access public
	 */
	public function toHTMLSolved(&$form, $showCorrectAnswers = true, $showGivenAnswers = true) {
	    $this -> toHTMLQuickForm($form);                                           //Assign proper elements to the form

		preg_match("/<file id = \"(.*)\">/", $this -> userAnswer, $uploadedFiles);
		if ($uploadedFiles) {
			$this -> userAnswer = preg_replace("/<file id = \"(.*)\">/", "", $this -> userAnswer);
			try {
				$uploadedFile = new EfrontFile($uploadedFiles[1]);
			} catch (Exception $e) {}
		}

		$results = $this -> correct();                                             //Correct question
		if ($showGivenAnswers) {
        	$form -> setDefaults(array("question[".$this -> question['id']."]" => $this -> userAnswer));
		} else {
		    $form -> setDefaults(array("question[".$this -> question['id']."]" => null));
		}
        
	    $renderer           =& new HTML_QuickForm_Renderer_ArraySmarty($foo);                //Get a smarty renderer, only because it reforms the form in a very convenient way for printing html
	    $form               -> freeze();                                           //Freeze the form elements
	    $form               -> accept($renderer);                                  //Render the form
	    $formArray           = $renderer -> toArray();                             //Get the rendered form fields

	    $questionString = '
	    			<table width = "100%">
		            	<tr><td>'.$this -> question['text'].'</td></tr>
	            		<tr><td style = "vertical-align:middle">
	            				'.$formArray['question'][$this -> question['id']]['html'].'
	            				'.($showCorrectAnswers  && $this -> answer ? '<span class = "correctAnswer"><br/>'._EXAMPLEANSWER.':<br/> '.$this -> answer.'</span>' : '').'
	            				'.($uploadedFile ? '<br/><b>'._UPLOADEDFILE.': <a href = "view_file.php?file='.$uploadedFile['id'].'&action=download">'.$uploadedFile['name'].'</a></b>' : '').'
	            			</td></tr>
		            	'.($this -> question['explanation'] ? '<tr><td class = "questionExplanation">'._EXPLANATION.': '.$this -> question['explanation'].'</td></tr>' : '').'	            			
	            	</table>';
	    
        return $questionString;
	}
	
	/**
	 * Shuffle question options
	 * 
	 * This function is not used for this type of question
	 * 
	 * @since 3.5.0
	 * @access public
	 */
	public function shuffle() {
	    return true;
	}
	
	/**
	 * Correct question
	 * 
	 * This function is used to correct the question. In order to correct it,
	 * setDone() must already have been called, so that the user answer
	 * is present.
	 * <br/>Example:
	 * <code>
	 * $question = new RawTextQuestion(3);											//Instantiate question
	 * $question -> setDone($answer, $score, $order);								//Set done question information
	 * $results = $question -> correct();											//Correct question
	 * </code>
	 * 
	 * @return array The correction results
	 * @since 3.5.0
	 * @access public
	 */
	public function correct() {
		if ($this -> score) {
			$results = array('correct' => '', 'score' => $this -> score);
		} else {
			$results = array('correct' => '', 'score' => -1);
		}        
        	     
	    return $results;
	}
	
	/**
	 * Set question done information
	 * 
	 * This question is used to set its done information. This information consists of
	 * the user answer, the score and the answers order.
	 * <br/>Example:
	 * <code>
	 * $question = new RawTextQuestion(3);											//Instantiate question
	 * $question -> setDone($answer, $score);										//Set done question information
	 * </code>
	 * 
	 * @param array $userAnswer The user answer
	 * @param float score The user's score in this question
	 * @param array $order the question options order, not applicable to this question type
	 * @since 3.5.0
	 * @access public 
	 */
	public function setDone($userAnswer, $score = false, $order = false) {

	    $this -> userAnswer = $userAnswer;
	    $score !== false ? $this -> score = $score : null;
	    //$order !== false ? $this -> order = $order : null;
	}    
}

/**
 * 
 */
interface iQuestion 
{
    public function toHTMLQuickForm(&$form);
    public function toHTML(&$form);
    public function toHTMLSolved(&$form, $showCorrectAnswers = true, $showGivenAnswers = true);
    public function shuffle();
    public function correct();
    public function setDone($userAnswer, $score = false, $order = false); 
}

/**
 * 
 */
abstract class Question 
{
	/**
	 * The available question types
	 *
	 * @var array
	 * @since 3.5.0
	 * @access public
	 */
	public static $questionTypes = array('empty_spaces'  => _EMPTYSPACES, 
                               			 'raw_text'      => _DEVELOPMENT, 
                               			 'multiple_one'  => _MULTIPLEONE, 
                               			 'multiple_many' => _MULTIPLEMANY, 
			                             'match'         => _MATCH, 
			                             'true_false'    => _TRUEFALSE);	
	
	/**
	 * The available question types icons
	 *
	 * @var array
	 * @since 3.5.0
	 * @access public
	 */
	public static $questionTypesIcons = array('empty_spaces'  => 'images/16x16/dot-chart.png', 
                                   			  'raw_text'      => 'images/16x16/pens.png', 
                                   			  'multiple_one'  => 'images/16x16/branch_element.png', 
                                   			  'multiple_many' => 'images/16x16/branch.png', 
    			                              'match'         => 'images/16x16/component.png', 
    			                              'true_false'    => 'images/16x16/yinyang.png');	

	/**
	 * The available question difficulties
	 *
	 * @var array
	 * @since 3.5.0
	 * @access public
	 */
	public static $questionDifficulties = array('easy'   => _LOW,
	                                            'medium' => _MEDIUM,
	                                            'hard'   => _HARD);
	/**
	 * The available question difficulties icons
	 *
	 * @var array
	 * @since 3.5.0
	 * @access public
	 */
	public static $questionDifficultiesIcons = array('easy'   => 'images/16x16/flag_green.png', 
	                                                 'medium' => 'images/16x16/flag_blue.png', 
	                                            	 'hard'   => 'images/16x16/flag_red.png',);
	
	/**
	 * The question fields
	 * 
	 * @var array
	 * @access public
	 * @since 3.5.0
	 */
	public $question = array();
	
	/**
	 * Question options
	 * 
     * @var array
     * @since 3.5.0
     * @access public
	 */
	public $options = array();
	
	/**
	 * Question's answer(s)
	 * 
	 * @var array
	 * @since 3.5.0
	 * @access public 
	 */
	public $answer = array();
	
    /**
     * The user's answer, if the question is done
     * 
     * @var array
     * @since 3.5.0
     * @access public
     */
	public $userAnswer = false;
	
	/**
	 * The user's score, if the question is done
	 * 
     * @var float
     * @since 3.5.0
     * @access public
	 */
	public $score = false;
	
	/**
	 * The questions's answers order
	 * 
     * @var array
     * @since 3.5.0
     * @access public
	 */
	public $order = array();
	
	/**
	 * The maximum question text length, when displayed not in tests
	 * 
	 * @since 3.5.0
	 *@access public
	 */
	const maxQuestionText = 50;
	
	/**
	 * Class constructor
	 * 
	 * This function is used to instantiate a test question object.
	 * If an id is used, then the question is instantiate based on
	 * database information. Alternatively, the question array itself
	 * may be provided, thus overriding database query. 
	 * <br/>Example:
	 * <code>
	 * $question = QuestionFactory :: factory(4);					//Instantiate question using question id
	 * $result   = eF_getTableData("questions", "*", "id=4");	
	 * $question = QuestionFactory :: factory($result[0]);			//Instantiate question using question array
	 * </code>
	 * 
	 * @param mixed $question Either a question id or a question array
	 * @param array $testOptions specific test options that have impact on the question rendering
	 * @since 3.5.0
	 * @access public
	 */
	function __construct($question) {
		if (is_array($question)) {
			$this -> question = $question;
		} elseif (!eF_checkParameter($question, 'id')) {
			throw new EfrontTestException(_INVALIDID.': '.$question, EfrontTestException :: INVALID_ID);
		} else {
			$result = eF_getTableData("questions", "*", "id=".$question);
			if (sizeof($result) == 0) {
				throw new EfrontTestException(_INVALIDID.': '.$question, EfrontTestException :: QUESTION_NOT_EXISTS);
			} else {
				$this -> question = $result[0];
			}
		}

		@unserialize($this -> question['options']) !== false ? $this -> options = unserialize($this -> question['options']) : $this -> options = $this -> question['options'];
		@unserialize($this -> question['answer'])  !== false ? $this -> answer  = unserialize($this -> question['answer'])  : $this -> answer  = $this -> question['answer'];

		is_array($this -> options) ? $this -> order = array_keys($this -> options) : null;
		$this -> question['type_icon'] = Question :: $questionTypesIcons[$this -> question['type']];
		$plainText = trim(strip_tags($this -> question['text']));
		if (mb_strlen($plainText) > self :: maxQuestionText) {
		    $plainText = mb_substr($plainText, 0, self :: maxQuestionText).'...';
		}
		$this -> question['plain_text'] = $plainText;
		//$testOptions ? $this -> testOptions = array_merge($this -> testOptions, $testOptions) : null;            //Merge arrays, thus only overwriting values that exist in both arrays
	}

	/**
	 * Delete question
	 * 
	 * This function is used to delete the current question
	 * <br/>Example:
	 * <code>
	 * $question -> delete();
	 * </code>
	 * 
	 * @return boolean True if the delete was successful
	 * @since 3.5.0
	 * @access public
	 */
	public function delete() {
		eF_deleteTableData("questions", "id=".$this -> question['id']);
		eF_deleteTableData("done_questions", "id=".$this -> question['id']);
		eF_deleteTableData("tests_to_questions", "id=".$this -> question['id']);
		
		return true;
	}
	
	/**
	 * Persist question changes
	 * 
	 * This function is used to store changed question attributes to 
	 * the database.
	 * <br>Example:
	 * <code>
	 * $question -> question['text'] = 'new title';				//Change question title
	 * $question -> persist();									//Persist changed value
	 * </code>
	 * 
	 * @since 3.5.0
	 * @access public
	 */
	public function persist() {
		$fields = array("text" => $this -> question['text'],
                		"type" => $this -> question['type'],
						"answer" => $this -> question['answer'],
						"content_ID" => $this -> question['content_ID'],
						"difficulty" => $this -> question['difficulty'],
						"options" => $this -> question['options'],
						"explanation" => $this -> question['explanation']);
		eF_updateTableData("questions", $fields, "id=".$this -> question['id']);
	}
	
	/**
	 * This question's tests
	 * 
	 * This function returns a list of all the tests that it is 
	 * assigned to.
	 * <br/>Example:
	 * <code>
	 * $question -> getTests();
	 * </code>
	 * 
	 * @return array The array of the tests this question is in
	 * @since 3.5.0
	 * @access public
	 */
	public function getTests() {
		$result = eF_getTableData("tests_to_questions tq, tests t", "t.*", "t.id=tq.tests_ID and tq.questions_ID=".$this -> question['id']);
		$tests  = array();
		foreach ($result as $value) {
		    $tests[$value['id']] = $value;
		}
		return $tests;
	}

	/**
	 * Create a new question
	 * 
	 * This function is used to create a new question
	 * <br/>Example:
	 * <code>
	 * $fields = array('text' => 'new questions', 'type' => 'multiple_one', 'content_ID' => 10);
	 * $question = Question :: createQuestion($fields);
	 * </code>
	 * 
	 * @param array $question The new question attributes
	 * @return Question the new question object or false
	 * @since 3.5.0
	 * @access public
	 * @static 
	 */
	public static function createQuestion($question) {
		!isset($question['difficulty']) ? $question['difficulty'] = 'medium' : null;
		!isset($question['timestamp'])  ? $question['timestamp']  = time()   : null;
		if ($newId = eF_insertTableData("questions", $question)) {
			return QuestionFactory :: factory($newId); 
		} else {
			return false;
		}
	}

}

/**
 *
 */
class QuestionFactory
{
    /**
     * Construct question object
     *
     * This function is used to construct a question object, based on the question type.
     * Specifically, it creates an TrueFalseQuestion, MultipleOneQuestion, MultipleManyQuestion etc
     * If $question is an id, the function queries the database. Alternatively, it may
     * use a prepared question array, which is mostly convenient when having to perform
     * multiple initializations
     * <br/>Example :
     * <code>
     * $question = QuestionFactory :: factory(43);            			//Use factory function to instantiate question object with id 43
     * $questionData = eF_getTableData("questions", "*", "id=43");
     * $question = QuestionFactory :: factory($$questionData[0]);      //Use factory function to instantiate user object using prepared data
     * </code>
     *
     * @param mixed $question A question id or an array holding question data
     * @return Question an object of a class extending Question
     * @since 3.5.0
     * @access public
     * @static
     */
    public static function factory($question) {
        if (!is_array($question)) {
	        if (eF_checkParameter($question, 'id')) {
	            $result = eF_getTableData("questions", "*", "id='".$question."'");
	            if (sizeof($result) == 0) {
	                throw new EfrontTestException(_INVALIDID.': '.$question, EfrontTestException :: QUESTION_NOT_EXISTS);
	            } 
	            $question = $result[0];
	        } else {
	            throw new EfrontTestException(_INVALIDID.': '.$question, EfrontTestException :: INVALID_ID);
	        }
        }

        switch ($question['type']) {
            case 'raw_text'      : $factory = new RawTextQuestion($question);      break;
            case 'multiple_one'  : $factory = new MultipleOneQuestion($question);  break;
            case 'multiple_many' : $factory = new MultipleManyQuestion($question); break;
            case 'empty_spaces'  : $factory = new EmptySpacesQuestion($question);  break;
            case 'match'         : $factory = new MatchQuestion($question);        break;
            case 'true_false'    : $factory = new TrueFalseQuestion($question);    break;
            default: throw new EfrontTestException(_INVALIDQUESTIONTYPE.': "'.$question['type'].'"', EfrontTestException :: INVALID_TYPE); break;
        }

        return $factory;
    }
}



?>