/*
 * Created on 14-mag-2004
 */
package jaCop.protocol;

import java.util.*;

/**
 * @author Lompa
 */
public class ControlSystem {
	
	private PriorityQueue controlQueue = null;
	private Condition structureComplete = null;
	private TaskRegister register = null; 
	private ProtocolTrigger trigger = null;
	
	public ControlSystem(ProtocolTrigger trigger){
		this.trigger = trigger;
		structureComplete = new Condition();
		register = new TaskRegister();
		controlQueue = new PriorityQueue(register);
	}
	
	public void takeSystemControl(COPTask req) throws InterruptedException{
		controlQueue.takeControl(req);
	}
	
	public void releaseSystemControl(COPTask req){
		controlQueue.releaseControl(req);
	}
	
	public void waitForStructureComplete() throws InterruptedException{
		structureComplete.waitForCondition();
	}
	
	public void signalStructureComplete() {
		structureComplete.signalCondition();
	}
	
	/**
	 * @param task
	 */
	public void registerTask(COPTask task) {
		register.registerTask(task);
	}
	/**
	 * @param task
	 */
	public void unRegisterTask(COPTask task) {
		register.unRegisterTask(task);
		if (task.isClient() && !register.areThereClients(task.getCommunity()))
			trigger.noMoreClients(task.getCommunity());
	}
	

	public class PriorityQueue {
		
		private int value = 1;
		private TaskRegister reqReg = null;
		private TaskRegister atr = null;
		private COPTask currentActiveTask = null;
		
		/**
		 * @param reqReg
		 */
		public PriorityQueue(TaskRegister atr) {
			this.reqReg = new TaskRegister();
			this.atr = atr;
		}
		
		public synchronized void takeControl(COPTask req) throws InterruptedException{
			if (currentActiveTask != req){
				/* aggiungo il nodo alla lsita di quelli che stanno richiedendo il controllo */
				if (!reqReg.isThere(req)) reqReg.registerTask(req);
				/* controlli ed eventuale attesa */
				jaCop.Debug.print(req.convID.encode()+": tentativo di presa di controllo");
				jaCop.Debug.print(req.convID.encode()+": P = "+ req.isPriority()+ "; CNPT = "+req.isCurrentNPTask()+"; Altro CNPT = "+atr.isThereACurrentNPTask(req.getCommunity()));
				while ( value == 0 ||
						(!req.isPriority() && reqReg.areTherePriorityTasks(req.getCommunity())) ||
						(!req.isPriority() && !req.isCurrentNPTask() && atr.isThereACurrentNPTask(req.getCommunity()))) {
					this.wait();
					jaCop.Debug.print(req.convID.encode()+": tentativo di presa di controllo");
					jaCop.Debug.print(req.convID.encode()+": P = "+ req.isPriority()+ "; CNPT = "+req.isCurrentNPTask()+"; Altro CNPT = "+atr.isThereACurrentNPTask(req.getCommunity()));
				}
				/* controllo ottenuto */
				value = 0;
				currentActiveTask = req;
				if (!req.isPriority()) req.setCurrentNPTask(true);
				
				jaCop.Debug.print(req.convID.encode()+": controllo preso; CNPT = "+req.isCurrentNPTask());
			}
		}
		
		public synchronized void releaseControl(COPTask req){
			jaCop.Debug.print(req.convID.encode()+": " + (currentActiveTask != req ? " NON" : "") + " è il task attivo");
			if (currentActiveTask == req){
				//if (!req.isPriority() || reqReg.areTherePriorityTasks(req.getCommunity())){
					if (value == 0 ) value = 1;
					//currentActiveTask = null;
					reqReg.unRegisterTask(req);
					this.notifyAll();
					jaCop.Debug.print(req.convID.encode()+": controllo rilasciato");
					jaCop.Debug.print(req.convID.encode()+": P = "+ req.isPriority()+ "; CNPT = "+req.isCurrentNPTask()+"; Altro CNPT = "+atr.isThereACurrentNPTask(req.getCommunity()));
				//}
			}
		}
		
		public synchronized boolean isBusy(){
			return this.value == 0;
		}

	}
	
	private class Condition {
		
		public synchronized void waitForCondition() throws InterruptedException{
			this.wait();
		}
		
		public synchronized void signalCondition(){
			this.notifyAll();
		}
	}
	
	
	private class TaskRegister {
		
		private Vector tasks = new Vector();
		
		public synchronized void registerTask(COPTask task){
			tasks.add(task);
		}
		
		public synchronized void unRegisterTask(COPTask task){
			tasks.remove(task);
		}
		
		public synchronized boolean areThereClients(String community){
			for(int i = 0; i < tasks.size(); i++){
				COPTask t = (COPTask) tasks.get(i);
				if (t.getCommunity().equals(community) && t.isClient())
					return true;
			}
			return false;
		}
		
		public synchronized boolean areTherePriorityTasks(String community){
			for(int i = 0; i < tasks.size(); i++){
				COPTask t = (COPTask) tasks.get(i);
				if (t.getCommunity().equals(community) && t.isPriority())
					return true;
			}
			return false;
		}
		
		public synchronized boolean isThereACurrentNPTask(String community){
			for(int i = 0; i < tasks.size(); i++){
				COPTask t = (COPTask) tasks.get(i);
				if (t.getCommunity().equals(community) && !t.isPriority() && t.isCurrentNPTask())
					return true;
			}
			return false;
		}
		
		public synchronized boolean areThereTasks(String community){
			for(int i = 0; i < tasks.size(); i++){
				COPTask t = (COPTask) tasks.get(i);
				if (t.getCommunity().equals(community))
					return true;
			}
			return false;
		}
		
		public synchronized boolean isThere(COPTask task){
			return tasks.contains(task);
		}
		
	}

}
