/*
 * Created on 24-apr-2004
 */
package convFramework.facade;

import convFramework.domain.*;
import convFramework.traffic.*;
import convFramework.exceptions.*;
import java.net.*;

/**
 * @author Lompa
 */
public class Loader {
	
	/**
	 * Carica il coversationframework
	 * @param conf il set di configurazioni
	 * @return un TrafficManager per l'uso del framework
	 */
	public static TrafficManager loadFramework(ConfigurationSet conf) throws LoadingException {
		try {
			DatagramSocket inSocket = new DatagramSocket(conf.inPort, InetAddress.getLocalHost());
			DatagramSocket outSocket = inSocket;
			if (conf.outPort != conf.inPort)
				outSocket = new DatagramSocket(conf.outPort, InetAddress.getLocalHost());
			
			UDPNode currentNode = new UDPNode(InetAddress.getLocalHost(), conf.inPort);
			
			BasicPacketIDFactory packetIDFactory = new BasicPacketIDFactory();
			BasicConversationIDFactory convIDFactory = new BasicConversationIDFactory();
			
			IMarshaller marshaller = new XMLMarshaller();
			IUnMarshaller unMarshaller = new XMLUnMarshaller(packetIDFactory, convIDFactory);
			
			InGate inGate = new InGate(currentNode, inSocket, unMarshaller, 65536);
			OutGate outGate = new OutGate(currentNode, outSocket, marshaller);
			
			ReceptionEventListenerManager recptEvMan = new ReceptionEventListenerManager(); 
			TransmissionEventListenerManager transEvMan = new TransmissionEventListenerManager();
			
			OutQueue outQueue = new OutQueue();
			PacketDispatcher dispatcher = new PacketDispatcher();
			
			ConversationManager convMan = new ConversationManager(dispatcher);
			
			ReTransmissionManager reTransMan = new ReTransmissionManager(outGate, transEvMan, conf.getMaxRetransmissiosNumber(), conf.getReTransmissionTimeout());
			recptEvMan.addAckArrivedListener(reTransMan);
			transEvMan.addMissingAckListener(reTransMan);
			
			AutoAckManager ackMan = new AutoAckManager(packetIDFactory, outQueue, currentNode);
			
			Thread receiver = new Thread(new Receiver(inGate, dispatcher, convMan, recptEvMan, reTransMan, ackMan));
			receiver.setDaemon(true);
			receiver.setName("servant");
			receiver.start();
			
			Thread transmitter = new Thread(new Transmitter(outGate, outQueue, reTransMan, transEvMan));
			transmitter.setDaemon(true);
			transmitter.setName("transmitter");
			transmitter.start();
			
			TrafficManager trafficMan = new TrafficManager(dispatcher, outQueue, recptEvMan, transEvMan, convMan, convIDFactory, packetIDFactory, currentNode);
			return trafficMan;
		}
		catch(InstantiationRuleBrokenException e){
			throw new LoadingException(e.getMessage());
		}
		catch(SocketException e){
			throw new LoadingException(e.getMessage());
		}
		catch(UnknownHostException e){
			throw new LoadingException(e.getMessage());
		}
		catch(BadInvocationArgumentException e){
			throw new LoadingException(e.getMessage());
		}
	}
	
	/**
	 * @return
	 * @throws BadInvocationArgumentException
	 */
	public static ConfigurationSet newConfigurationSet(){
		return new ConfigurationSet();
	}
	
	/**
	 * @author Lompa
	 */
	public static class ConfigurationSet {
		
		private int inPort = 3337;
		private int outPort = 3337;
		private int maxRetransmissiosNumber = 2;
		private long reTransmissionTimeout = 10000;
		
		private ConfigurationSet(){
		}
		
		/**
		 * @return Returns the maxRetransmissiosNumber.
		 */
		public int getMaxRetransmissiosNumber() {
			return maxRetransmissiosNumber;
		}
		/**
		 * @param maxRetransmissiosNumber The maxRetransmissiosNumber to set.
		 */
		public void setMaxRetransmissiosNumber(int maxRetransmissiosNumber) {
			this.maxRetransmissiosNumber = maxRetransmissiosNumber;
		}
		/**
		 * @return Returns the reTransmissionTimeout.
		 */
		public long getReTransmissionTimeout() {
			return reTransmissionTimeout;
		}
		/**
		 * @param reTransmissionTimeout The reTransmissionTimeout to set.
		 */
		public void setReTransmissionTimeout(long reTransmissionTimeout) {
			this.reTransmissionTimeout = reTransmissionTimeout;
		}
		/**
		 * @return Returns the inPort.
		 */
		public int getInPort() {
			return inPort;
		}
		/**
		 * @param inPort The inPort to set.
		 */
		public void setInPort(int inPort) {
			this.inPort = inPort;
		}
		/**
		 * @return Returns the outPort.
		 */
		public int getOutPort() {
			return outPort;
		}
		/**
		 * @param outPort The outPort to set.
		 */
		public void setOutPort(int outPort) {
			this.outPort = outPort;
		}
	}	
	
}
