/*
 * Created on 10.03.2004 13:42:37
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.feedback;

import mauda.OperationRecorder;
import mauda.operation.*;

import java.util.*;
/**
 * The automatic evaluator of FaultMode-exercises
 * 
 * @author Markus Krebs
 */
public class FaultEvaluator extends Evaluator {

	private static final double falseCorrectWeight = 1.0;
	private static final double falseNotFirstWeight = 0.5;
	private static final double falseWeight = 1.0;
	
	// negativ-Punkte beim selektieren des falschen Fehlertyps
	private static final double falseFailureTypeRating = 0.5;

	/**
	 * ID that defines that the student is in search-the-failure-mode
	 */
	public static final int SEARCH_FAILURE = 0;
	/**
	 * ID that defines that the student found the first failure
	 */
	public static final int FIRST_FAILURE_FOUND = 1;
	/**
	 * ID that defines that the student thought that a correct
	 * operation was incorrect.
	 */
	public static final int FALSE_CORRECT = 2;
	/**
	 * ID that defines that the student found a failure, but it was
	 * not the first failure.
	 */ 
	public static final int FALSE_NOT_FIRST = 3;
	/**
	 * ID that defines that the student selected the false type of
	 * the failure.
	 */
	public static final int FALSE_FAILURE_TYPE_SELECTED = 4;
	/**
	 * ID that defines that the student has found the first failure,
	 * and selected the correct failure-type, and is currently
	 * specifying what the correct suboperation is.
	 */
	public static final int CORRECT_MODE = 5;
	/**
	 * ID that defines that the student given up the exercise,
	 */
	public static final int GIVE_UP = 6;
	
	private int state;
	
	// Incorrect-Button bei richtiger Operation gedrückt
	private Vector falseCorrectFeedbackObjects;
	// Incorrect-Button bei falscher aber nicht 1. falscher gedrückt
	private Vector falseNotFirstFeedbackObjects;
	// gemachte Fehler beim Spezifizieren der richtigen Operation
	private Vector falseFeedbackObjects;
	// Anzahl falsch gemachter Auswahlen beim spezifizieren des Fehlertyps
	private int falseFailureTypeCounter;
	
	/**
	 * Creates a FaultEvaluator
	 */
	public FaultEvaluator() {
		super();
		state = SEARCH_FAILURE;
		falseCorrectFeedbackObjects = new Vector();
		falseNotFirstFeedbackObjects = new Vector();
		falseFeedbackObjects = new Vector();
		falseFailureTypeCounter = 0;
	}
	
	private double calculatePoints(Vector v, double weight) {
		double errorPoints = 0.0;
		Enumeration en = v.elements();
		while(en.hasMoreElements()) {
			FeedbackObject fo = (FeedbackObject)en.nextElement();
			//System.out.println(fo);
			switch(fo.getCorrectness()) {
				case FeedbackObject.INCORRECT_MISSING :
					errorPoints += ((double)templateRating)*weight;
					errorCounter++;
					break;
				case FeedbackObject.CORRECT :	// <-- für FaultMode
				case FeedbackObject.INCORRECT :
					SubOperation cso = fo.getCorrectSubOperation();
					int r = templateRating;
					if(cso != null)	// <- bei ADDITIONAL_FALSE
						r = ((Integer)ratings.get(cso.getID())).intValue();
					errorPoints += ((double)r)*weight;
				default : break;
			}
		}	
		return errorPoints;
	}

	/**
	 * Calculates the maximal reachable points. This must be
	 * calculated before the student works on the exercise.
	 * @param v Vector of the exercise-todo-operations
	 * @see mauda.feedback.Evaluator#postProcessing(java.util.Vector)
	 */
	// Übergabe: sequentieller Vector von Ops/SubOps
	public void postProcessing(Vector v) {
		int counter = -1;
		int opnr = -1;
		int subopnr = -1;
		while(counter<v.size()) {
			counter++; if(counter>=v.size()) break;
			Object o = v.elementAt(counter);
			if(o instanceof Operation) {
				opnr++;
				Operation op = (Operation)o;
				//System.out.println("  "+op.out());
				// korrekte SubOperationen holen
				SubOperationQueue csoq = op.getSubOperationQueue();
				// aktuelle SubOperationen holen
				SubOperationQueue asoq = new SubOperationQueue();
				counter++;
				if(counter<v.size()) {
					o = v.elementAt(counter);
					while(counter<v.size() && o instanceof SubOperation) {
						asoq.add((SubOperation)o);
						counter++; if(counter>=v.size()) break;
						o = v.elementAt(counter);
					}
				}
				//System.out.println("    CORRECT-SOQ: "+csoq);
				//System.out.println("    ACTUAL-SOQ:  "+asoq);
				counter--;
				// Ratings der aktuellen SubOperationen auf-
				// summieren (also nicht die der korrekten)
				maxPoints += (double)templateRating;
				for(int i=0; i<asoq.length(); i++) {
					SubOperation aso = asoq.get(i);
					int r = ((Integer)ratings.get(aso.getID())).intValue();
					maxPoints += (double)r;
				}
			}
		}
		//System.out.println("Max-Points = "+doubleRound(maxPoints));
	}
	
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#evaluate(mauda.OperationRecorder)
	 */
	public void evaluate(OperationRecorder or) {
		if(state == GIVE_UP) return;
		double fc = calculatePoints(falseCorrectFeedbackObjects, falseCorrectWeight);
		double fnf = calculatePoints(falseNotFirstFeedbackObjects, falseNotFirstWeight);
		double f = calculatePoints(falseFeedbackObjects, falseWeight);
		errorPoints = fc+fnf+f;
		errorPoints += ((double)falseFailureTypeCounter)*falseFailureTypeRating;
		
		if(errorPoints>maxPoints) errorPoints = maxPoints;
		
		errorCounter += falseCorrectFeedbackObjects.size();
		errorCounter += falseNotFirstFeedbackObjects.size();
		errorCounter += falseFeedbackObjects.size(); 

		//System.out.println("False-Correct: "+falseCorrectFeedbackObjects.size());
		//System.out.println("False-Not-First: "+falseNotFirstFeedbackObjects.size());
		//System.out.println("Correct-Mode: "+falseFeedbackObjects.size());
		
	}
	
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#log(mauda.feedback.FeedbackObject)
	 */
	public void log(FeedbackObject fo) {
		Vector v = null;
		switch(state) {
			case FALSE_CORRECT :
				v = falseCorrectFeedbackObjects; break;
			case FALSE_NOT_FIRST :
				v = falseNotFirstFeedbackObjects; break;
			case CORRECT_MODE :
				v = falseFeedbackObjects; break;
		}
		//System.out.print("Log:");
		if(!v.contains(fo)) {
			//System.out.println(fo);
			v.add(fo);
		} 
		//else System.out.println("Already made error");
	}
	
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#log(int)
	 */
	public void log(int id) {
		String s = "";
		switch(id) {
			case SEARCH_FAILURE : s = "SEARCH_FAILURE"; break;
			case FIRST_FAILURE_FOUND : s = "FIRST_FAILURE_FOUND"; break;
			case FALSE_CORRECT : s = "FALSE_CORRECT"; break;
			case FALSE_NOT_FIRST : s = "FALSE_NOT_FIRST"; break;
			case FALSE_FAILURE_TYPE_SELECTED : s = "FALSE_FAILURE_TYPE_SELECTED"; break;
			case CORRECT_MODE : s = "GO_IN_CORRECT_MODE"; break;
		}
		if(id == FALSE_FAILURE_TYPE_SELECTED)
			falseFailureTypeCounter++;
		//System.out.println("Log: ID = "+s);
		state = id;
	}
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#getHTMLMessage()
	 */
	public String getHTMLMessage() {
		String msg = "";
		if(state == GIVE_UP) {
			// Wenn Aufgabe aufgegeben wurde: 0 Punkte geben
			msg += "Exercise was given up!<br><br>";
			msg += "You have reached 0 points.<br>";
			return msg;
		}
		// Prozentual korrekte Operationen
		percent = doubleRound(((maxPoints-errorPoints) / maxPoints) * 100.0);
		msg += "<table width=100%><tr><td>Score:</td><td align=right>";
		msg += "<big><b>"+percent+"</b></big> of 100";
		msg += "</td></tr></table>";
		
		msg += "<hr>";
		msg += "You have made "+errorCounter+" errors.<br>";
		msg += falseCorrectFeedbackObjects.size()+" times you think a operation was incorrect although the operation was correct.<br>";
		msg += falseNotFirstFeedbackObjects.size()+" times you found a non first failure.<br>";
		msg += falseFailureTypeCounter+" attemps you needed to specify the type of the failure.<br>";
		msg += falseFeedbackObjects.size()+" tries you needed to specify the correct operation.<br>";
		msg += doubleRound(maxPoints)+" are the maximal reachable points in this exercise.<br>";
		msg += "You have reached "+doubleRound(maxPoints-errorPoints)+" points.<br>";
		return msg;
	}
	/**
	 * Gets the current state the student reached 
	 * @return state-id
	 * @see mauda.feedback.FaultEvaluator#SEARCH_FAILURE
	 * @see mauda.feedback.FaultEvaluator#FIRST_FAILURE_FOUND
	 * @see mauda.feedback.FaultEvaluator#FALSE_CORRECT
	 * @see mauda.feedback.FaultEvaluator#FALSE_NOT_FIRST
	 * @see mauda.feedback.FaultEvaluator#FALSE_FAILURE_TYPE_SELECTED
	 * @see mauda.feedback.FaultEvaluator#CORRECT_MODE
	 */
	public int getState() { return state; }
	
	public void setState(int id) { state = id; } 

	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#save()
	 */
	public HashMap save() {
		HashMap hm = new HashMap();
		hm.put("FalseCorrectFeedbackObjects", saveFeedbackObjects(falseCorrectFeedbackObjects));
		hm.put("FalseNotFirstFeedbackObjects", saveFeedbackObjects(falseNotFirstFeedbackObjects));
		hm.put("FalseFeedbackObjects", saveFeedbackObjects(falseFeedbackObjects));
		hm.put("State", new Integer(state));
		hm.put("FalseFailureTypeCounter", new Integer(falseFailureTypeCounter));
		return hm;
	}
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#load(java.util.HashMap)
	 */
	public void load(HashMap hm) {
		falseCorrectFeedbackObjects = loadFeedbackObjects((Vector)hm.get("FalseCorrectFeedbackObjects"));
		falseNotFirstFeedbackObjects = loadFeedbackObjects((Vector)hm.get("FalseNotFirstFeedbackObjects"));
		falseFeedbackObjects = loadFeedbackObjects((Vector)hm.get("FalseFeedbackObjects"));
		state = ((Integer)hm.get("State")).intValue();
		falseFailureTypeCounter = ((Integer)hm.get("FalseFailureTypeCounter")).intValue();
	}

}
