/*
 * Created on 4-mag-2004
 */
package a3.protocol;

import java.util.*;

import convFramework.domain.*;

import a3.traffic.*;
import a3.domain.*;
import a3.exceptions.*;
import a3.event.*;

import jaCop.protocol.*;
import jaCop.domain.*;

/**
 * @author Lompa
 */
public abstract class A3Task implements Runnable {
	
	/* campi */
	protected A3PacketManager packetMan = null;
	protected ConversationGraveyard convGraveyard = null;
	protected IConversationID convID = null;
	protected String community = null;
	protected A3EventListenerManager listenerMan = null;
	protected ProtocolManager jaCOPMan = null;
	
	
	private ControlQueue controlSystem = null;
	
	/* timer per l'autoterminazione */
	private Timer timer = new Timer(true);
	private TimerTask currentTerminator = null;
	protected long waitingTimeout = 40000;
	private Thread enclosingThread = null;
	
	/* costruttore ************************************************************************************/
	/**
	 * @param conv
	 * @param packetMan
	 * @param statusStore
	 * @param convGraveyard
	 */
	public A3Task(ConfigurationSet conf, A3PacketManager packetMan, ConversationGraveyard convGraveyard,
			A3EventListenerManager listenerMan, ControlQueue queue, ProtocolManager jaCOPMan) {
		this.community = conf.community;
		this.convID = conf.convID;
		this.waitingTimeout = conf.waitingTimeout;
		
		this.packetMan = packetMan;
		this.convGraveyard = convGraveyard;
		this.controlSystem = queue;
		this.jaCOPMan = jaCOPMan;
		this.listenerMan = listenerMan;
		
		this.enclosingThread = new Thread(this);
	}
	
	/* metodi per l'accesso ai campi *******************************************************************/
	/**
	 * @return la conversazione
	 */
	public IConversationID getConv() {
		return convID;
	}
	
	/* metodo per l'interruzione del client */
	public void interrupt(){
		this.enclosingThread.interrupt();
	}
	
	/* metodi per l'invio e la ricezione di messaggi */

	protected A3Packet receive()
			throws TranslationException, InterruptedException {
		/* schedulazione del task di terminazione */
		currentTerminator = new TerminatorTask(this.enclosingThread);
		timer.schedule(currentTerminator, waitingTimeout);
		/* ricezione */
		A3Packet packet = packetMan.receive(convID);
		a3.Debug.print(convID.encode()+" << "+packet.getType()+" da "+packet.getSender().getPort());
		/* se la ricezione va a buon fine nei tempi stabiliti si cancella il terminatore */
		currentTerminator.cancel();
		/*restituzione del pacchetto */
		return packet;
	}

	protected A3Packet receiveAndStart()
			throws TranslationException, InterruptedException {
		/* rilascio del controllo */
		controlSystem.releaseControl();
		/* schedulazione del task di terminazione */
		currentTerminator = new TerminatorTask(this.enclosingThread);
		timer.schedule(currentTerminator, waitingTimeout);
		/* ricezione */
		A3Packet packet = packetMan.receive(convID);
		a3.Debug.print(convID.encode()+" << "+packet.getType()+" da "+packet.getSender().getPort());
		/* se la ricezione va a buon fine nei tempi stabiliti si cancella il terminatore */
		currentTerminator.cancel();
		/* ripresa del controllo */
		controlSystem.takeControl();
		/*restituzione del pacchetto */
		return packet;
	}
	/**
	 * @param packet
	 * @throws TranslationException
	 */
	/* metodo aggiunto solo per ragioni di omogeneità */
	protected void send(A3Packet packet) throws TranslationException {
		packetMan.send(packet);
		a3.Debug.print(convID.encode()+" >> "+packet.getType()+" a "+packet.getReceiver().getPort());
	}
	
	/* metodo run ***********************************************************************************/
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run(){
		try {
			/* inizio sezione critica */
			controlSystem.takeControl();
			body();
			/* fine sezione critica */
			terminate();
		}
		catch(InterruptedException e){
			terminate();
		}
		catch(Exception e){
			e.printStackTrace();
			terminate();
		}
	}
	
	/* metodo body, da implementare ******************************************************************/
	/**
	 * @throws Exception
	 */
	/* ogni eccezione non catturata termina la conversazione corrente */
	protected abstract void body() throws Exception;
	
	/* metodo start */
	/**
	 * 
	 */
	public void start() {
		enclosingThread.start();
	}
	
	/* metodo di notifica */
	public void notifyStructureSounded(String community, COPNode sender, COPNode[] structure){
		StructureSoundedEvent event = new StructureSoundedEvent(this, community, sender, structure);
		IStructureSoundedListener[] listeners = this.listenerMan.getStructureSoundedListeners();
		for(int i = 0; i< listeners.length; i++)
			listeners[i].structureSounded(event);
	}
	
	/* metodo di terminazione */
	protected synchronized void terminate(){
		convGraveyard.bury(convID);
		if (currentTerminator != null) currentTerminator.cancel();
		controlSystem.releaseControl();
		//System.out.println("conversazione terminata");
	}
	
	/* task terminatore: interrompe il thread se è in attesa di un pacchetto */
	private static class TerminatorTask extends TimerTask {
		
		private Thread target = null;
		
		public TerminatorTask(Thread target){
			this.target = target;
		}
		
		public void run(){
			target.interrupt();
		}
	}
	
	public static class ConfigurationSet {
		
		private String community = null;
		private IConversationID convID = null;
		private int connectionRank = 2;
		private long waitingTimeout = 30000;
		
		public ConfigurationSet(String community, IConversationID convID){
			this.community = community;
			this.convID = convID;
		}
		
		/**
		 * @return Returns the connectionRank.
		 */
		public int getConnectionRank() {
			return connectionRank;
		}
		/**
		 * @param connectionRank The connectionRank to set.
		 */
		public void setConnectionRank(int connectionRank) {
			this.connectionRank = connectionRank;
		}
		/**
		 * @return Returns the waitingTimeout.
		 */
		public long getWaitingTimeout() {
			return waitingTimeout;
		}
		/**
		 * @param waitingTimeout The waitingTimeout to set.
		 */
		public void setWaitingTimeout(long waitingTimeout) {
			this.waitingTimeout = waitingTimeout;
		}
	}

}
	
	

