/*
 * Created on 26.02.2004 17:53:38
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.feedback.types;

import mauda.*;
import mauda.feedback.FeedbackExercise;
import mauda.feedback.FeedbackObject;
import mauda.feedback.SimpleFeedback;
import mauda.feedback.select.*;
import mauda.operation.*;

import java.util.*;

/**
 * Provides the logic for <i>Immediate Feedback and Error Correction</i>
 * 
 * @author Markus Krebs
 */
public class ImmediateFeedbackAndErrorCorrection extends SimpleFeedback {
	
	private static final int type = FeedbackSelector.IFAEC;
	
	// Schon probierte SubOperationen
	private Vector tries_vector;

	private static final String specialGetNext = "Get next ...";
	private static final String specialFinishOp = "Finish Operation ...";
	private static final String specialFinishEx = "Finish exercise ...";
	private static final String specialPerform = "Perform correct Operation...";
	
	private boolean noBackEvent;
	private boolean noKBCall;
	
	// Folgendes wird für MAX_TRIES benötigt
	// --> siehe checkMaxTries-, performCorrect-Methode
	private SimpleOperation nextCorrect;
	
	
	/**
	 * Creates an ImmediateFeedbackAndErrorCorrection
	 * @param exercise FeedbackExercise
	 */
	public ImmediateFeedbackAndErrorCorrection(FeedbackExercise exercise) {
		super(exercise);
		specialState = specialGetNext;
		
		// Initiale Bewertung
		tries_vector = new Vector();
		
		feedbackBound = Integer.MAX_VALUE;
		
	}
	
	protected void jumpEvent(int delta) {
		super.jumpEvent(delta);
		updateBackForwardButtons();
		//System.out.println("IF&EC: opDelta = "+opCounter);
		//System.out.println("       todoPosition = "+todoPosition);
		if(counter>=0) {
			OperationRecorder or = exercise.getOperationRecorder();
			SimpleOperation so = (SimpleOperation)or.getCurrentOperation();
			String msg = feedbackGenerator.genMessage(
							"information.gif",
							"<b>Jump</b> to "+so.out()); 
			if(so instanceof SubOperation) {
				msg = makeNextLink(msg);
				displayMessage("FEEDBACK", msg);
				setSpecialButtonText(specialFinishOp);
				specialState = specialFinishOp;
				// ALT
				//counter--; interactiveEvent();
				// NEU
				updateBackForwardButtons();
				FeedbackObject fo = callKB();
				if(	fo.getCorrectness() == FeedbackObject.INCORRECT) {
					noBackEvent = true;
					
					// NEU
					addToTriesVector(); noKBCall = true;
					// ALT (folgende Zeile alt)
					//noKBCall = addToTriesVector();
					
					//if(!noKBCall) noKBCall = checkMaxTries();	// muss vor undo sein
					exercise.back();
				}  
			} else if (so instanceof Operation) {
				displayMessage("FEEDBACK", msg);
				setSpecialButtonText(specialFinishOp);
				specialState = specialGetNext;
				todoPosition--;	// wird in interactiveEvent erhöht
				counter--;		// dito
				doInteractiveEvent();
			} 
			exercise.setInteractiveMode(Exercise.SUBOPERATION);
		} else {
			String msg = feedbackGenerator.genMessage(
							"information.gif",
							"<b>Jump</b> to beginning");
			displayMessage("FEEDBACK", msg);
			setSpecialButtonText(specialGetNext);
			specialState = specialGetNext;
			exercise.setInteractiveMode(Exercise.NOPOPUP);						 			  
		}
		//System.out.println("todoPosition = "+todoPosition);
		updateTaskMessage(); 
	}
	
	protected void specialPressed() {
		String msg = null;
		// EXERCISE-START
		if(specialState == specialGetNext) { // || specialState == specialFinishOp) {
			if(todoPosition>=0) checkForFailure();
			//todoPosition++;	// wird in interactiveEvent erledigt
			//counter++;		// dito
			Operation op = (Operation)todoOperationQueue.get(todoPosition+1).clone();
			op.setExecution(false);
			msg = feedbackGenerator.genMessage(
						"explanation.gif",
						"Perform the SubOperations for:");
			msg +=	"<ul><li><b>"+op.out()+"</b></li></ul>"+
					"by pressing the right mouse button in the drawing-area, and selecting the correct suboperation."+
					"<br><br>"+
					"When you have finished all suboperations, please click the <b><i>"+specialFinishOp+"</i></b> button.";
			displayMessage("FEEDBACK", msg);
			
			// Fehler im Tree markieren inklusive Folder		
			exercise.getTreeOperationView().markCorrectness(true, true);
			
			setSpecialButtonText(specialFinishOp);
			//specialState = specialFinishOp;
			exercise.setInteractiveMode(Exercise.SUBOPERATION);
			exercise.getTreeOperationView().enableJumping();

			exercise.commit(op);	// <-- löst OPERATION_EXECUTED aus --> interactiveEvent
			// -> WAIT_FOR_FIRST
			return;
		} else if(specialState == specialFinishEx) {
			finishedExercise();
			return;			
		} else if(specialState == specialPerform) {
			setBackEnabled(true);
			setForwardEnabled(true);
			exercise.setInteractiveMode(Exercise.SUBOPERATION);
			tries_vector.removeAllElements();	
			if(nextCorrect instanceof Operation) {
				specialState = specialGetNext;
				specialPressed();
				return;
			}
			exercise.commit(nextCorrect);
			nextCorrect = null;
			setSpecialButtonText(specialFinishOp);
			specialState = specialFinishOp;
			exercise.getTreeOperationView().enableJumping();
			return;
		}
		// Folgendes wenn: specialFinishOp
		finishSwitch();
	}
	protected void demandPressed() { }
	protected void forwardPressed() { exercise.forward(); }
	protected void backPressed() {
		Object actop = exercise.getOperationRecorder().getCurrentOperation(); 
		String msg;
		if(specialState == specialGetNext || specialState == specialFinishEx) {
			setSpecialButtonText(specialFinishOp);
			specialState = specialFinishOp;
			exercise.setInteractiveMode(Exercise.SUBOPERATION);
			msg = feedbackGenerator.genMessage(
					"information.gif",
					"<b>Back</b> "+specialFinishOp);
			msg = makeNextLink(msg);
			displayMessage("FEEDBACK", msg);
			return; 
		}
		exercise.back();
	}
	
	// =========================================================================
	protected void forwardEvent() {
		counter++;
		super.forwardEvent();
		updateBackForwardButtons();
		FeedbackObject fo = callKB();
		Object o = exercise.getOperationRecorder().getCurrentOperation();
		String msg;
		if(o instanceof Operation) {
			todoPosition++;
			msg = feedbackGenerator.genMessage(
						"information.gif",
						"<b>Forward</b> "+((Operation)o).out());
			msg = makeNextLink(msg);
			displayMessage("FEEDBACK", msg);
		}
		else if(o instanceof SubOperation) {
			// markCorrectness ausführen, da dies für das switchen
			// zwischen Feedback-Typen notwendig wird. Denn dann
			// ist die Markierung eventuell nicht richtig
			exercise.getTreeOperationView().markCorrectness(false, false);
			switch(fo.getCorrectness()) {
				case FeedbackObject.CORRECT :
					msg = feedbackGenerator.genMessage(
							"information.gif",
							"<b>Forward</b> "+((SubOperation)o).out());
					msg = makeNextLink(msg);
					displayMessage("FEEDBACK", msg);
					break;
				case FeedbackObject.INCORRECT :
					noBackEvent = true;
					setForwardEnabled(false);
					if(o instanceof Operation) {
						todoPosition--;
					}
					msg = feedbackGenerator.genMessage(
							"information.gif",
							"<b>Forward</b> "+((SubOperation)o).out(),
							null,
							"Because this Operation was incorrect, it was undoed.");
					// Link-Name auf vorherige Operation definieren, da ja dieser nach dem Undo aktuell ist
					OperationRecorder or = exercise.getOperationRecorder();
					Object prevop = or.getEntry(or.getOffset(or.getCurrentOperationNr(), or.getCurrentSubOperationNr())-1);
					msg = makeNextLink(msg, prevop);
					displayMessage("FEEDBACK", msg);
					exercise.back();
					break;
				case FeedbackObject.UNKNOWN :
				default :
					displayMessage("FEEDBACK", "Internal Error (redoEvent())");
					break;
			}
		}
	}
	
	// =========================================================================
	protected void backEvent() {
		counter--;
		String msg;
		if(noBackEvent) {
			//Object o = exercise.getOperationRecorder().getNextOperation();
			// o muss SubOperation (nicht Operation) sein, da nur falsche SubOperationen
			// automatisch geundoed werden.
			//msg = makeNextLink(msg);
			//displayMessage("FEEDBACK", msg);
			FeedbackObject temp = currentFeedbackObject;
			FeedbackObject fo = callKB();
			// Wenn die aktuelle korrekt ist, dann sind wir fertig,
			// ansonsten undoEvent ausführen, und nochmals zurück-
			// gehen. Dies solange machen, bis wir auf einer
			// korrekten Operation landen.
			if(fo.getCorrectness() == FeedbackObject.CORRECT) {
				currentFeedbackObject = temp;
				if(!noKBCall)
					callKB();	// <- damit currentFeedbackObject aktualisiert wird
				else noKBCall = false;
				noBackEvent = false;
				return;
			} else {
				// Undo nur machen wenn wir nicht auf einer Operation
				// stehen
				OperationRecorder or = exercise.getOperationRecorder();
				Object o = or.getCurrentOperation();
				if(o instanceof Operation) {
					currentFeedbackObject = temp;
					noKBCall = false;
					noBackEvent = false;
					return;
				} 
				// Wir befinden uns auf einer falschen SubOperation
				// => UNDO
				noBackEvent = true;
				//noKBCall = addToTriesVector();
				noKBCall = true;
				exercise.back();
				return;
			}
		}
		super.backEvent();
		updateBackForwardButtons();
		FeedbackObject fo = callKB();
		Object o = exercise.getOperationRecorder().getNextOperation();
		if(o instanceof Operation) {
			todoPosition--;
			msg = feedbackGenerator.genMessage(
					"information.gif",
					"<b>Back</b> "+((Operation)o).out());
			msg = makeNextLink(msg);
			displayMessage("FEEDBACK", msg);
		}
		else if(o instanceof SubOperation) {
			msg = feedbackGenerator.genMessage(
					"information.gif",
					"<b>Forward</b> "+((SubOperation)o).out());
			msg = makeNextLink(msg);
			displayMessage("FEEDBACK", msg);
		}		
	}
	
	// =========================================================================
	protected void interactiveEvent() {
		// Beim Replay kann aktuelle Operation sein
		OperationRecorder or = exercise.getOperationRecorder();
		SimpleOperation so = (SimpleOperation)or.getCurrentOperation(); 
		if(so instanceof Operation) {
			if(specialState != specialGetNext) {
				String msg = feedbackGenerator.genMessage(
						"information.gif",
						"<b>Execute</b> "+so.out());
				displayMessage("FEEDBACK", msg);
			}
			todoPosition++;
			updateTaskMessage();
			specialState = specialFinishOp;
			return;			
		}

		// Fehler im Tree markieren: keine Folder markieren !
		// sonst würde man MISSING_SUBOP sofort erkennen
		exercise.getTreeOperationView().markCorrectness(false, false);
		
		/*
		// commit(OpTemplate) nicht behandeln
		// Ansonsten würde Operation hier behandelt werden
		if(specialState == specialGetNext) {
			specialState = specialFinishOp;
			return;
		}*/
		updateBackForwardButtons();

		// Jetzt kann aktuelle Operation nur eine SubOperation sein
		FeedbackObject fo = callKB();
		String msg = fo.getNextMessage();
		displayMessage("FEEDBACK", msg);
		if(	fo.getCorrectness() == FeedbackObject.INCORRECT) {
			noBackEvent = true;
			
			// NEU 14.06.2004
			noKBCall = true;
			if(!addToTriesVector()) checkMaxTries();
			// ALT
			//noKBCall = addToTriesVector();
			//if(!noKBCall) noKBCall = checkMaxTries();	// muss vor undo sein
			
			// ev. Abspielen stoppen, da es sonst eine Exception
			// geben würde
			exercise.stopPlaying();	
			// configurated, da: Wenn eine Aufgabe die in Bearbeitung
			// ist geladen wurde, würde hier ein Undo gemacht würden,
			// aber nach FEEDBACK_CONFIG-Event!!! => nach dem dort
			// gemachten JumpTo würde ein Undo folgen => falsch 
			if(configurated) exercise.back();
		}
		
		FeedbackObject temp = currentFeedbackObject;
		  
		if(differentOperation) {
			cutFirstFailureFeedbackObjects();
			checkForFailure();
		}
		
		currentFeedbackObject = temp;
	}
	
	
	// =========================================================================
	
	private void finishSwitch() {
		OperationRecorder or = exercise.getOperationRecorder();
		Failure f = or.getLimitedFailure(or.getOffset(or.getCurrentOperationNr(),-1));
		if(f!=null) {
			mauda.plugin.KBFormulaEvaluator kbfe = feedbackGenerator.getKBFormulaEvaluator();
			kbfe.setFinishOperationState(true);
			FeedbackObject fo = callKB();
			kbfe.setFinishOperationState(false);
			displayMessage("FEEDBACK", fo.getNextMessage());
			//setState(CORRECT);
			evaluator.log(currentFeedbackObject);
			return;
		}
		//setState(FINISHED_OPERATION);
		exercise.setInteractiveMode(Exercise.NOPOPUP);
		String msg;
		if(todoPosition==todoOperationQueue.length()-1) {
			msg = feedbackGenerator.genMessage(
						"correct_v.gif",
						"<b>Well done.</b>",
						null,
						"You have performed all SubOperations correctly. Press <b><i>"+specialFinishEx+"</i></b> to complete the exercise.");
			displayMessage("FEEDBACK", msg);
			setSpecialButtonText(specialFinishEx);
			specialState = specialFinishEx;
		} else {
			msg = feedbackGenerator.genMessage(
						"correct_v.gif",
						"<b>Well done.</b>",
						null,
						"You have performed all SubOperations correctly. Press <b><i>"+specialGetNext+"</i></b> to process the next Operation.");
			displayMessage("FEEDBACK", msg);
			setSpecialButtonText(specialGetNext);
			specialState = specialGetNext;
		}
	}

	protected void finishedLoading() {
		super.finishedLoading();
		String msg = genOpToPerformMessage();				
		msg += getFeedbackDescription();
		msg += "<br>";
		msg += "To perform the first operation, please click the <b><i>'"+specialGetNext+"'</i></b> button below.";
		displayMessage("TASK", msg);
		setSpecialButtonText(specialGetNext);
		setSpecialEnabled(true);
	}
	protected String getFeedbackDescription() {
		return "You will get an immediate feedback after every operation, and errors are automatically undoed.";
	}
	
	protected void linkClicked(String href) {
		super.linkClicked(href);
	}
	
	private int triesVectorPosition;
	private boolean addToTriesVector() {
		
		if(triesVectorPosition != counter) 
			tries_vector.removeAllElements();
		triesVectorPosition = counter;
		
		Object actop = exercise.getOperationRecorder().getCurrentOperation();
		SimpleOperation so = (SimpleOperation)actop;
		// contains ruft "SimpleOperation.equals" auf
		if(tries_vector.contains(so)) {
			String msg = feedbackGenerator.genMessage(
					"information.gif",
					"This Operation ("+so.out()+") you already tried, and it was incorrect!");
			msg = makeNextLink(msg);
			displayMessage("FEEDBACK", msg);
			//System.out.println("Already tried SubOperation...");
			return true;
		} 
		tries_vector.add(so);
		evaluator.log(currentFeedbackObject);
		return false;
	}
	
	// Liefert TRUE zurück wenn über MAX_TRIES
	private boolean checkMaxTries() {
		if(tries_vector.size()<MAX_TRIES) return false;

		exercise.getTreeOperationView().disableJumping();
		
		OperationRecorder or = exercise.getOperationRecorder();
		Failure f = or.getLimitedFailure(or.getOffset(or.getCurrentOperationNr(),-1));
		switch(f.getDescription()) {
			case Failure.ADDITIONAL_FALSE_SUBOP :
				Operation op = (Operation)todoOperationQueue.get(todoPosition+1).clone();
				op.setExecution(false);
				nextCorrect = op;
				break;
			case Failure.DIFFERENT :
			case Failure.MISSING_SUBOP :
				Object actop = or.getCorrectSubOperation(or.getCurrentOperationNr(), or.getCurrentSubOperationNr());
				SubOperation subop = (SubOperation)actop;
				nextCorrect = subop;
		}

		setSpecialButtonText(specialPerform);
		specialState = specialPerform;
		setBackEnabled(false);
		setForwardEnabled(false);
		exercise.setInteractiveMode(Exercise.NOPOPUP);
		String msg = feedbackGenerator.genMessage("information.gif",
							"You have tried this operation for different "+MAX_TRIES+" times,"+
							"and it was allways incorrect.",
							"lamp.gif",
							"The correct Operation is<br>"+
							"<i>"+nextCorrect.out()+"</i><br><br>"+
							"Press <b><i>"+specialPerform+"</i></b> to perform this correct operation.");
		msg = makeNextLink(msg);
		displayMessage("FEEDBACK", msg);
		return true;
	}
	/* (non-Javadoc)
	 * @see mauda.feedback.SimpleFeedback#getFeedbackTypeID()
	 */
	public int getFeedbackTypeID() { return type; }

	/* (non-Javadoc)
	 * @see mauda.feedback.SimpleFeedback#save()
	 */
	public HashMap save() {
		HashMap hm = super.save();
		// tries_vector
		Vector v = new Vector();
		Enumeration en = tries_vector.elements();
		while(en.hasMoreElements()) {
			SimpleOperation so = (SimpleOperation)en.nextElement();
			if(so instanceof Operation) v.add("OPERATION "+so.save());
			else if(so instanceof SubOperation) v.add("SUBOPERATION "+so.save()); 			
		}
		hm.put("tries_vector", v);
		if(nextCorrect == null)
			hm.put("nextCorrect", "NULL");
		else if(nextCorrect instanceof Operation)
			hm.put("nextCorrect", "OPERATION "+nextCorrect.save());
		else if(nextCorrect instanceof SubOperation)
			hm.put("nextCorrect", "SUBOPERATION "+nextCorrect.save());
		return hm;
	}
	
	
	/* (non-Javadoc)
	 * @see mauda.feedback.SimpleFeedback#load(java.util.HashMap)
	 */
	public void load(HashMap hm) {
		super.load(hm);
		Vector v = (Vector)hm.get("tries_vector");
		tries_vector.removeAllElements();
		Enumeration en = v.elements();
		while(en.hasMoreElements()) {
			String s = (String)en.nextElement();
			if(s.startsWith("OPERATION ")) {
				Operation op = new Operation();
				op.load(s.substring(10));
				tries_vector.add(op);
			} else if(s.startsWith("SUBOPERATION ")) {
				SubOperation subop = new SubOperation();
				subop.load(s.substring(13));
				tries_vector.add(subop);
			}
		}
		String s = (String)hm.get("nextCorrect");
		if(s.equals("NULL")) {
			nextCorrect = null;
		} else if(s.startsWith("OPERATION ")) {
			nextCorrect = new Operation();
			nextCorrect.load(s.substring(10));
		} else if(s.startsWith("SUBOPERATION")) {
			nextCorrect = new SubOperation();
			nextCorrect.load(s.substring(13));
		}
		
		if(tries_vector.size()>=MAX_TRIES) {
			int lastIndex = tries_vector.size()-1;
			SimpleOperation so = (SimpleOperation)tries_vector.elementAt(lastIndex);
			tries_vector.removeElementAt(lastIndex);
			// letzte falsche Committen => richtige Anzeige
			exercise.commit(so);
		}
	}
}
