/*
 * 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.jedasnew.JedasPlayer;
import mauda.operation.*;

//import jedas.io.JedasPlayer;
import evalplayer.OperationPlayerThread;

import java.util.*;

/**
 * Extends Exercise by player-specific methods.
 * 
 * @author Markus Krebs
 */
public class PlayerExercise extends Exercise implements ExerciseUpdateListener {

	/**
	 * Comment for <code>operationPlayerThread</code>. Thread that performs actions
	 * (SimpleOperation or ExerciseUpdateEvent).
	 */
	public OperationPlayerThread operationPlayerThread;

	private TreeOperationView treeOperationView;
	private CommentView commentView;
	//private PlayPerformer playPerformer;
	private TimeStampRecorder timeStampRecorder;

	private JedasPlayer jedasPlayer;

	/**
	 * Creates a new PlayerExercise
	 * @param type The plugin-type, e.g. FibHeap
	 */
	public PlayerExercise(String type) {
		super(type);
		
		operationPlayerThread = null;
		
		treeOperationView = new TreeOperationView(this);
		addExerciseUpdateListener(treeOperationView);
		
		commentView = new CommentView(this);
		addExerciseUpdateListener(commentView);
		
		//playPerformer = new PlayPerformer(this);
		//addExerciseUpdateListener(playPerformer);

		timeStampRecorder = new TimeStampRecorder(this);
		timeStampRecorder.setPlayerMode(true);
		addExerciseUpdateListener(timeStampRecorder);
		
		addExerciseUpdateListener(this);
		
		jedasPlayer = null;
	}

	/**
	 * Returns the used TreeOperationView
	 * @return TreeOperationView
	 */
	public TreeOperationView getTreeOperationView() {
		return treeOperationView;
	}
	/**
	 * Returns the used CommentView
	 * @return CommentView
	 */
	public CommentView getCommentView() {
		return commentView;
	}
	
	/**
	 * Returns the used TimeStampRecorder
	 * @return TimeStampRecorder
	 */
	public TimeStampRecorder getTimeStampRecorder() {
		return timeStampRecorder;
	}

	/**
	 * Loads an exercise that was completely evaluated by a
	 * tutor.
	 * @param ht The Hash-Map-Representation of the exercise.
	 * @return true, if loading was successful, else false
	 */
	public boolean loadEvaluated(HashMap ht) {
		
		operationPlayerThread = null;

		reset();		
		
		jedasPlayer = null;

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

		setMode(metaData.getMode());
		//javax.swing.JOptionPane.showMessageDialog(MAUDA.getApplicationMainFrame(), "Mode: "+m);
	
		//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"));
		
		commentView.load((Vector)ht.get("Comments"));
		timeStampRecorder.load((Vector)ht.get("TimeStamps"));
				
		// Loading-Message
		/*
		feedback.displayMessage("TASK", 
			"<b>Loading Exercise.</b><br><br>"+
			"Please wait ..."
		);*/

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

		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.FINISHED_LOADING));
		
		// 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));
		}
				
		eue = new ExerciseUpdateEvent(this, ExerciseUpdateEvent.SPECIAL);
		eue.setMessage("FULL_LOADED");
		exOps.add(eue);
		
		exOps = addFullLoadedMessage(exOps);
		
		//System.out.println("EXOPS: "+exOps);

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

		// Jetzt Init-Operations ausführen
		commit(exOps);
		
		String jeaFilename = metaData.getEvaluatorJEAFilename();
		String path = getFilename();
		path = path.replace('\\', '/');
		path = path.substring(0, path.lastIndexOf("/")+1);
		
		loadAnimation(path + jeaFilename);
		
		// Zum Testen funktionierende Animation verwenden
		// wird nachfolgende Animation verwendet, kann man zumindest
		// das Abspielen/Springen innerhalb der Animation testen
		//loadAnimation(path + "arcobjdemo.jea");
		//loadAnimation(path + "fibheapdemo.jea");
		
		getTimeStampRecorder().calculateSeqTimeStamps();
		// Berechnete SeqTimestamps ausgeben
		// getTimeStampRecorder().printSeqTimeStamps();
		evalplayer.TimeSlider.getInstance().configure();
		
		return true;
	}

	// JEDAS-Animation laden
	protected void loadAnimation(String filename) {
		//System.out.println("Loading JEDAS-Animation: "+filename);
		
		// XXX: Fenster: Loading animation... (geht so noch nicht richtig)
		// Inhalt des Fensters wird nicht angezeigt!!! JedasPlayer blockiert alles!
		// Habe keine Lösung für dieses Problem gefunden.
		PleaseWaitFrame frame = new PleaseWaitFrame();
		// Folgendes geht leider auch nicht!
		//jedasPanel.setMessage("loading animation...");
	 		
		String[] args = new String[1];
		args[0] = filename;
		// LOAD: Animation2Explorer
		//Animation2Explorer.main(args);
		// LOAD: JedasPlayer (und DEBUGMODE)
		//JedasPlayer.DEBUGMODE = true;
		jedas.Jedas.setPlayerMode(true);
		new JedasPlayer(filename, false);
		//JedasPlayer.main(args);
		jedasPlayer = JedasPlayer.getInstance();
		Vector v = jedasPlayer.getCompPanels();
		jedasPanel.setPlayerCompPanel((jedas.CompPanel)v.firstElement());
		//System.out.println("   ... finished loading.");
		
		frame.close();
	}
	/**
	 * Returns the current JedasPlayer-Instance
	 * @return JedasPlayer
	 */
	public JedasPlayer getJedasPlayer() { return jedasPlayer; }	
	
	/* (non-Javadoc)
	 * @see mauda.Exercise#commit(mauda.operation.SimpleOperation)
	 */
	public void commit(SimpleOperation so) {
		//System.out.println("PlayerExercise.commit(SimpleOperation): "+so);
		if(jedasPlayer == null) {
			super.commit(so);
		} else {
			addToAnimationQueue(so);
			startOperationPlayer();
		}
	}
	/* (non-Javadoc)
	 * @see mauda.Exercise#commit(java.util.Vector)
	 */
	public void commit(Vector v) {
		//System.out.println("PlayerExercise.commit(Vector): "+v);
		if(jedasPlayer == null) {
			super.commit(v);
		} else {
			addToAnimationQueue(v);
			startOperationPlayer();
		}
	}
	/* (non-Javadoc)
	 * @see mauda.Exercise#stopPlaying()
	 */
	public void stopPlaying() {
		super.stopPlaying();
		// Animation auch anhalten
		if(operationPlayerThread != null)
			operationPlayerThread.stopAnimation();
	}

	private void startOperationPlayer() {
		operationPlayerThread = new OperationPlayerThread(this);
		operationPlayerThread.start();
	}

	/**
	 * Returns the state of the player.
	 * @return true if the current exercise is playing else false
	 */
	public boolean isPlaying() {
		if(operationPlayerThread == null) return false;
		return operationPlayerThread.isPlaying();
	}

	// Methode: im Prinzip aus JedasPanel.run() übernommen, nur das
	// die Operationen nicht auf der Datenstruktur ausgeführt werden
	/* (non-Javadoc)
	 * @see mauda.Exercise#animate()
	 */
	public void animate() {
		setActionsAllowed(false);

		while (opsToAnimate.size() > 0) {
			Object obj = opsToAnimate.firstElement();
			//System.out.println("PlayerExercise.animate(): Performing: "+obj);
			synchronized (opsToAnimate) {
				opsToAnimate.removeElementAt(0);
			}
			performAction(obj);

		}
		setActionsAllowed(true);
		
		// Folgende Ausgabe wird erst gemacht, wenn letzte
		// Operation ausgeführt wurde
		//System.out.println("PlayerExercise.animate(): end of execution");
		ExerciseUpdateEvent eue = new ExerciseUpdateEvent(this, ExerciseUpdateEvent.END_OF_EXECUTION);
		sendExerciseUpdateMessages(eue);
	}
	/**
	 * Performs an operation or event
	 * @param obj SimpleOperation or ExerciseUpdateEvent
	 */
	public void performAction(Object obj) {
		//System.out.println("PlayerExercise.performAction(): "+obj);
		if(obj instanceof SimpleOperation) {
			SimpleOperation op = (SimpleOperation)obj;

			OperationRecorder or = getOperationRecorder();
			TimeStampRecorder tsr = getTimeStampRecorder();
			int position = or.getCurrentOffset()-or.getOffset(0,-1);
			//System.out.println("PlayerExercise.performAction(): position = "+position);
			if(operationPlayerThread!=null) {
				operationPlayerThread.playSeqOpTimeStamp(tsr.getSeqEntry(position+1));
			} else {
				ExerciseUpdateEvent eue = new ExerciseUpdateEvent(this, ExerciseUpdateEvent.START_OF_EXECUTION);
				sendExerciseUpdateMessages(eue);
				operationExecuted(op);
			}
				
		} else if(obj instanceof ExerciseUpdateEvent) {
			ExerciseUpdateEvent event = (ExerciseUpdateEvent)obj;
			if(event.getID() == ExerciseUpdateEvent.FORWARD_ANIMATED) {
				// Bei REDO_ANIMATED nächste Operation holen und
				// ausführen. Das Problem ist, wenn ich eine
				// REDO_ANIMATED-Message schicke, wird diese Routine
				// (animate) nochmal aufgerufen, und zwar mit der
				// nächsten Operation, was zur Folge hat, das um
				// 2 nach vorne gesprungen wird
				OperationRecorder or = getOperationRecorder();
				SimpleOperation so = (SimpleOperation)or.getEntry(or.getCurrentOffset()+1);
				// Redo un-animiert ausführen
				//operationExecuted(so);
				// Redo animiert ausführen
				addToAnimationQueue(so);
			} else {
				//System.out.println("PlayerExercise: Create new Event: "+event);
				sendExerciseUpdateMessages(event);
			}
		}		
	}
	
	/* (non-Javadoc)
	 * @see mauda.ExerciseUpdateListener#exerciseUpdate(mauda.ExerciseUpdateEvent)
	 */
	public void exerciseUpdate(ExerciseUpdateEvent e) {
		if(e.getID() == ExerciseUpdateEvent.SPECIAL) {
			if(e.getMessage().equals("FULL_LOADED")) {
				// markCorrectness kann leider nicht funktionieren,
				// da die Templates auf der Datenstruktur garnicht
				// ausgeführt wurden
				//treeOperationView.markCorrectness(false, true);
				jumpToBeginning();
				treeOperationView.enableJumping();
				treeOperationView.enableStop();
				//System.out.println("OperationRecorder:\n"+getOperationRecorder());
			}
		}
	}
}
