package mapping.data;

import java.io.Serializable;
import java.util.Hashtable;
import adjusting.availability_adjusting.FailureTrace;

/**
 * A server objects resembles a node in the desktop grid.
 * 
 * @author Majd Kokaly
 */
public class Server implements Serializable {

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

	/**
	 * The hostname of that server.
	 */
	private String hostName;

	/**
	 * Number of current active jobs.
	 */
	private int numberOfActiveJobs = 0;

	/**
	 * Estimated time when this server will become free
	 */
	private double timeToBeTaskFree;

	/**
	 * A table of processing rates.
	 */
	private Hashtable<Integer, ProcessingRatesPair> processingRates;

	/**
	 * aj. Please refer to the thesis document (Section Availability Model).
	 */
	private double availabilty = 1.0;

	/**
	 * To keep track of that aj was before going down.
	 */
	private double previousAvailability = 1.0;

	/**
	 * The id or index of that server. It is the order of this server in the mue
	 * matrix. Please refer to the thesis document (Section Workload Model).
	 */
	private int index;

	/**
	 * A boolean flag is set to true when the server is down and set true
	 * otherwise.
	 */
	private boolean down = false;

	/**
	 * The failure trace of this Server message.
	 */
	private FailureTrace failureTrace;

	/**
	 * The configuration message sent to this server.
	 */
	private MessageToServer lastMessageSent;

	public Server(String hostName) {
		this.setHostName(hostName);
		this.setProcessingRates(new Hashtable<Integer, ProcessingRatesPair>());
		this.setFailureTrace(new FailureTrace());
		this.setLastMessageSent(new MessageToServer());
	}

	public double getTimeToBeTaskFree() {
		return timeToBeTaskFree;
	}

	protected void setTimeToBeTaskFree(double timeToBeTaskFree) {
		this.timeToBeTaskFree = timeToBeTaskFree;
	}

	public String getHostName() {
		return hostName;
	}

	public void setHostName(String hostName) {
		this.hostName = hostName;
	}

	synchronized public int getNumberOfActiveJobs() {
		return numberOfActiveJobs;
	}

	private void setNumberOfActiveJobs(int numberOfActiveJobs) {
		this.numberOfActiveJobs = numberOfActiveJobs;
	}

	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	protected void print() {
		System.out.print("Host Name: " + this.getHostName() + ", busy for : " + this.getTimeToBeTaskFree()
				+ " time units.");
	}
	
	public void setAvailabilty(double availabilty) {
		this.availabilty = availabilty;
	}

	public double getAvailability() {
		return this.availabilty;
	}

	public double getPreviousAvailability() {
		return previousAvailability;
	}

	public void setPreviousAvailability(double previousAvailability) {
		this.previousAvailability = previousAvailability;
	}

	public FailureTrace getFailureTrace() {
		return failureTrace;
	}

	public void setFailureTrace(FailureTrace failureTrace) {
		this.failureTrace = failureTrace;
	}

	protected boolean isDown() {
		return down;
	}

	protected void setDown(boolean down) {
		if (down) {
			// to store the availability when it comes back up again
			this.setPreviousAvailability(getAvailability());
			this.setAvailabilty(0.0);
		} else {
			if (this.down) {
				this.setAvailabilty(getPreviousAvailability());
			}
		}
		this.down = down;
	}

	public MessageToServer getLastMessageSent() {
		return lastMessageSent;
	}

	public void setLastMessageSent(MessageToServer lastMessageSent) {
		this.lastMessageSent = lastMessageSent;
	}

	protected void printprocessingRates() {
		System.out.print(" { ");
		System.out.print(processingRates.toString());
		System.out.print(" }");
	}

	public Hashtable<Integer, ProcessingRatesPair> getProcessingRates() {
		return processingRates;
	}

	public void setProcessingRates(Hashtable<Integer, ProcessingRatesPair> processingRate) {
		this.processingRates = processingRate;
	}

	/**
	 * @param pair
	 *            Is the processing rate pair for this Server.
	 * 
	 * @param jobClassID
	 *            Is the jobClassID for which the processing rates are.
	 */

	synchronized public void addProcessingRate(ProcessingRatesPair pair, int jobClassID) {
		this.getProcessingRates().put(jobClassID, pair);
	}

	/**
	 * @param realRate
	 *            Is the actual processing rate for this Server to execute a job
	 *            from job class with ID jobClassID.
	 * @param assumedRate
	 *            Is the assumed processing rate for this Server to execute a
	 *            job from job class with ID jobClassID.
	 * @param jobClassID
	 *            Is the ID of the job class for which the pair of processing
	 *            rate is.
	 */
	synchronized public void addProcessingRate(double realRate, double assumedRate, int jobClassID) {
		this.getProcessingRates().put(jobClassID, new ProcessingRatesPair(realRate, assumedRate));
	}

	/**
	 * 
	 * @param jobClassID
	 *            is the job class in subject.
	 * @return The processing rates pair for that job class.
	 */
	synchronized public ProcessingRatesPair getProcessingRate(int jobClassID) {
		return ((ProcessingRatesPair) this.getProcessingRates().get(jobClassID));
	}

	/**
	 * 
	 * @param jobClassID
	 *            is the job class in subject.
	 * @return The assumed for that job class.
	 */
	synchronized public double getAssumedRate(int jobClassID) {
		return ((ProcessingRatesPair) this.getProcessingRates().get(jobClassID)).getAssumedRate();
	}

	/**
	 * 
	 * @param jobClassID
	 *            is the job class in subject.
	 * @return The actual processing rate for that job class.
	 */
	synchronized public double getRealRate(int jobClassID) {
		return ((ProcessingRatesPair) this.getProcessingRates().get(jobClassID)).getRealRate();
	}

	/**
	 * This method increments the number of active jobs by one. This method is
	 * thread safe.
	 */
	synchronized public void incrementActiveJobsNumber() {
		this.setNumberOfActiveJobs(this.getNumberOfActiveJobs() + 1);
	}

	/**
	 * This method decrements the number of active jobs by one. This method is
	 * thread safe.
	 */
	synchronized public void decrementActiveJobsNumber() {
		this.setNumberOfActiveJobs(this.getNumberOfActiveJobs() - 1);
		if (this.getNumberOfActiveJobs() < 0) {
			this.setNumberOfActiveJobs(0);
		}
	}

	/**
	 * This method sets the number of active jobs to zero. This method is thread
	 * safe.
	 */
	synchronized protected void clearActiveJobsNumber() {
		this.setNumberOfActiveJobs(0);
	}

	public String toString() {
		return "Server (" + this.getIndex() + ", " + this.getHostName() + ")\n\tNumber of Active Tasks: "
				+ getNumberOfActiveJobs() + "\n\tTime to be task free: " + getTimeToBeTaskFree()
				+ "\n\tAvailabilty: " + this.getAvailability() + "\n\tProcessing Rates: "
				+ this.getProcessingRates().toString() + "\n";
	}

}
