/*
 * Created on 18.03.2004 15:13:09
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.evaluation;

import mauda.Exercise;
import mauda.MAUDA;
import mauda.treeoperationview.*;
import mauda.*;
import mauda.operation.*;
import mauda.utils.FileLocation;

import java.util.*;
import java.io.*;

import javax.swing.Timer;

/**
 * Extends the Exercise-Class by evaluation-specific methods.
 * 
 * @author Markus Krebs
 */
public class EvalExercise extends Exercise implements ExerciseUpdateListener {

	private TreeOperationView treeOperationView;
	private CommentView commentView;
	private TimeStampRecorder timeStampRecorder;
	
	private Timer modifiedTimer;

	/**
	 * Creates an EvalExercise
	 * @param type The plugin-type, e.g. FibHeap
	 */
	public EvalExercise(String type) {
		super(type);

		treeOperationView = new TreeOperationView(this);
		addExerciseUpdateListener(treeOperationView);
		
		commentView = new CommentView(this);
		addExerciseUpdateListener(commentView);

		timeStampRecorder = new TimeStampRecorder(this);
		addExerciseUpdateListener(timeStampRecorder);
		
		addExerciseUpdateListener(this);
		
		modifiedTimer = null;
	}
	
	/**
	 * Returns the used TreeOperationView
	 * @return TreeOperationView
	 */
	public TreeOperationView getTreeOperationView() {
		return treeOperationView;
	}
	/**
	 * Returns the used CommentView
	 * @return CommentView
	 */
	public CommentView getCommentView() {
		return commentView;
	}

	/**
	 * Loads an exercise that was completely processed by a
	 * student.
	 * @param ht The Hash-Map-Representation of the exercise.
	 * @return true, if loading was successful, else false
	 */
	public boolean loadCompleted(HashMap ht) {
		// modifiedTimer stoppen: falls aktiv (siehe exerciseUpdate(..): starten des timers)
		if(modifiedTimer!=null) modifiedTimer.stop();

		// Aufnahme-Temp-Datei löschen, falls sie exisitiert
		File file = new File(FileLocation.jedasRecordingFilename);
		if(file.exists()) file.delete();
		
		reset();

		// Meta-Daten
		metaData.load(ht.get("Meta-Data"));
		difficulty = metaData.getDifficulty();
		setDifficulty(difficulty);

		setMode(metaData.getMode());
		//javax.swing.JOptionPane.showMessageDialog(MAUDA.getApplicationMainFrame(), "Mode: "+m);
		
		// if-Zeile für Abwärtskompatibilität
		if(ht.containsKey("Ratings"))
			getOperationExecuter().getRatings().load((Vector)ht.get("Ratings"));
	
		OperationQueue init = new OperationQueue();
		OperationQueue todo = new OperationQueue();
		OperationQueue acco = new OperationQueue();
		init.load((Vector)ht.get("InitOperations"));
		todo.load((Vector)ht.get("TodoOperations"));
		acco.load((Vector)ht.get("AccomplishedOperations"));
				
		// Loading-Message
		/*
		feedback.displayMessage("TASK", 
			"<b>Loading Exercise.</b><br><br>"+
			"Please wait ..."
		);*/

		// Jetzt InitOps ausführen
		Vector exOps = new Vector();

		exOps.add(new ExerciseUpdateEvent(this, ExerciseUpdateEvent.DSOBJECT_HIDE));
		exOps.addAll((Vector)init.toVector().clone());
		
		ExerciseUpdateEvent eue = new ExerciseUpdateEvent(this, ExerciseUpdateEvent.SPECIAL);
		eue.setMessage("GO_IN_TODO-MODE");
		exOps.add(eue);

		// Event für das Löschen der Init-Operationen
		exOps.add(new ExerciseUpdateEvent(this,ExerciseUpdateEvent.CLEAR_PRECEDING));
		//exOps.add(new ExerciseUpdateEvent(this,ExerciseUpdateEvent.DSOBJECT_SHOW));
		exOps.add(new ExerciseUpdateEvent(this,ExerciseUpdateEvent.FINISHED_LOADING));
		
		//exOps.addAll((Vector)acco.toVector().clone());

		// Jetzt die von-Hand-ausgeführten Operationen aus-
		// führen
		for(int i=0; i<acco.length(); i++) {
			Operation op = acco.get(i);
			exOps.add(op);
			SubOperationQueue soq = op.getSubOperationQueue();
			for(int j=0; j<soq.length(); j++)
				exOps.add(soq.get(j));
		}
		
		exOps.add(new ExerciseUpdateEvent(this,ExerciseUpdateEvent.DSOBJECT_SHOW));
		
		exOps = addFullLoadedMessage(exOps);

		//System.out.println("EXOPS: "+exOps);

		getUndoRedo().clear();
		setDSObject(getUndoRedo().getDSObject());
		getOperationRecorder().clear();

		// Jetzt Init-Operations ausführen
		commit(exOps);
		
		metaData.setEvaluatorName("<Please enter your name!>");
		metaData.actualizeEvaluatorDate();
		metaData.setEvaluatorState("in process");
		metaData.setEvaluatorScore("<?> of 100");

		return true;
	}
	
	/* (non-Javadoc)
	 * @see mauda.ExerciseUpdateListener#exerciseUpdate(mauda.ExerciseUpdateEvent)
	 */
	public void exerciseUpdate(ExerciseUpdateEvent e) {
		if(e.getID() == ExerciseUpdateEvent.FULL_LOADED) {
			treeOperationView.markCorrectness(false, true);
			jumpToBeginning();
			treeOperationView.enableJumping();
			treeOperationView.enableStop();
			
			// modifiedTimer starten
			// Modified-Erkennung bestimmen indem getestet wird, ob eine Aufnahme
			// gestartet wird. Wurde eine gestartet, modified auf true setzen und den
			// Timer stoppen.
			final Exercise ex = this;
			modifiedTimer = new Timer(1000, new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					//System.out.print("*");
					if(jedas.Jedas.isRecording()) {
						//System.out.println("EvalExercise.loadCompleted: Timer: modified=true");
						ex.setModified(true);
						((Timer)e.getSource()).stop();
					}
				}
			});
			modifiedTimer.start();
		}
	}
	/**
	 * Saves an evaluated exercise to the given filename
	 * @param filename The filename to save the exercise
	 */
	public void saveEvaluated(String filename) {
		String jeaFilename = mauda.utils.FileLocation.jedasRecordingFilename;
		File jeaFile = new File(jeaFilename);
		if(!jeaFile.exists()) {
			// ev. Speicherung ohne Animation => Zwischenspeicherung
			javax.swing.JOptionPane.showMessageDialog(MAUDA.getApplicationMainFrame(), "No Jedas-Recording performed!");
			return;
		}
		File newFile = new File(filename+".jea");
		// Schon vorhandene Animations-Datei löschen
		if(newFile.exists()) newFile.delete();
		// Jetzt Temp-Datei umbenennen/verschieben
		//System.out.println("Copying:");
		//System.out.println("   FROM: "+jeaFile.getAbsolutePath());
		//System.out.println("   TO:   "+newFile.getAbsolutePath());
		boolean ok = copyFile(jeaFile, newFile);
		//boolean ok = jeaFile.renameTo(newFile);
		if(!ok) {
			javax.swing.JOptionPane.showMessageDialog(MAUDA.getApplicationMainFrame(), "Cant rename Jedas-Animation-File");
			return;
		}
		jeaFilename = newFile.getAbsolutePath();	// für Meta-Daten
		
		metaData.setEvaluatorState("evaluated");

		// Jedas-Dateiname ohne Pfad vermerken
		// Korrekt wäre eigentlich relativ zur Steuer-Datei, da
		// diese aber sowieso ins gleiche Verzeichnis soll,
		// kann man den check sparen.
		// => filename und jeaFilename müssen den gleichen Pfad
		//    haben!!!
		jeaFilename = jeaFilename.replace('\\', '/');
		jeaFilename = jeaFilename.substring(jeaFilename.lastIndexOf("/")+1);
		metaData.setEvaluatorJEAFilename(jeaFilename);

		metaData.inputDialog();
		
		HashMap ht = new HashMap();
		ht.put("Meta-Data", metaData.save());				

		OperationRecorder or = getOperationRecorder();
		OperationQueue acco = or.getAllTodoOperationQueue();
		ht.put("AccomplishedOperations", acco.save());
		
		ht.put("Comments", commentView.save());
		ht.put("TimeStamps", timeStampRecorder.save());
		
		Vector v = new Vector();
		v.add(ht);
		boolean result = XMLFileAccess.save(filename, v);
		if(!result) javax.swing.JOptionPane.showMessageDialog(MAUDA.getApplicationMainFrame(), "Cant save file: '"+filename+"' !\n\n"+"--> File not saved!");
		else setModified(false);
	}
	/**
	 * Copies a file from <code>src</code> to <code>dest</code>.
	 * This method is used to copy the jedas-animation-file from
	 * the default-location, to a new destination.
	 * @param src The source-file as file-object
	 * @param dest The destination-file as file-object
	 * @return true, if copying was successful, false otherwise
	 */
	private boolean copyFile(File src, File dest) {
		boolean ok = true;
		BufferedInputStream srcIn = null;
		BufferedOutputStream destOut = null;
		try {
			srcIn = new BufferedInputStream(new FileInputStream(src));
			// Da die Dateien relativ klein sind, alles in einem
			// Rutsch einlesen und schreiben
			byte[] b = new byte[srcIn.available()];
			srcIn.read(b);
			srcIn.close();
			destOut = new BufferedOutputStream(new FileOutputStream(dest));
			destOut.write(b);
			destOut.close();
		} catch(Exception ex) {
			ok = false;
		}
		if(!ok) {
			// Sicherstellen das beide Streams geschlossen sind
			try{ srcIn.close(); }
			catch(Exception ex) {}
			try{ destOut.close(); }
			catch(Exception ex) {}
		}
		return ok;
	}	
}
