package mapping;

import mapping.data.IDsQueue;
import mapping.data.Job;

/**
 * A very minor change to the FCFS_MS class which allows the cancel flags
 * to be used.
 * 
 * @author Majd Kokaly and Ben Kybartas
 * 
 */
public class NREP_MS extends MappingScheme {

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

	/**
	 * This queue is the queue of jobs. It is the essence of this policy since
	 * the jobs are served on FCFS basis.
	 */
	private IDsQueue<Long> serviceQueue;

	/**
	 * The helper thread.
	 */
	private NREP_MS_Thread nrep_ms_thread;
	
	/**
	 * The results from the job submissions
	 */
	private long result[];

	/**
	 * This final int is used to notify the helper when there is no jobs in the
	 * queue.
	 */
	final static int NO_JOBS = -100;

	/**
	 * A default constructor.
	 * 
	 * @param mapper
	 *            The mapper object
	 */
	public NREP_MS(Mapper mapper) {
		super(mapper);
		this.setDescription("NREP");

	}

	/**
	 * 
	 * In this method the queue is constructed and the NREP_MS_Thread is
	 * initialized and started.
	 * 
	 * @see MappingScheme#startMappingScheme()
	 */
	public void startMappingScheme() {
		serviceQueue = new IDsQueue<Long>();
		nrep_ms_thread = new NREP_MS_Thread(this);
		nrep_ms_thread.startThread();

	}

	/**
	 * The NREP_MS_Thread is stopped.
	 * 
	 * @see MappingScheme#stopMappingScheme()
	 */
	public void stopMappingScheme() {
		nrep_ms_thread.stopThread();
	}

	/**
	 * This method is invoked by the mapper. The job is enqueued in the service
	 * queue. It will be assigned to a server when it reaches the head of that
	 * queue.
	 * 
	 * @see MappingScheme#mapJob(Job)
	 */
	protected void mapJob(Job job) {
		serviceQueue.enqueue(job.getIndex());
	}

	/**
	 * The job is resubmitted when a timeout happens.
	 * 
	 * @see MappingScheme#handleJobTimeOut(long)
	 */
	public void handleJobTimeOut(long jobID) {
		this.getMapper().getJobsTable().setTimedOut(jobID, true);
		this.getMapper().getJobsTable().setJobServer(jobID, null);
		this.getMapper().resubmitJob(this.getMapper().getJobsTable().get(jobID));
	}

	/**
	 * NREP policy is not sensitive for availability/unavailability events, thus
	 * this method is not implemented.
	 * 
	 * @see MappingScheme#serverIsDown(int)
	 */
	public void serverIsDown(int serverID) {

	}

	/**
	 * NREP policy is not sensitive. NREP policy is not sensitive for
	 * availability/unavailability events, thus this method is not implemented.
	 * 
	 * @see MappingScheme#handleJobTimeOut(long)
	 */
	public void serverIsUp(int serverID) {

	}

	public boolean failuresOccur()
	{
		boolean fail = false;
		for (int i = 0; i < result.length; i++)
		{
			if (result[i] < 0)
			{
				fail = true;
			}
		}
		
		return fail;
	}
	/**
	 * This method is invoked by the NREP_MS_Thread object. In this method a job
	 * is sought to map it for the server with ID "serverID"
	 * 
	 * @param serverID
	 *            The available server id.
	 * 
	 * @return -1 when a failure happens, 1 otherwise.
	 */
	protected int sendJobForServer(int[] serverID) {
		long jobID;
		
		result = new long[serverID.length];
		
		if (this.serviceQueue.isEmpty())
			return NO_JOBS;

		jobID = this.serviceQueue.peak();
		
		for (int i = 0; i < serverID.length; i++)
		{
			result[i] = this.getMapper().sendJob(jobID, serverID[i]);
		}
		
		this.getMapper().setcancelFlag(true, serverID, jobID);
		if (!failuresOccur()) { // Could be sent
			this.serviceQueue.dequeue();
			return 1;
		} else {
			return -1;
		}
	}
}
