/*
 * Created on 31.01.2004 14:24:10
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.feedback;

import mauda.*;
import mauda.operation.*;
import mauda.plugin.KBFormulaEvaluator;

import java.util.*;

/**
 * Central class for generating feedback. 
 * 
 * @author Markus Krebs
 */
public class FeedbackGenerator implements ExerciseUpdateListener {
	
	private Exercise exercise;

	private FeedbackKB feedbackKB;

	private static KBFormulaEvaluator kbFormulaEvaluator;	// wird per PlugInHandler geladen
	
	/**
	 * Creates a FeedbackGenerator
	 * @param exercise Exercise
	 */
	public FeedbackGenerator(Exercise exercise) {
		this.exercise = exercise;
		
		exercise.addExerciseUpdateListener(this);
		
		loadKB();		
	}
	
	/* (non-Javadoc)
	 * @see mauda.ExerciseUpdateListener#exerciseUpdate(mauda.ExerciseUpdateEvent)
	 */
	public void exerciseUpdate(ExerciseUpdateEvent e) {
		if(e.getID() == ExerciseUpdateEvent.PLUGIN_CHANGED) {
			loadKB();
		}
	}
	
	/**
	 * Loads the plugin-specific knowledge-base
	 */
	public void loadKB() {
		feedbackKB = new FeedbackKB();
		String filename = exercise.getDataDirectory()+"feedbackkb.txt";
		boolean ok = feedbackKB.load(filename);
		if(!ok) {
			javax.swing.JOptionPane.showMessageDialog(MAUDA.getApplicationMainFrame(), "Error while loading Feedback-Knowledge-Base:\n"+filename);
			return;
		}
	}
	/**
	 * Sets the plugin-specific KBFormualEvaluator. E.g. FibHeap:
	 * FibHeapKBFormualEvaluator
	 * @param kbfe KBFormualEvaluator
	 */
	public static void setKBFormulaEvaluator(KBFormulaEvaluator kbfe) {
		kbFormulaEvaluator = kbfe;
	}
	/**
	 * Gets the KBFormulaEvaluator
	 * @return KBFormualEvaluator
	 * @see mauda.feedback.FeedbackGenerator#setKBFormulaEvaluator(KBFormulaEvaluator)
	 */
	public KBFormulaEvaluator getKBFormulaEvaluator() {
		return kbFormulaEvaluator;
	}
	
	/**
	 * Gets a FeedbackObject, to the current position inside the
	 * exercise.
	 * @return FeedbackObject
	 */
	public FeedbackObject getFeedback() {
		OperationRecorder or = exercise.getOperationRecorder();
		int opnr = or.getCurrentOperationNr();
		int subopnr = or.getCurrentSubOperationNr();
		Operation op = or.getOperation(opnr);
		SubOperation so1 = or.getSubOperation(opnr, subopnr);
		SubOperation so2 = or.getCorrectSubOperation(opnr, subopnr);
		
		FeedbackObject fo = new FeedbackObject();
		fo.setCorrectness(FeedbackObject.UNKNOWN);
		fo.setOperation(op);
		fo.setSubOperation(so1);
		fo.setCorrectSubOperation(so2);
		fo.setOpNr(opnr);
		fo.setSubOpNr(subopnr);

		if(subopnr>0) {
			// Folgefehler --> kein Feedback verfügbar
			SubOperation pso1 = or.getSubOperation(opnr, subopnr-1);
			SubOperation pso2 = or.getCorrectSubOperation(opnr, subopnr-1);
			if(pso2==null || !pso1.equals(pso2)) {
				fo.setCorrectness(FeedbackObject.INCORRECT);
				Vector messages = new Vector();
				messages.add("No feedback for aftereffects available");
				fo.setMessages(messages);
				return fo;
			}
		}

		if(so1 == null) {
			fo.setCorrectness(FeedbackObject.INCORRECT_MISSING);
			Vector messages = new Vector();
			messages.add("You have to perform SubOperations by right-click in the drawing pane!");
			fo.setMessages(messages);
			return fo;
		}

		Vector fkbos = null;
		//if(so2 == null) kbFormulaEvaluator.setFinishOperationState(true);

		fkbos = feedbackKB.call(kbFormulaEvaluator);

		//if(so2 == null) kbFormulaEvaluator.setFinishOperationState(false);
				
		// Im Moment wird nur erstes zutreffendes FeedbackKBObject genommen
		// Normalerweise sollten die Formeln disjunkt sein, d.h.
		// es dürfte keine mehreren zutreffenden geben!
		Vector messages = new Vector();
		FeedbackKBObject fkbo = null;
		int copnr = or.getCurrentOperationNr();
		int csubopnr = or.getCurrentSubOperationNr();
		if(fkbos!=null&&!fkbos.isEmpty()) {
			fkbo = (FeedbackKBObject)fkbos.firstElement();
			messages = (Vector)fkbo.getMessages().clone();
		} else if(kbFormulaEvaluator.isFinishOperationState()) {
			// kein Feedback zu MISSING_SUBOPERATIONS verfügbar
			SubOperation so = or.getCorrectSubOperation(copnr, csubopnr+1);
			messages.add("Think about what you can have forgotten!");
			messages.add("Missing <i>"+so.getID().toLowerCase()+"</i>.");
		}
		else {
			messages.add("Dont know what you are doing.");	// <-- kein Feedback zur aktuellen Aktion verfügbar
		}
		
		// Ersetzungen machen
		for(int i=0; i<messages.size(); i++) {
			String msg = (String)messages.elementAt(i);
			messages.setElementAt(makeSubstitutions(msg), i);
		}
		/*
		// Feedback: getFailure-Aufruf
		Failure f = or.getFailure(or.getOffset(copnr,-1));
		if(f!=null) {
			if(f.getDescription() != Failure.MISSING_SUBOP)
				javax.swing.JOptionPane.showMessageDialog(MAUDA.getApplicationMainFrame(), "FAILURE:\n"+f.getDescriptionString());
		}*/
		
		// FeedbackObject generieren und instanziieren
		fo.setCorrectness(FeedbackObject.UNKNOWN);
		
		if(kbFormulaEvaluator.isFinishOperationState()) {
			fo.setCorrectness(FeedbackObject.INCORRECT_MISSING);			
		} else if(so1!=null&&so2!=null&&so1.equals(so2)) {
			fo.setCorrectness(FeedbackObject.CORRECT);
		} else {
			fo.setCorrectness(FeedbackObject.INCORRECT);
		}
		fo.setMessages(messages);
		return fo;	
	}
	private String makeSubstitutions(String msg) {
		OperationRecorder or = exercise.getOperationRecorder();
		int copnr = or.getCurrentOperationNr();
		if(copnr<0) return msg;
		int csubopnr = or.getCurrentSubOperationNr();
		Operation op = or.getOperation(copnr);
		SubOperation subop = or.getSubOperation(copnr, csubopnr);
		SubOperation csubop = or.getCorrectSubOperation(copnr, csubopnr);

		Vector k = new Vector();
		Vector v = new Vector();
		if(csubopnr>=0) {
			// Referenz auf korrekte SubOperation
			if(csubop!=null) {
				k.add("CORRECTSUBOPERATIONID"); v.add(csubop.getID().toLowerCase());
				k.add("CORRECTSUBOPERATION"); v.add(csubop.out());
				k.add("CORRECTSUBOPPARAM1"); v.add(csubop.getParameter1()+"");
				k.add("CORRECTSUBOPPARAM2"); v.add(csubop.getParameter2()+"");
			} else {
				String s = "'Next Operation'";
				k.add("CORRECTSUBOPERATIONID"); v.add(s);
				k.add("CORRECTSUBOPERATION"); v.add(s);
				k.add("CORRECTSUBOPPARAM1"); v.add(s);
				k.add("CORRECTSUBOPPARAM2"); v.add(s);				
			}
			// Referenz auf durchgeführte SubOperation
			k.add("SUBOPERATIONID"); v.add(subop.getID().toLowerCase());
			k.add("SUBOPERATION"); v.add(subop.out());
			k.add("SUBOPPARAM1"); v.add(subop.getParameter1()+"");
			k.add("SUBOPPARAM2"); v.add(subop.getParameter2()+"");
		}
		k.add("OPERATIONID"); v.add(op.getID());
		k.add("OPERATION"); v.add(op.out());
		k.add("OPPARAM1"); v.add(op.getParameter1()+"");
		k.add("OPPARAM2"); v.add(op.getParameter2()+"");
		for(int i=0; i<k.size(); i++) {
			String key = (String)k.elementAt(i);
			String value = (String)v.elementAt(i);
			msg = msg.replaceAll(key, "<i>"+value+"</i>");
		}
		return msg;
	}
	/**
	 * Generates a 2x2 table-HTML-Message from the given parameters 
	 * @param img1 The image that should be displayed in the upper-left-cell of the message
	 * @param txt1 The HTML-text that should be displayed in the upper-right-cell of the message
	 * @param img2 The image that should be displayed in the lower-left-cell of the message
	 * @param txt2 The HTML-text that should be displayed in the upper-right-cell of the message
	 * @return A HTML-Message
	 */
	public String genMessage(String img1, String txt1, String img2, String txt2) {
		String msg =	"<table border=0 cellpadding=1><tr>"+
						"<td valign=top>";
		if(img1!=null) msg += "<img src='./images/"+img1+"'>";
		msg += "</td><td>"+txt1+"</td></tr>";
		if(img2!=null || txt2!=null) {
			msg += "<tr><td valign=top>";
			if(img2!=null) msg += "<img src='./images/"+img2+"'>";
			msg += "</td><td>"+txt2+"</td></tr>";
		}
		msg += "</table>";
		//System.out.println("FeedbackGenerator.genMessage:\n"+msg);
		return msg;
	}
	/**
	 * Generates a 1x2 table-HTML-Message from the given parameters
	 * @param img1 The image that should be displayed in the left-cell of the message
	 * @param txt1 The HTML-text that should be displayed in the right-cell of the message
	 * @return A HTML-Message
	 */
	public String genMessage(String img1, String txt1) {
		return genMessage(img1, txt1, null, null);
	}
}



