/*
 * Created on Apr 18, 2005
 *
 */
package com.dragonsoft.tryapp.ejb.session.utils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import com.dragonsoft.tryapp.common.SubmissionObj;
import com.dragonsoft.tryapp.common.exceptions.ExceptionHandler;
import com.dragonsoft.tryapp.common.exceptions.TryException;
import com.dragonsoft.tryapp.ejb.entity.interfaces.SubmissionHome;
import com.dragonsoft.tryapp.support.TryFileSystem;

import fireTester.communicator.server.Server;
import fireTester.interfaces.ResultsObserver;
import fireTester.interfaces.SubmissionTest;
import fireTester.messages.AcknowledgeMessage;
import fireTester.messages.KeepAliveMessage;
import fireTester.messages.TestUnitResults;
import fireTester.messages.TesterException;

/**
 * ExecutionManager
 * This class is responsible for the observing and execution of tests
 * for the Fire system. The class is a singleton unit and exists throughout
 * test existance.
 * @author Greg
 *
 */
public class ExecutionManager implements ResultsObserver {

		private static ExecutionManager selfReference;

		private Map sub2Tests;
		private Map subID2Results;
		private Map sub2Exceptions;
		private Map subID2Sub;
		private Server theServer;
		private SubmissionHome subHome;
		
		/**
		 * This thread is responsible for the timing out of tests that 
		 * are no longer needed. The results should be removed when they are
		 * last checked however, this guarantees that if they are not checked,
		 * they do not exist indefinitely.
		 * @author Greg
		 *
		 * TimerThread
		 */
		private class TimerThread implements Runnable{
			private long millis;
			private String id;
			
			public TimerThread(String id, long mill){
				this.millis = mill;
				this.id = id;
			}
			public void run(){
				try {
					Thread.sleep(millis);
				} catch (InterruptedException e) {
					ExceptionHandler.logException(e,this);
				}
				SubmissionObj sub = (SubmissionObj)subID2Sub.get(id);
				sub2Tests.remove(sub);
				subID2Results.remove(id);
				subID2Sub.remove(id);
			}
		}
	private ExecutionManager() {
		sub2Tests = new HashMap();
		subID2Results = new HashMap();
		sub2Exceptions = new HashMap();
		subID2Sub = new HashMap();
		System.out.println("MAKING SERVER:)");
		theServer = new Server();
		System.out.println("MAde server regin");
		theServer.registerObserver(this);
		System.out.println("REGISTERed");
	}
	
	/* (non-Javadoc)
	 * @see fireTester.interfaces.ResultsObserver#receive_keep_alive(fireTester.messages.KeepAliveMessage)
	 */
	public void receive_keep_alive(KeepAliveMessage keepAlive) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see fireTester.interfaces.ResultsObserver#receive_acknowledge(fireTester.messages.AcknowledgeMessage)
	 */
	public void receive_acknowledge(AcknowledgeMessage ack) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see fireTester.interfaces.ResultsObserver#receive_unit_results(fireTester.messages.TestUnitResults)
	 */
	public void receive_unit_results(TestUnitResults r) {
		// TODO Auto-generated method stub
		SubmissionObj submissionObj = r.get_submission();
		String id = getID(submissionObj);
		List results = null;
		if(this.sub2Tests.get(submissionObj)!=null){
			if((results = (List)this.subID2Results.get(id))==null){
				results = new ArrayList();
			}
			results.add(r);
			this.subID2Results.put(id,results);
		}
		if(r.complete()){
			new Thread(new TimerThread(id,1000*60*5)).start();
		}
	}
	
	private String getID(SubmissionObj submissionObj){
		String id = submissionObj.getSubmissionID();
		
		return id;
	}

	/* (non-Javadoc)
	 * @see fireTester.interfaces.ResultsObserver#receive_test_failed(fireTester.messages.TesterException)
	 */
	public void receive_test_failed(TesterException e) {
		SubmissionObj id = null;
		id = e.get_submission();
		if(this.sub2Tests.get(id)!= null){
			this.sub2Exceptions.put(id,e);
		}
	}
	/**
	 * Gives back a reference to the singleton ExecutionManager.
	 * @return an ExecutionManager ready for action.
	 */
	public static ExecutionManager getSingleton(){
		System.out.println("HERE: START SINGLE");
		if(selfReference == null){
			selfReference = new ExecutionManager();
		}
		System.out.println("AFTER SINGLE:)");
		return selfReference;
	}
	/**
	 * 
	 * @param id
	 * @param test
	 */
	public void addSubmission(SubmissionObj id, SubmissionTest test){
			File file = null;
			try {
				file = new File(TryFileSystem.getSubmissionDirectory());
			} catch (TryException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			file = new File(file,id.getCourseID());
			file = new File(file,id.getAssignmentID());
			file = new File(file,id.getActivityID());
			file = new File(file,id.getStudentUsername());
			file = new File(file,id.getSubmissionID());
			test.set_submission(id,file);
			System.out.println("QUEUEING TEST");
			theServer.queueTest(test);
			this.sub2Tests.put(id,test);
			this.subID2Results.put(id.getSubmissionID(),null);
			this.subID2Sub.put(id.getSubmissionID(),id);
	}

	private void init(){
		try {
			Context ctx = new InitialContext();
			Object o = ctx.lookup(SubmissionHome.JNDI_NAME);
			subHome = (SubmissionHome)PortableRemoteObject.narrow(o,SubmissionHome.class);
			
		} catch (NamingException e) {
			ExceptionHandler.logException(e,this);
		}
	}

	/**
	 * @param id
	 * @return
	 */
	public TestUnitResults[] getResults(String id) throws TesterException{
		List list  = (List) this.subID2Results.get(id);
		TesterException e = null;
		if((e=(TesterException)this.sub2Exceptions.get(id)) != null){
			this.sub2Exceptions.remove(id);
			new Thread(new TimerThread(id,1000*60*5)).start();
			throw e;
		}
		TestUnitResults[] rtVal = null;
		if(list != null){
			rtVal = new TestUnitResults[list.size()];
			list.toArray(rtVal);
		}
		return rtVal;
	}
}
