package mapping.data;

import java.io.Serializable;

import java.util.GregorianCalendar;

/**
 * This class represents a job in the system. It stores information like the ID
 * of this job, submission date, etc..
 * 
 * @author Majd Kokaly
 * 
 */
public class Job implements Serializable {

	/**
	 * The serialVersionUID is used to universally identify this version of this
	 * class.
	 */
	private static final long serialVersionUID = 1166268079811990240L;

	/**
	 * A final string representing the state Queued.
	 */
	public static final String QUEUED = "Queued";

	/**
	 * A final string representing the state Running.
	 */
	public static final String RUNNING = "Running";

	/**
	 * A final string representing the state Done
	 */
	public static final String DONE = "Done";

	/**
	 * A final string representing the state Timed-Out.
	 */
	public static final String TIMEDOUT = "Timed-out";

	/**
	 * final value used to express the fact a the job has not been assigned to a
	 * server yet.
	 */
	public static final int NOT_ASSIGNED = -4000;

	/**
	 * final value used to express the fact a the job has not been completed
	 * yet.
	 */
	public static final long NOT_DONE = -6000;

	/**
	 * final value used to express the fact a the job has not been sent yet.
	 */
	public static final long NOT_SENT = -7000;

	/**
	 * The job index or the job ID.
	 */
	private long index;

	/**
	 * The job secondary id. The id given to the job by the execution layer
	 */
	private long secondaryID = NOT_ASSIGNED;

	/**
	 * The Server object that has executed this job
	 */
	private Server server;

	/**
	 * The time for this job to be completed. This is initialized after
	 * assigning the job to a server.
	 */
	private double timeExpected;

	/**
	 * The executionTime of this job. Please refer to Appendix C from the thesis
	 * document.
	 */
	private long executionTime;

	/**
	 * The id of the job class that this job belongs to.
	 * 
	 * @see JobClassesTable
	 */
	private int JobClassID;

	/**
	 * The time when this job was submitted to the Mapper
	 */
	private GregorianCalendar timeSubmitted;

	/**
	 * The time when this job was sent to the execution layer.
	 */
	private GregorianCalendar timeSent;

	/**
	 * The time when this job was started
	 */
	private GregorianCalendar timeStarted;
	
	/**
	 * The time when this job was completed
	 */
	private GregorianCalendar timeDone;
	
	/**
	 * A flag determining if this job has timed out or not 
	 */
	private boolean timedOut = false;
	
	
	/**
	 * Please note timed out is not a status. It is a property.
	 * 
	 * @return the status Job.QUEUED , Job.RUNNING or Job.DONE
	 */
	protected String getStatus() {
		if (timeDone == null && timeStarted == null && timeSent == null) {
			return Job.QUEUED;
		}
		if (timeDone == null) {
			return Job.RUNNING;
		}
		if (timeDone != null) {
			return Job.DONE;
		}
		return null; 
	}

	/**
	 * @param id
	 *            JOB ID in the system
	 * @param server
	 *            ServerRecord object that this job was sent to
	 * @param timeExpected
	 *            timeExpected for this job to be executed on the assigned
	 *            server
	 */
	public Job(int id, Server server, double timeExpected) {
		this.setIndex(id);
		this.setServer(server);
		this.setTimeExpected(timeExpected);
		this.setTimeSubmitted(new GregorianCalendar());
	}

	public Job(int id, Server server) {
		this.setIndex(id);
		this.setServer(server);
		this.setTimeExpected(timeExpected);
		this.setTimeSubmitted(new GregorianCalendar());
	}

	public Job(int id, Server server, GregorianCalendar timeSubmitted) {
		this.setIndex(id);
		this.setServer(server);
		this.setTimeExpected(NOT_ASSIGNED);
		this.setTimeSubmitted(timeSubmitted);
	}

	public Job(int index, Server server, double timeExpected, GregorianCalendar timeSubmitted) {
		this.setIndex(index);
		this.setServer(server);
		this.setTimeExpected(timeExpected);
		this.setTimeSubmitted(timeSubmitted);
	}

	public Job(int jobClassId, long executionTime) {
		this.setIndex(NOT_ASSIGNED);
		this.setServer(new Server(""));
		this.setTimeExpected(NOT_ASSIGNED);
		this.setJobClassID(jobClassId);
		this.setexecutionTime(executionTime);
	}

	// It is public
	synchronized public long getIndex() {
		return index;
	}

	protected void setIndex(long index) {
		this.index = index;
	}

	protected long getSecondaryID() {
		return secondaryID;
	}

	protected void setSecondaryID(long secondaryID) {
		this.secondaryID = secondaryID;
	}

	public long getexecutionTime() {
		return executionTime;
	}

	protected void setexecutionTime(long executionTime) {
		this.executionTime = executionTime;
	}

	protected GregorianCalendar getTimeSubmitted() {
		return timeSubmitted;
	}

	protected void setTimeSubmitted(GregorianCalendar timeSubmitted) {
		this.timeSubmitted = timeSubmitted;
	}

	protected double getTimeExpected() {
		return timeExpected;
	}

	protected void setTimeExpected(double timeExpected) {
		this.timeExpected = timeExpected;
	}

	protected Server getServer() {
		return server;
	}

	protected void setServer(Server server) {
		this.server = server;
	}

	protected int getJobClassID() {
		return JobClassID;
	}

	protected void setJobClassID(int jobClassID) {
		JobClassID = jobClassID;
	}

	protected GregorianCalendar getTimeStarted() {
		return timeStarted;
	}

	protected GregorianCalendar getTimeSent() {
		return timeSent;
	}

	protected void setTimeSent(GregorianCalendar timeSent) {
		this.timeSent = timeSent;
	}

	protected void setTimeStarted(GregorianCalendar timeStarted) {
		this.timeStarted = timeStarted;
	}

	protected GregorianCalendar getTimeDone() {
		return timeDone;
	}

	protected void setTimeDone(GregorianCalendar timeDone) {
		this.timeDone = timeDone;
	}

	protected boolean isTimedOut() {
		return timedOut;
	}

	protected void setTimedOut(boolean timedOut) {
		this.timedOut = timedOut;
	}

	public String toString() {
		String hostName = "";
		if (this.getServer() != null)
			hostName = this.getServer().getHostName();
		return "Job ( " + this.getIndex() + ", " + this.getSecondaryID() + ") Server: " + hostName
				+ ",\n Time Submitted: " + this.getTimeSubmitted().getTime().toString() +
				"  Class ID: " + this.getJobClassID();
	}

	public void print() {
		System.out.print(this.toString());
	}
	
	public long getTimeBetweenSubmissionTimeAndDoneTimeInMilliSeconds() {
		if (this.getStatus().equals(Job.DONE)) {
			return this.getTimeDone().getTimeInMillis() - this.getTimeSubmitted().getTimeInMillis();
		} else {
			return Job.NOT_DONE;
		}
	}

	public double getTimeBetweenSubmissionTimeAndDoneTimeInSeconds() {
		if (this.getStatus().equals(Job.DONE)) {
			return (this.getTimeDone().getTimeInMillis() - this.getTimeSubmitted().getTimeInMillis()) / 1000.0;
		} else {
			return Job.NOT_DONE;
		}
	}

	public double getTimeBetweenSubmissionTimeAndSentTimeInMilliSeconds() {
		if (this.getStatus().equals(Job.RUNNING) || this.getStatus().equals(Job.DONE)) {
			return (this.getTimeSent().getTimeInMillis() - this.getTimeSubmitted().getTimeInMillis());
		} else {
			return Job.NOT_SENT;
		}
	}

	public double getTimeBetweenStartTimeAndSentTimeInSeconds() {
		if (this.getStatus().equals(Job.DONE)) {
			return (this.getTimeStarted().getTimeInMillis() - this.getTimeSent().getTimeInMillis()) / 1000.0;
		} else {
			return Job.NOT_DONE;
		}
	}

}
