/*
 * Created on 28-apr-2004
 */
package a3.traffic;

import convFramework.domain.*;
import convFramework.exceptions.*;

import jaCop.domain.*;
import a3.exceptions.*;
import a3.domain.*;

/**
 * @author Lompa
 */
public class A3PacketTranslator {
	
	/* campi */
	private IA3Encoder encoder = null;
	private IA3Decoder decoder = null;
	
	/**
	 * @param encoder
	 * @param decoder
	 */
	public A3PacketTranslator(IA3Encoder encoder, IA3Decoder decoder){
		this.encoder = encoder;
		this.decoder = decoder;
	}
	
	/**
	 * @param packet
	 * @return
	 * @throws TranslationException
	 */
	public synchronized ContentPacket translate(A3Packet packet) throws TranslationException{
		
		/* creazione pacchetto, packeID e conversationID */
		IPacketID packetID = packet.getPacketID();
		IConversationID convID = packet.getConvID(); 
		
		ContentPacket outPacket = new ContentPacket(packetID, convID);
		
		/* mittente */
		try {
			UDPNode sender = new UDPNode(packet.getSender().getAddress(), packet.getSender().getPort());
			outPacket.setSender(sender);
			
			/* destinatario */
			if (packet.getReceiver() == null)
				throw new TranslationException("Packet without servant");
				
			UDPNode receiver = new UDPNode(packet.getReceiver().getAddress(), packet.getReceiver().getPort());
			outPacket.setReceiver(receiver);
		}
		catch(convFramework.exceptions.BadInvocationArgumentException e){
			throw new TranslationException(e.getMessage());
		}
		
		/* dati di sequenza */
		outPacket.setSequence(packet.isSequence());
		outPacket.setPreviousPacket(packet.getPreviousPacket());
		
		/* dati di richiesta risposta */
		outPacket.setAutoAckRequired(true);
		
		/* contenuto */
		outPacket.setContent(encoder.encode(packet));
		
		return outPacket;
	}
	
	/**
	 * @param packet
	 * @return
	 */
	public synchronized A3Packet translate(ContentPacket packet) throws TranslationException{
		
		/* recupero dei dati */
		A3PacketData store = this.decoder.decode(packet.getContent());
		
		/* controlli */
		if (store.community == null) throw new TranslationException("Invalid packet, missing community");
		
		/* recupero dati di conversazione */
		A3Node sender = null;
		A3Node receiver = null;
		try {
			/* mittente */
			Node pSender = packet.getSender();
			sender = new A3Node(pSender.getAddress(), pSender.getPort());
			/* destinatario */
			Node pReceiver = packet.getReceiver();
			receiver = new A3Node(pReceiver.getAddress(), pReceiver.getPort());
		}
		catch(convFramework.exceptions.BadInvocationArgumentException e){
			throw new TranslationException("Invalid packet: sender or servant port out of range");
		}
		IPacketID packetID = packet.getPacketID();
		IConversationID convID = packet.getConversationID();
		IPacketID previousPacket = packet.getPreviousPacket();
		boolean hasFollowing = false;
		if (previousPacket == null && packet.isSequence())
			hasFollowing = true;
		
		/* crezione pacchetto ed inizilizzazione specifica */
		A3Packet outPacket = null; 
		/* gestione di A3 */
		if (store.type.equals(A3Stop.getTypeDescription()))
			outPacket = new A3Stop(store.community, sender, convID, packetID);
		else if (store.type.equals(A3Structure.getTypeDescription()))
			outPacket = initA3Structure(new A3Structure(store.community, sender, convID, packetID), store);
		else if (store.type.equals(A3StructureRequest.getTypeDescription()))
			outPacket = initA3StructureRequest(new A3StructureRequest(store.community, sender, convID, packetID), store);
		else
			throw new TranslationException("Invalid packet: unknown packet type");
		
		/* inizializzazione generica */
		outPacket.setReceiver(receiver);
		outPacket.follows(previousPacket);
		outPacket.setFollowed(hasFollowing);
		
		return outPacket;
		
	}
	
	private A3StructureRequest initA3StructureRequest(A3StructureRequest packet, A3PacketData store) throws TranslationException{
		/* controlli */
		if (store.node == null)
			throw new TranslationException("Invalid A3StructureRequest: requester not specified");
		
		/* inizializzazione */
		packet.setRequester(store.getNode());
		
		/* restituzione */
		return packet;
	}
	
	private A3Structure initA3Structure(A3Structure packet, A3PacketData store) throws TranslationException{
		/* controlli */
		if (store.nodeList == null)
			throw new TranslationException("Invalid A3Structure: structure not specified");
		
		/* inizializzazione */
		packet.setSenderCOPNode(store.getCOPNode());
		packet.setStructureNeighbors(store.getNodeList());
		
		/* restituzione */
		return packet;
	}
	
	/**
	 * @author Lompa
	 */
	public static class A3PacketData {
		
		private String type = null;
		private String community = null;
		private A3Node node = null;
		private COPNode copNode = null;
		private COPNode[] nodeList = null;
		
		/**
		 * 
		 */
		public A3PacketData(){}
		/**
		 * @return Returns the community.
		 */
		public String getCommunity() {
			return community;
		}
		/**
		 * @param community The community to set.
		 */
		public void setCommunity(String community) {
			this.community = community;
		}
		/**
		 * @return Returns the node.
		 */
		public A3Node getNode() {
			return node;
		}
		/**
		 * @param node The node to set.
		 */
		public void setNode(A3Node node) {
			this.node = node;
		}
		/**
		 * @return Returns the node.
		 */
		public COPNode getCOPNode() {
			return copNode;
		}
		/**
		 * @param node The node to set.
		 */
		public void setCOPNode(COPNode copNode) {
			this.copNode = copNode;
		}
		/**
		 * @return Returns the nodeList.
		 */
		public COPNode[] getNodeList() {
			return nodeList;
		}
		/**
		 * @param nodeList The nodeList to set.
		 */
		public void setNodeList(COPNode[] nodeList) {
			this.nodeList = nodeList;
		}
		/**
		 * @return Returns the type.
		 */
		public String getType() {
			return type;
		}
		/**
		 * @param type The type to set.
		 */
		public void setType(String type) {
			this.type = type;
		}
	}
	
}
