package javax.safetycritical;

/**
 * This class is a two dimension set, which contains pairs of clock and process. 
 * The infrastructure will use this class to model the wait set and lock set.
 */
class PriorityQueueForLockAndWait {
	int[] queue;
	int tail;
	int queueSize;

	PriorityQueueForLockAndWait(int size) {
		queue = new int[size];
		tail = -1;
		queueSize = 0;

		makeEmptyQueue(queue);
	}

	private void makeEmptyQueue(int[] set) {
		for (int i = 0; i < set.length; i++)
			set[i] = -999;
	}

	/**
	 * Add a relation between the lock and the process into the set.
	 * 
	 * @param target
	 *            The lock associate with the process
	 * @param process
	 *            The process
	 */
	protected void addProcess(Object monitor, ScjProcess process) {
		vm.ClockInterruptHandler.instance.disable();

		if (tail < queue.length - 1) {
			tail++;
			int index = tail;
			// find the place in the set for this process
			for (int i = 0; i < tail; i++) {
				ScjProcess temp = getScjProcess(queue[i]);
				if (temp == null)
					throw new IllegalArgumentException("1");

				if (ManagedSchedMethods.getPriorityParameter(process.getTarget()).getPriority() > ManagedSchedMethods
						.getPriorityParameter(temp.getTarget()).getPriority()) {
					index = i;
					break;
				}
			}
			// reserve the place in the set for this process
			if (index != tail) {
				for (int i = tail; i > index; i--) {
					queue[i] = queue[i - 1];
				}
			}
			// add the index of the process into the set and set the required lock
			process.monitorLock = monitor;
			queue[index] = process.index;
			queueSize++;
		} else {
			throw new IndexOutOfBoundsException("set: too small");
		}
		//print();
		vm.ClockInterruptHandler.instance.enable();
	}

	/**
	 * Get the first process who needs to lock. 
	 * Rules: 
	 * 1. The highest priority process who needs the lock will be returned. 
	 * 2. If there are more than one processes who have the same priority, then FIFO.
	 * 
	 * @param target
	 *            The lock
	 * @return The process who needs to lock.
	 */
	protected/*synchronized*/ScjProcess getNextProcess(Object monitor) {
		for (int i = 0; i <= tail; i++) {
			ScjProcess process = getScjProcess(queue[i]);
			if (process.monitorLock == monitor) {
				process.monitorLock = null;
				reorderSet(i);
				queueSize--;
				return process;
			}
		}
		return null;
	}

	public/*synchronized*/void removeProcess(ScjProcess process) {
		for (int i = 0; i <= tail; i++) {
			if (queue[i] == process.index) {
				reorderSet(i);
				process.monitorLock = null;

				queueSize--;
			}
		}
	}

	private void reorderSet(int index) {
		for (int i = index; i <= tail - 1; i++) {
			queue[i] = queue[i + 1];
		}
		queue[tail] = -999;
		tail--;
	}

	private ScjProcess getScjProcess(int processIdx) {
		if (processIdx == -999) {
			return null;
		}
		if (processIdx == -2) {
			return PriorityScheduler.instance().outerMostSeqProcess;
		}
		if (processIdx == -1) {
			return ScjProcess.idleProcess;
		}

		return (ScjProcess)Mission.missionBehaviour.getProcess(processIdx);
	}

	/**
	 * For testing purpose.
	 */
	public void print() {
		devices.Console.println("queue size = " + (tail + 1));
		//		for (int i = 0; i <= tail; i++) {
		//			ScjProcess temp = getScjProcess(queue[i]);
		//			if (temp != null)
		//				devices.Console.println(temp.print());
		//		}

		for (int i = 0; i < queue.length; i++) {
			devices.Console.print("[ " + queue[i] + " ] ");
		}
		devices.Console.println("");
	}
}