/**
 *  Upper Tester port implementation. This port is used to trigger IUT's upper interface
 *  
 *  @author     ETSI / STF424
 *  @version    $URL$
 *              $Id$
 *
 */
package org.etsi.its.adapter.ports;

import java.io.IOException;
import java.util.Date;
import java.util.logging.Logger;

import org.etsi.adapter.TERFactory;
import org.etsi.common.ByteHelper;
import org.etsi.its.codec.TestAdapterMessageTypeEnum;
import org.etsi.its.codec.adapter.CodecsFactory;
import org.etsi.its.codec.adapter.IType;
import org.etsi.its.codec.adapter.IValue;
import org.etsi.its.codec.adapter.SupportedCodecsEnum;
import org.etsi.its.uppertester.IBtpUpperTester;
import org.etsi.its.uppertester.ICamUpperTester;
import org.etsi.its.uppertester.IDenmUpperTester;
import org.etsi.its.uppertester.IGnUpperTester;
import org.etsi.its.uppertester.IUpperTester;
import org.etsi.its.uppertester.UpperTesterFactory;
import org.etsi.its.uppertester.IDenmUpperTester.ActionId;
import org.etsi.its.uppertester.IDenmUpperTester.DenmEvent;
import org.etsi.ttcn.tci.CharstringValue;

/** This class implements behaviour for Upper Tester port
 * The Upper tester entity in the SUT enables triggering Protocol functionalities by simulating primitives from 
 * application or LDM entities
 * It is required to trigger the Protocol layer in the SUT to send Protocol specific messages, which are 
 * resulting from upper layer primitives
 */
public class UpperTesterPort 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");

    /**
     * Constructor
     * @param   portName            Name of the port
     * @param   componentName       Name of the component owning this port instance
     * @param   localPortNumber     Local port number for the UDP listener
     * @param   remotePortNumber    UDP port listener of remote UT application
     */
    public UpperTesterPort(final String portName, final String componentName, final int localPortNumber, final int remotePortNumber) {
        super(portName, componentName);
        _logger.entering("UpperTesterPort", "Constructor", String.format("version:%s", Version));

    }

    /**
     * This method encodes in TLF format an UtResult message and notifies observers
     * 
     * @param result The result value
     */
    public void setResult(boolean result) {
        org.etsi.its.codec.adapter.ICodec impl = CodecsFactory.getInstance().InstanciateCodec(SupportedCodecsEnum.LibItsCommon_TypesAndValues);
        setChanged();        
        byte[] value = null;
        try {
            value = impl.Encode(TestAdapterMessageTypeEnum.UtResult, new byte[] {result ? (byte)0x01 : (byte)0x00}, new Date().getTime());
        } catch (IOException e) {
            e.printStackTrace();
        }
        notifyObservers(new PortEvent(value, getPortName(), getComponentName()));
    }
    
    @Override
    public boolean send(final byte[] message) {
        _logger.entering("UpperTesterPort", "send");

        // Decode message from network to UpperTester Application
        org.etsi.its.codec.adapter.ICodec impl = CodecsFactory.getInstance().InstanciateCodec(SupportedCodecsEnum.LibItsCommon_TypesAndValues);
        IValue tlvs = impl.Decode(message);
        switch ((TestAdapterMessageTypeEnum)tlvs.getFirstType().getTypeId()) {
        case UtInitialize:
            return processUtInitialize(tlvs);
        case UtTrigger:
            // Skip UtEvent
            tlvs.getNextType();
            return processUtTrigger(tlvs);
        case UtCheck:
            // Skip UtEvent
            tlvs.getNextType();
            return processUtCheck(tlvs);
        default:
            throw new RuntimeException();
        } // End of 'switch' statement

    } // End of method send

    private boolean processUtCheck(IValue tlvs) {
        switch ((TestAdapterMessageTypeEnum)tlvs.getNextType().getTypeId()) {
        case UtBtpEvent:
            return processUtBtpEvent(tlvs);
        case UtCamEvent:
            return processUtCamEvent(tlvs);
        case UtDenmEvent:
            return processUtCheckDenmEvent(tlvs);
        case UtGNEvent:
            return processUtGnEvent(tlvs);
        default:
            throw new RuntimeException();
        } 
    }

    private boolean processUtTrigger(IValue tlvs) {
        switch ((TestAdapterMessageTypeEnum)tlvs.getNextType().getTypeId()) {
        case UtBtpEvent:
            return processUtBtpEvent(tlvs);
        case UtCamEvent: 
            return processUtCamEvent(tlvs);
        case UtDenmEvent: 
            return processUtDenmEvent(tlvs);
        case UtGNEvent:            
            return processUtGnEvent(tlvs);        
        default:
            throw new RuntimeException();
        } 
    }

    private boolean processUtCheckDenmEvent(IValue tlvs) {
        final IDenmUpperTester ut = getDenmUpperTester();
        final byte[] denmPdu = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.UtEvent).getValue();
        new Thread(new Runnable() {
            public void run() { 
                boolean result = ut.checkDenmEvent(denmPdu);
                self.setResult(result);
            }
        }).start();
        return true;
    }

    private boolean processUtDenmEvent(IValue tlvs) {
        final IDenmUpperTester ut = getDenmUpperTester();
        TestAdapterMessageTypeEnum currentType = (TestAdapterMessageTypeEnum)tlvs.getNextType().getTypeId(); // Skip Situation
        final byte[] cause = (byte[])tlvs.getNextType().getValue(); // Get CauseCode
        final byte[] subCause = (byte[])tlvs.getNextType().getValue(); // SubCauseCode
        IType eventOption = (IType) tlvs.getNextType();
        if (eventOption == null) { // The optional 'EventOption' field is omitted
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue())); 
                    self.setResult(result);
                }
            }).start();
            return true;
        } else { // The optional 'EventOption' field is set
            // Extract current type
            IType item = (IType) tlvs.getNextType();
            currentType = (TestAdapterMessageTypeEnum)item.getTypeId();
            switch(currentType) {
                case eventCancellation: {
                    tlvs.getNextType(); // Skip ActionID
                    final byte[] stationId = (byte[])tlvs.getNextType().getValue();
                    final byte[] sequenceNo = (byte[])tlvs.getNextType().getValue();
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEventCancellation(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), 
                                                                                            ByteHelper.byteArrayToInt(subCause).shortValue()),
                                                                              new ActionId(ByteHelper.byteArrayToInt(stationId).intValue(), 
                                                                                           ByteHelper.byteArrayToInt(sequenceNo).shortValue())); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case eventNegation: {
                    tlvs.getNextType(); // Skip ActionID
                    final byte[] stationId = (byte[])tlvs.getNextType().getValue();
                    final byte[] sequenceNo = (byte[])tlvs.getNextType().getValue();                    
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEventNegation(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), 
                                                                                        ByteHelper.byteArrayToInt(subCause).shortValue()),
                                                                          new ActionId(ByteHelper.byteArrayToInt(stationId).intValue(), 
                                                                                       ByteHelper.byteArrayToInt(sequenceNo).shortValue())); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case eventTrafficFlowEffect: {
                    final byte[] trafficFlowEffect = (byte[])item.getValue();
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), ByteHelper.byteArrayToInt(trafficFlowEffect).shortValue()); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case eventExpirationTime: {
                    final byte[] expirationTime = (byte[])item.getValue();
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), ByteHelper.byteArrayToLong(ByteHelper.extract(expirationTime, expirationTime.length - Long.SIZE / 8, Long.SIZE / 8))); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case eventSeverity: {
                    final byte[] eventSeverity = (byte[])item.getValue();
                    final TestAdapterMessageTypeEnum type = currentType;
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), type, ByteHelper.byteArrayToInt(eventSeverity)); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case eventReliability: {
                    final byte[] eventReliability = (byte[])item.getValue();
                    final TestAdapterMessageTypeEnum type = currentType;
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), type, ByteHelper.byteArrayToInt(eventReliability)); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case updateEventExpiry: {
                    final byte[] stationId = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.stationID).getValue();
                    final byte[] sequenceNo = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.sequenceNo).getValue();
                    final byte[] expiryTime = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.expiryTime).getValue();
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.updateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), new ActionId(ByteHelper.byteArrayToInt(stationId), ByteHelper.byteArrayToInt(sequenceNo).shortValue()), ByteHelper.byteArrayToLong(expiryTime)); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case eventLinkedCause: {
                    tlvs.getNextType(); // Skip Situation type
                    final byte[] linkedCause = (byte[])tlvs.getNextType().getValue();
                    final byte[] linkedSubCause = (byte[])tlvs.getNextType().getValue();
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), new DenmEvent(ByteHelper.byteArrayToInt(linkedCause).shortValue(), ByteHelper.byteArrayToInt(linkedSubCause).shortValue())); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case EventTimeFrequency: {
                    tlvs.getNextType(); // Skip EventExpirationTimeFrequency type
                    final byte[] expirationTime = (byte[])tlvs.getNextType().getValue();
                    final byte[] frequency = (byte[])tlvs.getNextType().getValue();
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEvent(new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), ByteHelper.byteArrayToLong(ByteHelper.extract(expirationTime, expirationTime.length - Long.SIZE / 8, Long.SIZE / 8)), ByteHelper.byteArrayToInt(frequency).shortValue()); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                case eventCharact: {
                    tlvs.getNextType(); // Skip eventCharact type
                    final byte[] mobile = (byte[])tlvs.getNextType().getValue();
                    final byte[] causeType = (byte[])tlvs.getNextType().getValue();
                    final byte[] relevance = (byte[])tlvs.getNextType().getValue();
                    final byte[] timeCritical = (byte[])tlvs.getNextType().getValue();
                    new Thread(new Runnable() {
                        public void run() { 
                            boolean result = ut.generateDenmEvent(
                                new DenmEvent(ByteHelper.byteArrayToInt(cause).shortValue(), ByteHelper.byteArrayToInt(subCause).shortValue()), 
                                (mobile[0] == 0x01), 
                                ByteHelper.byteArrayToInt(causeType).shortValue(), 
                                ByteHelper.byteArrayToInt(relevance).shortValue(), 
                                (timeCritical[0] == 0x01)); 
                            self.setResult(result);
                        }
                    }).start();
                    return true;
                }
                default:
                    throw new RuntimeException(String.format("UpperTesterPort.processUtDenmEvent: Unknown type '%s'", currentType.name()));
            } // End of 'switch' statement
        }
    } // End of method processUtDenmEvent

    private boolean processUtCamEvent(IValue tlvs) {
        final ICamUpperTester ut = getCamUpperTester();
        switch ((TestAdapterMessageTypeEnum)tlvs.getNextType().getTypeId()) {
        case changePosition: {
            final byte[] distance = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.changePosition).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.changePosition(ByteHelper.byteArrayToInt(distance)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case changeHeading: {
            final byte[] distance = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.changeHeading).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.changeHeading(ByteHelper.byteArrayToInt(distance)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case changeSpeed: {
            final byte[] variation = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.changeSpeed).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.changeSpeed(ByteHelper.byteArrayToInt(variation)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setCrashSignal: {
            final byte[] crashed = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setCrashSignal).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setCrashSignal((crashed[0] == 0x01) ? true : false); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setDangerousGoodsStatus: {
            final byte[] dangerousGoodsStatus = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setDangerousGoodsStatus).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setDangerousGoodsStatus(ByteHelper.byteArrayToInt(dangerousGoodsStatus)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setLengthWidthPrecision: {
            final byte[] lengthWidthPrecision = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setLengthWidthPrecision).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setLengthWidthPrecision((lengthWidthPrecision[0] == 0x01) ? true : false); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setDistanceToStopLine: {
            final byte[] distance = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setDistanceToStopLine).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setDistanceToStopLine(ByteHelper.byteArrayToInt(distance)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setTurnAdvice: {
            final byte[] direction = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.direction).getValue();
            final byte[] distance = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.distance).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setTurnAdvice(ByteHelper.byteArrayToInt(direction), ByteHelper.byteArrayToInt(distance)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case changeCurvature: {
            final byte[] curvature = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.changeCurvature).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.changeCurvature(ByteHelper.byteArrayToInt(curvature)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setOccupancy: {
            final byte[] occupancy = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setOccupancy).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setOccupancy(ByteHelper.byteArrayToInt(occupancy)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setDoorStatus: {
            final byte[] doorStatus = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setDoorStatus).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setDoorStatus(ByteHelper.byteArrayToInt(doorStatus).byteValue()); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setLightBarStatus: {
            final byte[] status = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setLightBarStatus).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setLightBarStatus(ByteHelper.byteArrayToInt(status).shortValue()); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setSireneStatus: {
            final byte[] status = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setSireneStatus).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setSireneStatus(ByteHelper.byteArrayToInt(status).shortValue()); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setTrafficLightPriority: {
            final byte[] priority = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setTrafficLightPriority).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setTrafficLightPriority(ByteHelper.byteArrayToInt(priority).shortValue()); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setScheduleDeviation: {
            final byte[] delay = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setScheduleDeviation).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setScheduleDeviation(ByteHelper.byteArrayToInt(delay)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setPtLineDescription: {
            final byte[] courseOfJourney = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.courseOfJourney).getValue();
            final byte[] lineRef = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.lineRef).getValue();
            final byte[] routeRef = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.routeRef).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setPtLineDescription(
                        ByteHelper.byteArrayWithLengthToString(courseOfJourney), 
                        ByteHelper.byteArrayWithLengthToString(lineRef), 
                        ByteHelper.byteArrayWithLengthToString(routeRef)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case setExteriorLightsStatus: {
            final byte[] lightStatus = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.setExteriorLightsStatus).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.setExteriorLightsStatus(lightStatus[0]); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case checkLdm: {
            final byte[] camPdu = (byte[])tlvs.getPrevType().getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.checkLdm(camPdu); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        default:
            throw new RuntimeException();
        } // End of 'switch' statement
    } // End of method processUtCamEvent

    private boolean processUtBtpEvent(IValue tlvs) {
        final IBtpUpperTester ut = getBtpUpperTester();
        switch ((TestAdapterMessageTypeEnum)tlvs.getNextType().getTypeId()) {
        case BtpAHeader: {
            final byte[] destinationPort = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.destinationPort).getValue();
            final byte[] sourcePort = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.sourcePort).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateBtpA(ByteHelper.byteArrayToInt(destinationPort), ByteHelper.byteArrayToInt(sourcePort)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case BtpBHeader: {
            final byte[] destinationPort = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.destinationPort).getValue();
            final byte[] destinationPortInfo = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.destinationPortInfo).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateBtpB(ByteHelper.byteArrayToInt(destinationPort), ByteHelper.byteArrayToInt(destinationPortInfo)); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case BtpPayload: {
            final byte[] payload = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.BtpPayload).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.checkPayload(payload); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        default:
            throw new RuntimeException();
        }
    }

    private boolean processUtGnEvent(IValue tlvs) {

        final IGnUpperTester ut = getGnUpperTester();
        
        switch ((TestAdapterMessageTypeEnum)tlvs.getNextType().getTypeId()) {
        case GeoNetworkingPacket:
            final byte[] packet = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.GeoNetworkingPacket).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.checkPacket(packet); 
                    self.setResult(result);
                }
            }).start();
            return true;
        case GenerateGeoUnicastMessage: {
            final byte[] gnAddress = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.gnAddress).getValue();
            final byte[] lifetime = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.lifetime).getValue();
            final byte[] payload = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.payload).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateGeoUnicast(gnAddress, ByteHelper.byteArrayToInt(lifetime).shortValue(), payload); 
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case GenerateGeoBroadcastMessage: { // See record LibItsGeoNetworking_TypesAndValues.Area
            final byte[] shape = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.shape).getValue();
            final byte[] geoAreaPosLatitude = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.geoAreaPosLatitude).getValue();
            final byte[] geoAreaPosLongitude = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.geoAreaPosLongitude).getValue();
            final byte[] distanceA = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.distanceA).getValue();
            final byte[] distanceB = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.distanceB).getValue();
            final byte[] angle = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.angle).getValue();
            final byte[] lifetime = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.lifetime).getValue();
            final byte[] payload = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.payload).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateGeoBroadcast(
                        ByteHelper.byteArrayToInt(shape).shortValue(), 
                        ByteHelper.byteArrayToInt(geoAreaPosLatitude), 
                        ByteHelper.byteArrayToInt(geoAreaPosLongitude), 
                        ByteHelper.byteArrayToShort(distanceA), 
                        ByteHelper.byteArrayToShort(distanceB), 
                        ByteHelper.byteArrayToShort(angle), 
                        ByteHelper.byteArrayToInt(lifetime).shortValue(),
                        payload);
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case GenerateGeoAnycastMessage: { // See record LibItsGeoNetworking_TypesAndValues.Area
            final byte[] shape = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.shape).getValue();
            final byte[] geoAreaPosLatitude = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.geoAreaPosLatitude).getValue();
            final byte[] geoAreaPosLongitude = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.geoAreaPosLongitude).getValue();
            final byte[] distanceA = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.distanceA).getValue();
            final byte[] distanceB = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.distanceB).getValue();
            final byte[] angle = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.angle).getValue();

            final byte[] lifetime = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.lifetime).getValue();
            final byte[] payload = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.payload).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateGeoAnycast(
                        ByteHelper.byteArrayToInt(shape).shortValue(), 
                        ByteHelper.byteArrayToInt(geoAreaPosLatitude), 
                        ByteHelper.byteArrayToInt(geoAreaPosLongitude), 
                        ByteHelper.byteArrayToShort(distanceA), 
                        ByteHelper.byteArrayToShort(distanceB), 
                        ByteHelper.byteArrayToShort(angle),
                        ByteHelper.byteArrayToInt(lifetime).shortValue(),
                        payload);
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case GenerateSHBMessage: {
            final byte[] payload = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.payload).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateShb(payload);
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case GenerateTSBMessage: {
            final byte[] payload = (byte[])tlvs.getType(TestAdapterMessageTypeEnum.payload).getValue();
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.generateTsb(payload, (short)255);
                    self.setResult(result);
                }
            }).start();
            return true;
        }
        case ChangePosition:
            new Thread(new Runnable() {
                public void run() { 
                    boolean result = ut.changePosition();
                    self.setResult(result);
                }
            }).start();
            return true;
        default:
            throw new RuntimeException();
        }
    }
    
    
    private boolean processUtInitialize(IValue tlvs) {
        String utName = "Operator";
        String utSettings = "";
        switch ((TestAdapterMessageTypeEnum)tlvs.getNextType().getTypeId()) {
        case UtCamInitialize:
             utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("CamUpperTester")).getString();
             utSettings = ((CharstringValue)TERFactory.getInstance().getTaParameter("CamUpperTesterSettings")).getString();
            break;
        case UtDenmInitialize:
            utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("DenmUpperTester")).getString();
            utSettings = ((CharstringValue)TERFactory.getInstance().getTaParameter("DenmUpperTesterSettings")).getString();
            break;
        case UtBtpInitialize:
            utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("BtpUpperTester")).getString();
            utSettings = ((CharstringValue)TERFactory.getInstance().getTaParameter("BtpUpperTesterSettings")).getString();
            break;
        case UtGNInitialize:
            utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("GnUpperTester")).getString();
            utSettings = ((CharstringValue)TERFactory.getInstance().getTaParameter("GnUpperTesterSettings")).getString();
            break;
        }
        final IUpperTester ut = UpperTesterFactory.getInstance().getUpperTester(utName);
        final String settings = utSettings;
        new Thread(new Runnable() {
            public void run() { 
                boolean result = ut.initialize(settings);
                self.setResult(result);
            }
        }).start();
        return true;
    }

    @Override
    public void dispose() {
        UpperTesterFactory.getInstance().dispose(); // Initialising operation
    }
    
    private ICamUpperTester getCamUpperTester() {
        String utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("CamUpperTester")).getString();
        
        IUpperTester cam = UpperTesterFactory.getInstance().getUpperTester(utName); 
        if(cam instanceof ICamUpperTester) {
            return (ICamUpperTester)cam;
        }
        return null;
    }
    
    private IDenmUpperTester getDenmUpperTester() {
        String utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("DenmUpperTester")).getString();
        
        IUpperTester denm = UpperTesterFactory.getInstance().getUpperTester(utName);
        if(denm instanceof IDenmUpperTester) {
            return (IDenmUpperTester)denm;
        }
        return null;
    }
    
    private IBtpUpperTester getBtpUpperTester() {
        String utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("BtpUpperTester")).getString();
        
        IUpperTester btp = UpperTesterFactory.getInstance().getUpperTester(utName);
        if(btp instanceof IBtpUpperTester) {
            return (IBtpUpperTester)btp;
        }
        return null;
    }
    
    private IGnUpperTester getGnUpperTester() {
        String utName = ((CharstringValue)TERFactory.getInstance().getTaParameter("GnUpperTester")).getString();
        
        IUpperTester gn = UpperTesterFactory.getInstance().getUpperTester(utName);
        if(gn instanceof IGnUpperTester) {
            return (IGnUpperTester)gn;
        }
        return null;
    }
    
    /**
     *  Reference to self used by inner threads
     */
    final UpperTesterPort self = this;
    
} // End of class UpperTesterPort
