/*
 * Created on 09.03.2004 13:36:17
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.feedback;

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

import java.util.*;
/**
 * The automatic evaluator of NormalMode-exercises
 * 
 * @author Markus Krebs
 */
public class NormalEvaluator extends Evaluator {
		
	// Gewichtung des Fehlers (Prozent von SubOpRating)
	private static final double weight = 1.0;

	private int selfCorrectedErrorCounter;	// #Gemachter, aber korrigierter Fehler
	
	// Aufzeichnen der falschen Feedback-Objekte
	// jeweils 1. Fehler einer Operation --> Statistik
	private Vector falseFeedbackObjects;
	
	/**
	 * Creates a NormalEvaluator
	 */
	public NormalEvaluator() {
		super();
		falseFeedbackObjects = new Vector();
		selfCorrectedErrorCounter = 0;
	}

	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#log(mauda.feedback.FeedbackObject)
	 */
	// Vermerken eines Fehlers (FeedbackObject)
	// doppelte Fehler nicht loggen
	// --> z.B.: 2 maliges Drücken von FinishOp bei IF&EC
	public void log(FeedbackObject fo) {
		if(!falseFeedbackObjects.contains(fo)) {
			falseFeedbackObjects.add(fo);
			//System.out.println(fo);
		} else {
			//System.out.println("Evaluator: Already made error. Not added.");
		}
	}
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#evaluate(mauda.OperationRecorder)
	 */
	public void evaluate(OperationRecorder or) {
		//System.out.println("Evaluator.evaluate:");
		
		maxPoints = 0.0;
		errorPoints = 0.0;
		errorCounter = 0;
		selfCorrectedErrorCounter = 0;
		
		int allErrorCounter = 0;
		
		Vector v = or.getTodoOperations();
		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--;

				maxPoints += (double)templateRating;
				// Wenn keine ausgeführt wurden				
				if(asoq.length()==0) {
					allErrorCounter++;
					// MISSING_SUBOP
					FeedbackObject fo = check(opnr,-1,null,null, FeedbackObject.INCORRECT_MISSING);
					// Fehlerpunkte nur dann, wenn es keinen Eintrag gibt
					if(fo==null) {
						errorPoints += ((double)templateRating)*weight;
						errorCounter++;
						//System.out.println("      INCORRECT_MISSING: -> "+templateRating);
					}
				}
				
				int max = Math.max(csoq.length(), asoq.length());
				SubOperation cso = null;
				SubOperation aso = null;
				subopnr = -1;
				for(int i=0; i<max; i++) {
					subopnr++;
					cso = null; aso = null;
					int r = templateRating;	// MISSING_SUBOP- und ADDITIONAL_FALSE-Rating
					if(i<csoq.length()) {
						cso = csoq.get(i);
						r = ((Integer)ratings.get(cso.getID())).intValue();
						maxPoints += (double)r;
					}
					if(i<asoq.length()) {
						aso = asoq.get(i);
						// DIFFERENT und ADDITIONAL_FALSE
						if(cso==null || !cso.equals(aso)) {
							allErrorCounter++;
							FeedbackObject fo = check(opnr,subopnr,cso,aso, FeedbackObject.INCORRECT);
							// Fehlerpunkte nur dann, wenn es keinen Eintrag gibt
							if(fo==null) {
								errorPoints += ((double)r)*weight;
								errorCounter++;
								//if(cso!=null) System.out.println("      INCORRECT: "+cso.out()+" -> "+r);
								//else System.out.println("      INCORRECT_ADDFALSE: -> "+r);
							} 
						}
					} else {
						allErrorCounter++;
						// MISSING_SUBOP
						FeedbackObject fo = check(opnr,subopnr,cso,aso, FeedbackObject.INCORRECT_MISSING);
						// Fehlerpunkte nur dann, wenn es keinen Eintrag gibt
						if(fo==null) {
							errorPoints += ((double)r)*weight;
							errorCounter++;
							//System.out.println("      INCORRECT_MISSING: -> "+r);
						} 
					}
				}
			}
		}

		// errorCounter enthält jetzt die Anzahl der Fehler
		// exklusive der Fehler in den FeedbackObjects
		
		// allErrorCounter enthält die Anzahl aller Fehler

		// In den FeedbackObjects können zusätzliche Fehler
		// drinnen sein, die oben noch nicht behandelt wurden
		
		// So jetzt die geloggten Feedback-Objekte abarbeiten
		// Damit keine Fehler doppelt gezählt werden, wurden
		// oben die Punkte bei einem bereits geloggten Eintrag
		// noch nicht negativ bewertet. Somit können wir ALLE
		// geloggten Objekte negativ bewerten
		//System.out.println("Now checking FeedbackObjects ...");
		Enumeration en = falseFeedbackObjects.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++;
					//System.out.println("      INCORRECT_MISSING: -> "+templateRating);
					break;
				case FeedbackObject.CORRECT :	// <-- für FaultMode
				case FeedbackObject.INCORRECT :
					SubOperation cso = fo.getCorrectSubOperation();
					// cso kann null sein, wenn ADDITIONAL_FALSE
					int r = templateRating;
					if(cso!=null)
						r = ((Integer)ratings.get(cso.getID())).intValue();
					errorPoints += ((double)r)*weight;
					errorCounter++;
					/*
					if(cso!=null)
						System.out.println("      INCORRECT: "+cso.out()+" -> "+r);
					else
						System.out.println("      INCORRECT_ADDFALSE: -> "+r);
					*/			
					break;
				default : break;
			}
		}	
		// errorCounter enthält jetzt die Anzahl aller gemachten
		// Fehler (also auch nicht mehr vorhandene)
		// ==> folgende Differenz ergibt die Anzahl gemachter 
		// korrigierter Fehler			
		selfCorrectedErrorCounter = errorCounter-allErrorCounter;
		
		// negative Punkte können vorkommen. Kommt auf das
		// Gewicht an.
		if(errorPoints>maxPoints) errorPoints = maxPoints;
		/*
		System.out.println("Max-Points = "+doubleRound(maxPoints));
		System.out.println("Error-Points = "+doubleRound(errorPoints));
		System.out.println("#Errors made = "+errorCounter);
		System.out.println("#Self-Corrected-Errors = "+selfCorrectedErrorCounter);
		System.out.println("END (Evaluator.evaluate)");
		*/		
	}
	
	// CHECK auf Vorhandensein eines Fehlers im Vector:
	// Wenn es zu einem Fehler ein FeedbackObject im Vector gibt,
	// dann dieses zurückgeben
	// Wird benötigt, um bei korrigierten Fehlern diesen Fehler
	// trotzdem in die Bewertung einfließen zu lassen
	private FeedbackObject check(int opnr, int subopnr, SubOperation cso, SubOperation aso, int correctness) {
		FeedbackObject fo = new FeedbackObject();
		fo.setOpNr(opnr);
		fo.setSubOpNr(subopnr);
		fo.setCorrectSubOperation(cso);
		fo.setSubOperation(aso);
		fo.setCorrectness(correctness);
		int index = falseFeedbackObjects.indexOf(fo);
		if(index<0) return null;
		return (FeedbackObject)falseFeedbackObjects.elementAt(index);
	}
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#getHTMLMessage()
	 */
	public String getHTMLMessage() {
		// Prozentual korrekte Operationen
		percent = doubleRound(((maxPoints-errorPoints) / maxPoints) * 100.0);
		String msg = "";
		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 += selfCorrectedErrorCounter+" errors of them you have corrected by your own.<br>";
		msg += doubleRound(maxPoints)+" are the maximal reachable points in this exercise.<br>";
		msg += "You have reached "+doubleRound(maxPoints-errorPoints)+" points.<br>";
		return msg;
	}

	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#save()
	 */
	public HashMap save() {
		HashMap hm = new HashMap();
		hm.put("FalseFeedbackObjects", saveFeedbackObjects(falseFeedbackObjects));
		return hm;
	}
	/* (non-Javadoc)
	 * @see mauda.feedback.Evaluator#load(java.util.HashMap)
	 */
	public void load(HashMap hm) {
		falseFeedbackObjects = loadFeedbackObjects((Vector)hm.get("FalseFeedbackObjects"));
	}
}
