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

import mauda.operation.*;

import java.util.*;
/**
 * Indepented object for storing adequat feedback.
 * 
 * @author Markus Krebs
 */
public class FeedbackObject implements Comparator {

	// correctness
	/**
	 * Defines an unknown correctness
	 */
	public static final int UNKNOWN = 0;
	/**
	 * Defines that the operation is correct
	 */
	public static final int CORRECT = 1;
	/**
	 * Defines that the operation is incorrect
	 */
	public static final int INCORRECT = 2;
	/**
	 * Defines that this object is associated to the failure
	 * "missing-suboperations", what means that all performed
	 * operations are correct, but there are missing some
	 * suboperations to complete the actual operation
	 */
	public static final int INCORRECT_MISSING = 3;

	private Vector messages;
	private int correctness;
	
	private int messagePosition;
	
	private Operation op;
	private SubOperation subop;
	private SubOperation correctsubop;
	
	private int opnr;
	private int subopnr;
	
	private boolean showLocation;
	/**
	 * Create a FeedbackObject
	 */
	public FeedbackObject() {
		messages = null;
		correctness = UNKNOWN;
		messagePosition = -1;
		showLocation = false;
	}
	//public void addLeadingMessage(String msg) {
	//	messages.insertElementAt(msg, 0);
	//}
	
	/**
	 * Sets the messages
	 * @param m Vector of messages
	 */
	public void setMessages(Vector m) { messages = (Vector)m.clone(); }
	/**
	 * Gets the messages
	 * @return Vector of messages
	 */
	public Vector getMessages() { return messages; }
	
	/**
	 * Sets the operation the feedback belongs to
	 * @param op Operation
	 */
	public void setOperation(Operation op) { this.op = op; }
	/**
	 * Gets the operation the feedback belongs to
	 * @return Operation
	 */
	public Operation getOperation() { return op; }

	public void setSubOperation(SubOperation subop) { this.subop = subop; }
	public SubOperation getSubOperation() { return subop; }

	/**
	 * Sets the correct suboperation the feedback belongs to
	 * @param subop SubOperation
	 */
	public void setCorrectSubOperation(SubOperation subop) { this.correctsubop = subop; }
	/**
	 * Gets the correct suboperation the feedback belongs to
	 * @return SubOperation
	 */
	public SubOperation getCorrectSubOperation() { return correctsubop; }
	
	/**
	 * Sets the Operation-Number inside the exercise
	 * @param n Operation-Number
	 */
	public void setOpNr(int n) { opnr = n; }
	/**
	 * Gets the Operation-Number
	 * @return Operation-Number
	 */
	public int getOpNr() { return opnr; }
	
	/**
	 * Sets the SubOperation-Number inside the operation, the suboperation belongs to
	 * @param n SubOperation-Number
	 */
	public void setSubOpNr(int n) { subopnr = n; }
	/**
	 * Gets the SubOperation-Number
	 * @return SubOperation-Number
	 */
	public int getSubOpNr() { return subopnr; }
	
	/**
	 * Sets the correctness
	 * @param c correctness-ID
	 * @see mauda.feedback.FeedbackObject#UNKNOWN
	 * @see mauda.feedback.FeedbackObject#CORRECT
	 * @see mauda.feedback.FeedbackObject#INCORRECT
	 * @see mauda.feedback.FeedbackObject#INCORRECT_MISSING
	 */
	public void setCorrectness(int c) { correctness = c; }
	
	/**
	 * Gets the correctness
	 * @return correctness-ID
	 * @see mauda.feedback.FeedbackObject#setCorrectness(int)
	 */
	public int getCorrectness() { return correctness; }
	
	/**
	 * Sets the current message-position inside the messages that
	 * are associated to this FeedbackObject
	 * @param p message-position
	 */
	public void setMessagePosition(int p) {
		messagePosition = p;
	}
	/**
	 * Gets the next message. Internally there is a counter which
	 * is increased to every call to this method
	 * @return HTML-Message
	 */
	// Nächste HTML-Message zurückgeben
	public String getNextMessage() {
		if(messagePosition<messages.size()-1)
			messagePosition++;
		String msg = (String)messages.elementAt(messagePosition); 
		return makeHTML(msg);
	}
	/**
	 * Gets the previous message. Internally there is a counter
	 * which is decreased to every call to this method 
	 * @return HTML-Message
	 */
	// Vorherige HTML-Message zurückgeben
	public String getPrevMessage() {
		if(messagePosition>0) messagePosition--;
		String msg = (String)messages.elementAt(messagePosition);
		return makeHTML(msg);
	}
	
	// Feedback-Message in HTML umwandeln
	private String makeHTML(String msg) {
		switch(correctness) {
			case INCORRECT_MISSING :
				msg = genMessage("incorrect_x.gif", "<b>Incorrect</b><br>There are missing Sub-Operations", "explanation.gif", msg);
				break;
			case INCORRECT :
				msg = genMessage("incorrect_x.gif", "<b>Incorrect</b>", "explanation.gif", msg);
				break;
			case CORRECT :
				msg = genMessage("correct_v.gif", "<b>Correct</b>", "lamp.gif", msg);
				break;
			case UNKNOWN : msg = "Unknown"; break;
			default : msg = "Unknown correctness"; break;
		}
		// Jetzt noch die prev/next-links
		boolean prev = messagePosition>0;
		boolean next = messagePosition<messages.size()-1;
		if(prev || next) { 
			String plink = "";
			String nlink = "";
			if(prev) plink = "<a href='prevMessage'>prev...</a>";
			if(next) nlink = "<a href='nextMessage'>more...</a>";
			msg +=	"<table width=100%><tr><td align=left>"+plink+"</td>"+
					"<td align=right>"+nlink+"</td></tr></table>";
		}
		// Ort ausgeben
		if(showLocation) {		
			String location = "<br><b>Location:</b> ";
			location += "<i>"+op.out()+"</i>";
			if(correctness != INCORRECT_MISSING) {
				location += " / <i>"+subop.out()+"</i>";
			} else {
				location += " et seqq.";	// ff = und folgende
			}
			msg += location;
		}
		return msg;
	}
	
	/**
	 * Sets that when getNextMessage or getPrevMessage is called,
	 * that the returned HTML-message contains information about
	 * the position of the failure.
	 * E.g.: <i>Location: INSERT 7 / updatemin</i> 
	 * @param b true for displaying the location, false otherwise
	 */
	// Angabe ob Ort angezeigt werden soll
	public void setLocationShow(boolean b) { showLocation = b; }
	
	/**
	 * 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
	 */
	// 2x2-Tabellen Nachricht generieren
	// linke Spalte: images
	// rechte Spalte: Informationen
	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;
	}
	
	/* (non-Javadoc)
	 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
	 */
	// für Comparator
	// Vergleich der Positionsangaben
	// --> wird noch nicht benötigt
	public int compare(Object o1, Object o2) {
		if(o1.equals(o2)) return 0;
		if(!(o1 instanceof FeedbackObject)) return 0;
		if(!(o2 instanceof FeedbackObject)) return 0;
		FeedbackObject fo1 = (FeedbackObject)o1;
		FeedbackObject fo2 = (FeedbackObject)o2;
		if(fo1.getOpNr()<fo2.getOpNr()) return -1;
		if(fo1.getSubOpNr() < fo2.getSubOpNr()) return -1;
		return 1;
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	// für Comparator
	// ==> z.B.: Vector.contains(Object) verwendet diese Methode
	public boolean equals(Object o) {
		//System.out.println("CALL: FeedbackObject.equals");
		if(!(o instanceof FeedbackObject)) return false;
		FeedbackObject fo = (FeedbackObject)o;
		boolean nosubopcheck = false;
		if(subopnr<0 && fo.getSubOpNr()<0) nosubopcheck=true;
		if(correctness == INCORRECT_MISSING) {
			if(	fo.getCorrectness() == correctness &&
				fo.getOpNr() == opnr &&
				(fo.getSubOpNr()-1 == subopnr || nosubopcheck))
					return true;
		} else {
			if( fo.getCorrectness() == correctness &&
				fo.getOpNr() == opnr &&
				(fo.getSubOpNr() == subopnr || nosubopcheck) &&
				(fo.getCorrectSubOperation() == null || fo.getCorrectSubOperation().equals(correctsubop)) && // ==null bei ADDITIONAL_FALSE
				fo.getSubOperation().equals(subop))
					return true;
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	// Zu Testzwecken
	public String toString() {
		String s = "FEEDBACK-OBJECT:\n";
		s += "  Operation = "+op.out()+" ("+opnr+")\n";
		s += "  SubOperation = "+(subop!=null?subop.out():"null")+" ("+subopnr+")\n";
		s += "  Correct-SubOperation = "+(correctsubop!=null?correctsubop.out():"null")+"\n";
		s += "  Correctness = ";
		switch(correctness) {
			case UNKNOWN : s += "UNKNOWN"; break;
			case CORRECT : s += "CORRECT"; break;
			case INCORRECT : s += "INCORRECT"; break;
			case INCORRECT_MISSING : s += "INCORRECT_MISSING"; break;
			default : s+= "error"; break;
		}
		s += "\n";
		if(messages == null) s += "  NO MESSAGES!";
		else {
			Enumeration en = messages.elements();
			while(en.hasMoreElements())
				s += "  Message: "+en.nextElement();
		}
		return s;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	public Object clone() {
		//System.out.println("FeedbackObject.clone() called");
		FeedbackObject nfo = new FeedbackObject();
		nfo.load(save());
		return nfo;
	}

	/**
	 * Converts this FeedbackObject in a HashMap-representation for
	 * saving-purposes.
	 * @return HashMap-representation
	 */
	// Speichern / Laden
	public HashMap save() {
		HashMap hm = new HashMap();
		hm.put("Messages", messages);
		hm.put("Correctness", new Integer(correctness));
		hm.put("MessagePosition", new Integer(messagePosition));
		hm.put("Operation", op.save());
		
		if(subop!=null) hm.put("SubOperation", subop.save());
		else hm.put("SubOperation", "null");
		
		if(correctsubop !=null) 
			hm.put("CorrectSubOperation", correctsubop.save());
		else
			hm.put("CorrectSubOperation", "null");
			
		hm.put("OperationNr", new Integer(opnr));
		hm.put("SubOperationNr", new Integer(subopnr));
		hm.put("ShowLocation", new Boolean(showLocation));
		return hm;
	}
	/**
	 * Loads this FeedbackObject with the parameters defined in the
	 * deliverd HashMap-representation
	 * @param hm HashMap-representation
	 */
	public void load(HashMap hm) {
		messages = (Vector)hm.get("Messages");
		correctness = ((Integer)hm.get("Correctness")).intValue();
		messagePosition = ((Integer)hm.get("MessagePosition")).intValue();
		op = new Operation();
		op.load((String)hm.get("Operation"));
		
		String s = (String)hm.get("SubOperation");
		if(!s.equals("null")) {
			subop = new SubOperation();
			subop.load(s);
		} else subop = null;
		
		s = (String)hm.get("CorrectSubOperation");
		if(!s.equals("null")) {
			correctsubop = new SubOperation();
			correctsubop.load(s);
		} else correctsubop = null;
		
		opnr = ((Integer)hm.get("OperationNr")).intValue();
		subopnr = ((Integer)hm.get("SubOperationNr")).intValue();
		showLocation = ((Boolean)hm.get("ShowLocation")).booleanValue();
	}
}
