/*
 * Created on 20-apr-2004
 */
package convFramework.traffic;

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

import java.util.*;

/**
 * Trasmettotore: va incapsulato in un thread e lanciato come daemon
 * @author Lompa
 */
public class Transmitter implements Runnable {
	
	/* campi */
	private OutGate outGate = null;
	private OutQueue buffer = null;
	private TransmissionEventListenerManager eventListenerMan = null;
	private ReTransmissionManager reTransmissionMan = null;
	
	private TreeMap timerMap = new TreeMap();
	
	private int maxReTransmissions = 2;
	private long reTransmissionDelay = 10000;
	
	/**
	 * Crea una istanza di Transmitter
	 * @param outGate gate per l'invio dei pacchetti
	 * @param buffer buffer da cui vengono presi i pacchetti da inviaree
	 * @param reTransmissionMan il gestore da utilizzar per le ritrasmissioni
	 * @param eventListenerMan gestore dei listener dgli eventi legati alla trasmissione di pacchetti
	 */
	public Transmitter(OutGate outGate, OutQueue buffer, ReTransmissionManager reTransmissionMan, TransmissionEventListenerManager eventListenerMan){
		this.outGate = outGate;
		this.buffer = buffer;
		this.eventListenerMan = eventListenerMan;
		this.reTransmissionMan = reTransmissionMan;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		
		while(true){
			/* recupero di un pacchetto */
			Packet packet = null;
			try { packet = buffer.extractPacket(); }
			catch(InterruptedException e){ throw new CFKError("Transmitter interruption"); }

			/* controllo se richiede una risposta , se sė lo passo al gestore della ritrasmissione */
			if (packet.isAckRequired())
				reTransmissionMan.manage(packet);			
			
			/* invio il pacchetto */
			try { outGate.send(packet); }
			catch(MarshallingException e){ notifyTransmissionError(packet); }
			catch(TransmissionErrorException e){ notifyTransmissionError(packet); }
			
			//System.out.println("packet sent");
			
			/* altrimenti notifico la trasmissione di un pacchetto */
			if (!packet.isAckRequired())
				notifyPacketTransmitted(packet);
		}
		
	}
	
	/* METODI PER LA GESTIONE DI EVENTI */
	/* errore di trasmissione */
	private void notifyTransmissionError(Packet packet){
		TransmissionErrorEvent event = new TransmissionErrorEvent(this, packet);
		
		ITransmissionErrorListener[] listeners = eventListenerMan.getTransmissionErrorListeners();
		for(int i = 0; i < listeners.length; i++)
			listeners[i].transmissionError(event);
	}
	/* trasmissione di un pacchetto */
	private void notifyPacketTransmitted(Packet packet){
		if (!packet.getType().equals(AutoAckPacket.getTypeDescription())){
		PacketTransmittedEvent event = new PacketTransmittedEvent(this, packet);
		
		IPacketTransmittedListener[] listeners = eventListenerMan.getPacketTransmittedListeners();
		for(int i = 0; i < listeners.length; i++)
			listeners[i].packetTransmitted(event);
		}
	}
	
}
