/**
 *  Adapter control port implementation for dynamic interactions with Test Adapter
 *  
 *  @author     ETSI / STF424
 *  @version    $URL$
 *              $Id$
 *
 */
package org.etsi.its.adapter.ports;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.logging.Logger;

import org.etsi.adapter.TERFactory;
import org.etsi.common.ByteHelper;
import org.etsi.its.adapter.Management;
import org.etsi.its.codec.TestAdapterMessageTypeEnum;
import org.etsi.its.codec.adapter.CodecsFactory;
import org.etsi.its.codec.adapter.ICodec;
import org.etsi.its.codec.adapter.IValue;
import org.etsi.its.codec.adapter.SupportedCodecsEnum;
import org.etsi.its.codec.adapter.TLV;
import org.etsi.ttcn.tci.CharstringValue;

/** This class implements behaviour for Adapter controller port
 *
 */

public class AdapterControlPort extends AdapterPort implements IPort, IObservable { 

	/**
	 * Module version
	 */
	public static final String Version = "1.0.0.0";

	/**
	 * Logger instance
	 */
	private final static Logger _logger = Logger.getLogger("org.etsi.its");

	private static final byte CMD_IFACES = 0x03;
	private static final byte RPL_IFACES = 0x03;
	
	/**
	 * Constructor
	 * @param   portName        Name of the port
	 * @param   componentName   Name of the component owning this port instance
	 */
	public AdapterControlPort(final String portName, final String componentName) {
		super(portName, componentName);
		_logger.entering("AdapterControlPort", "Constructor", String.format("version:%s", Version));

	}

	@Override
	public boolean send(final byte[] message) {
		_logger.entering("AdapterControlPort", "send");

		boolean result = true;
		try { 
			// Decode non protocol part
			ICodec impl = CodecsFactory.getInstance().InstanciateCodec(SupportedCodecsEnum.LibItsGeoNetworking_TypesAndValues);
			IValue tlvs = impl.Decode(message);
			switch ((TestAdapterMessageTypeEnum)((TLV)tlvs.getFirstType()).getTypeId()) {
				case AcGnPrimitive: {
						TLV tlv = (TLV)tlvs.getNextType();
						switch ((TestAdapterMessageTypeEnum)tlv.getTypeId()) {
							case AcGetLongPosVector:
								ProcessAcGetLongPosVector(tlvs);
								break;
							case AcStartBeaconing:
								Management.getInstance(getComponentName()).startBeaconing((byte [])tlvs.getType(TestAdapterMessageTypeEnum.AcStartBeaconing).getValue());
								break;
							case AcStopBeaconing:
								Management.getInstance(getComponentName()).stopBeaconing();
								break;
							case AcStartPassBeaconing:
							    Management.getInstance(getComponentName()).startEnqueueingBeacons((byte [])tlvs.getType(TestAdapterMessageTypeEnum.AcStartPassBeaconing).getValue());
								break;
							case AcStopPassBeaconing:
							    Management.getInstance(getComponentName()).stopEnqueueingBeacons();
								break;
							case AcStartBeaconingMultipleNeighbour:
								Management.getInstance(getComponentName()).startMultipleBeaconing((byte [])tlvs.getType(TestAdapterMessageTypeEnum.AcStartPassBeaconing).getValue(), 1 /* FIXME */);
								break;
							case AcStopBeaconingMultipleNeighbour:
								Management.getInstance(getComponentName()).stopMultipleBeaconing();
								break;
						} // End of 'switch' statement
					}
					break;
				case AcGn6Primitive:
				    try {
				        byte[] buf = new byte[4096];
				        DatagramSocket remoteAdapterSocket = new DatagramSocket();
				        InetAddress remoteAdapterAddress = InetAddress.getByName(((CharstringValue)TERFactory.getInstance().getTaParameter("Gn6RemoteAdapterIp")).getString());
				        int remoteAdapterPort = Integer.decode(((CharstringValue)TERFactory.getInstance().getTaParameter("Gn6RemoteAdapterPort")).getString());
				        
				        DatagramPacket command = new DatagramPacket(new byte[] {CMD_IFACES}, 1, remoteAdapterAddress, remoteAdapterPort);
				        DatagramPacket response = new DatagramPacket(buf, buf.length);
				        
				        remoteAdapterSocket.send(command);
				        remoteAdapterSocket.receive(response); 
				        
				        byte[] data = ByteHelper.extract(response.getData(), response.getOffset(), response.getLength());
			            if(data[0] == RPL_IFACES) {
			                // enqueue response... 
			                byte[] buffer = impl.Encode(TestAdapterMessageTypeEnum.AcGn6Response, ByteHelper.extract(data, 1, data.length - 1), new Date().getTime());		                
    		                setChanged();
    		                notifyObservers(new PortEvent(buffer, getPortName(), getComponentName()));
			            }
			        } catch (SocketException e) {
			            e.printStackTrace();
			        } catch (UnknownHostException e) {
			            e.printStackTrace();
			        }         
					
					break;
			} // End of 'switch' statement

			return result;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return false;
	} // End of method send

	/**
	 * Processes AcGetLongPosVector command
	 * @param  tlvs    Command parameters in TLV format
	 * @throws IOException Signals that an I/O exception of some sort has occurred
	 */
	private void ProcessAcGetLongPosVector(IValue tlvs)  {
		_logger.entering("AdapterControlPort", "ProcessAcGetLongPosVector");

		final IValue ftlvs = tlvs;
		
		new Thread(new Runnable() {
            public void run() { 
                
        		byte[] longPosVector = Management.getInstance(getComponentName()).getLongPositionVector((byte [])ftlvs.getType(TestAdapterMessageTypeEnum.AcGetLongPosVector).getValue());
        
        		byte[] buffer = null;
        		ICodec impl = CodecsFactory.getInstance().InstanciateCodec(SupportedCodecsEnum.LibItsGeoNetworking_TypesAndValues);
        		try {
            		if ((longPosVector == null) || (longPosVector.length == 0)) {
            			// Add non protocol part: AcGnResponse/failure
            			buffer = impl.Encode(TestAdapterMessageTypeEnum.AcGnResponseFailure, new byte[] { 0x01 }, 0);
            		} else {
            			// Add non protocol part: AcGnResponse/LongPosVector
            			buffer = impl.Encode(TestAdapterMessageTypeEnum.LongPosVector, longPosVector, 0);
            		}
            		buffer = impl.Encode(TestAdapterMessageTypeEnum.AcGnResponse, buffer, new Date().getTime());
        		}
        		catch(IOException e) {
        		    e.printStackTrace();
        		}
        		setChanged();
        		notifyObservers(new PortEvent(buffer, getPortName(), getComponentName()));
            }
        }).start();
	}

	@Override
	public void dispose() {
		//empty
	}

} // End of class AdapterControlPort
