/**
 * @author    STF 424_ITS_Test_Platform
 * @version    $id$
 */
package org.etsi.ttcn.tool.elvior.adapter;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Properties;

import org.elvior.ttcn.tritci.ChannelEventHandler;
import org.elvior.ttcn.tritci.IntegerValueEx;
import org.elvior.ttcn.tritci.TciProvider;
import org.elvior.ttcn.tritci.TriFactory;
import org.elvior.ttcn.tritci.TriProvider;
import org.elvior.ttcn.tritci.TriTciChannel;
import org.etsi.ttcn.adapter.core.TERFactory;
import org.etsi.ttcn.codec.core.ITciCDWrapper;
import org.etsi.ttcn.codec.core.TciCDWrapperFactory;
import org.etsi.common.ByteHelper;
import org.etsi.extfunc.core.ExternalFunction;
import org.etsi.extfunc.core.XfFactory;
import org.etsi.extfunc.its.ItsExternalFunctionsProvider;
import org.etsi.ttcn.tool.elvior.codec.TciCDWrapper;
import org.etsi.ttcn.tci.BooleanValue;
import org.etsi.ttcn.tci.CharstringValue;
import org.etsi.ttcn.tci.EnumeratedValue;
import org.etsi.ttcn.tci.FloatValue;
import org.etsi.ttcn.tci.IntegerValue;
import org.etsi.ttcn.tci.OctetstringValue;
import org.etsi.ttcn.tci.RecordOfValue;
import org.etsi.ttcn.tci.RecordValue;
import org.etsi.ttcn.tci.TciParameterList;
import org.etsi.ttcn.tci.TciValueList;
import org.etsi.ttcn.tci.Value;
import org.etsi.ttcn.tri.TriAddress;
import org.etsi.ttcn.tri.TriAddressList;
import org.etsi.ttcn.tri.TriBoolean;
import org.etsi.ttcn.tri.TriCommunicationSA;
import org.etsi.ttcn.tri.TriCommunicationTE;
import org.etsi.ttcn.tri.TriComponentId;
import org.etsi.ttcn.tri.TriException;
import org.etsi.ttcn.tri.TriFunctionId;
import org.etsi.ttcn.tri.TriLong;
import org.etsi.ttcn.tri.TriMessage;
import org.etsi.ttcn.tri.TriParameter;
import org.etsi.ttcn.tri.TriParameterList;
import org.etsi.ttcn.tri.TriParameterPassingMode;
import org.etsi.ttcn.tri.TriPlatformPA;
import org.etsi.ttcn.tri.TriPortId;
import org.etsi.ttcn.tri.TriPortIdList;
import org.etsi.ttcn.tri.TriSignatureId;
import org.etsi.ttcn.tri.TriStatus;
import org.etsi.ttcn.tri.TriTestCaseId;
import org.etsi.ttcn.tri.TriTimerDuration;
import org.etsi.ttcn.tri.TriTimerId;
import org.etsi.ttcn.xtri.xTriCommunicationSA;

public class PluginAdapter implements TriCommunicationSA, TriCommunicationTE, xTriCommunicationSA, TriPlatformPA, ChannelEventHandler {
    /**
     * This is the main PluginAdapter class
     */
    private TestAdapter _itsTestAdapter;
    
    private static Properties _properties = new Properties();
    
    private TriStatus m_triOK;
    
    private TriStatus m_triKO;
    
    private ItsExternalFunctionsProvider _extfuncsImpl;

    public PluginAdapter() {
        
        // Load TA settings
        try {
            _properties.load(TestAdapterMain.class.getResourceAsStream("./etc/ta.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // Register this object as the instance implementing the TRI-SA interface
        TriProvider.getInstance().setTriCommunicationSA(this);
        // Register this object as the instance implementing the TRI-PA interface
        TriProvider.getInstance().setTriPlatformPA(this);
        TriTciChannel channel = TriTciChannel.getInstance();
        // Set application name that will be displayed in the TestCast log
        channel.setApplicationName("ItsPlatform");
        // Register this object as the receiver of events generated by TestCast framework
        channel.setEventHandler(this);
        TriFactory factory = TriProvider.getInstance().getTriFactory(); 
        m_triOK = factory.createOkStatus();
        m_triKO = factory.createErrorStatus();
        TERFactory.setImpl(new TeRequiredImpl(this));
        // External functions
        TciCDWrapperFactory.getInstance().setImpl(new TciCDWrapper());
        _extfuncsImpl = new ItsExternalFunctionsProvider();
    }
    
    public void run() {
        
        // Attempt to connect to TestCast
        if (TriTciChannel.getInstance().open()) {
            _itsTestAdapter = new TestAdapter();
            try {
                synchronized(this) {
                    wait();
                }
            } catch (InterruptedException e) {
            }
        }
    }
    
    public TriStatus triCall(TriComponentId componentId, TriPortId tsiPortId,
            TriAddress sutAddress, TriSignatureId signatureId,
            TriParameterList parameterList) {
        return _itsTestAdapter.triCall(componentId, tsiPortId, sutAddress, signatureId, parameterList);
    }

    public TriStatus triCallBC(TriComponentId componentId, TriPortId tsiPortId,
            TriSignatureId signatureId, TriParameterList parameterList) {
        return _itsTestAdapter.triCallBC(componentId, tsiPortId, signatureId, parameterList);
    }

    public TriStatus triCallMC(TriComponentId componentId, TriPortId tsiPortId,
            TriAddressList sutAddresses, TriSignatureId signatureId,
            TriParameterList parameterList) {
        return _itsTestAdapter.triCallMC(componentId, tsiPortId, sutAddresses, signatureId, parameterList);
    }

    public TriStatus triEndTestCase() {
        return _itsTestAdapter.triEndTestCase();
    }

    public TriStatus triExecuteTestCase(TriTestCaseId testCaseId, TriPortIdList tsiPorts) {
        // Disable XTRI functionality for all ports, so that existing SA implementation can be reused
        for(int i = 0; i < tsiPorts.size(); i++) {
            TriTciChannel.getInstance().enableXTriForPort(tsiPorts.get(i), false);
        }
        return _itsTestAdapter.triExecuteTestCase(testCaseId, tsiPorts);
    }

    public TriStatus triMap(TriPortId compPortId, TriPortId tsiPortId) {
        return _itsTestAdapter.triMap(compPortId, tsiPortId);
    }

    public TriStatus triRaise(TriComponentId componentId, TriPortId tsiPortId,
            TriAddress sutAddress, TriSignatureId signatureId, TriException exception) {
        return _itsTestAdapter.triRaise(componentId, tsiPortId, sutAddress, signatureId, exception);
    }

    public TriStatus triRaiseBC(TriComponentId componentId,
            TriPortId tsiPortId, TriSignatureId signatureId, TriException exc) {
        return _itsTestAdapter.triRaiseBC(componentId, tsiPortId, signatureId, exc);
    }

    public TriStatus triRaiseMC(TriComponentId componentId,
            TriPortId tsiPortId, TriAddressList sutAddresses,
            TriSignatureId signatureId, TriException exc) {
        return _itsTestAdapter.triRaiseMC(componentId, tsiPortId, sutAddresses, signatureId, exc);
    }

    public TriStatus triReply(TriComponentId componentId, TriPortId tsiPortId,
            TriAddress sutAddress, TriSignatureId signatureId,
            TriParameterList parameterList, TriParameter returnValue) {
        return _itsTestAdapter.triReply(componentId, tsiPortId, sutAddress, signatureId, parameterList, returnValue);
    }

    public TriStatus triReplyBC(TriComponentId componentId,
            TriPortId tsiPortId, TriSignatureId signatureId,
            TriParameterList parameterList, TriParameter returnValue) {
        return _itsTestAdapter.triReplyBC(componentId, tsiPortId, signatureId, parameterList, returnValue);
    }

    public TriStatus triReplyMC(TriComponentId componentId,
            TriPortId tsiPortId, TriAddressList sutAddresses,
            TriSignatureId signatureId, TriParameterList parameterList,
            TriParameter returnValue) {
        return _itsTestAdapter.triReplyMC(componentId, tsiPortId, sutAddresses, signatureId, parameterList, returnValue);
    }

    public TriStatus triSAReset() {
        return _itsTestAdapter.triSAReset();
    }

    public TriStatus triSend(TriComponentId componentId, TriPortId tsiPortId,
            TriAddress address, TriMessage sendMessage) {
        return _itsTestAdapter.triSend(componentId, tsiPortId, address, sendMessage);
    }

    public TriStatus triSendBC(TriComponentId componentId, TriPortId tsiPortId,
            TriMessage sendMessage) {
        return _itsTestAdapter.triSendBC(componentId, tsiPortId, sendMessage);
    }

    public TriStatus triSendMC(TriComponentId componentId, TriPortId tsiPortId,
            TriAddressList addresses, TriMessage sendMessage) {
        return _itsTestAdapter.triSendMC(componentId, tsiPortId, addresses, sendMessage);
    }

    public TriStatus triSutActionInformal(String description) {
        return _itsTestAdapter.triSutActionInformal(description);
    }

    public TriStatus triUnmap(TriPortId compPortId, TriPortId tsiPortId) {
        return _itsTestAdapter.triUnmap(compPortId, tsiPortId);
    }

    public TriStatus triExternalFunction(TriFunctionId functionId, TriParameterList parameterList, TriParameter returnValue) {

    	ExternalFunction xf = XfFactory.getInstance().getXf("");
    	
    	xf.unpack(functionId, parameterList, returnValue);
    	
    	return m_triOK;
    }
    
    public TriStatus triExternalFunction2(TriFunctionId functionId, TriParameterList parameterList, TriParameter returnValue) {
        
        TriStatus result = m_triOK;
        if (
                functionId.getFunctionName().endsWith("fx_computeIPv6CheckSum") && 
                (parameterList.size() == 5)
        ) { 
            // Calculate ICMPv6 checksum on pseudo header according RFC 4443 - Clause 2.3
            exec_computeIPv6CheckSum(parameterList, returnValue);
        } else if (
                functionId.getFunctionName().endsWith("fx_computeDistance") && 
                (parameterList.size() == 4)
        ) { 
            // Compute a distance between two points 
            exec_computeDistance(parameterList, returnValue);
        } else if (
                functionId.getFunctionName().endsWith("fx_computePositionUsingDistance") && 
                (parameterList.size() == 6)
        ) { 
            // Compute a position using a reference position, a distance and an orientation 
            exec_computePositionUsingDistance(parameterList, returnValue);
        } else if (
                functionId.getFunctionName().endsWith("fx_computePositionFromRotation") && 
                (parameterList.size() == 7)
        ) { 
            // Compute a reference position rotation 
            exec_computePositionFromRotation(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_computeGnTimestamp")) { 
            // Compute timestamp based on current time 
            exec_computeGnTimestamp(returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_getCurrentTime")) { 
            // Gets the current time 
            exec_getCurrentTime(returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_getCurrentTimeMark")) { 
            // Gets the tenths of a second in the current or next hour in units of 1/10th second from UTC time
            exec_getCurrentTimeMark(returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_getMinuteOfTheYear")) { 
            // Gets the the minutes of current UTC year
            exec_getMinuteOfTheYear(returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_getDSecond")) { 
            // Gets the milliseconds point in the current UTC minute
            exec_getDSecond(returnValue);
        } else if (functionId.getFunctionName().endsWith("xf_parseIpv6Address")) { 
            exec_parseIpv6Address(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_hashWithSha256") && (parameterList.size() == 1)) { 
            exec_hashWithSha256(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_signWithEcdsaNistp256WithSha256") && (parameterList.size() == 2)) { 
            exec_signWithEcdsaNistp256WithSha256(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_verifyWithEcdsaNistp256WithSha256") && (parameterList.size() == 4)) { 
            exec_verifyWithEcdsaNistp256WithSha256(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_generateKeyPair") && (parameterList.size() == 3)) { 
            exec_generateKeyPair(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_dms2dd") && (parameterList.size() == 4)) { 
            exec_dms2dd(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_isValidPolygonalRegion") && (parameterList.size() == 1)) { 
            exec_isValidPolygonalRegion(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_isPolygonalRegionInside") && (parameterList.size() == 2)) { 
            exec_isPolygonalRegionInside(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_isLocationInsideCircularRegion") && (parameterList.size() == 2)) { 
            exec_isLocationInsideCircularRegion(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_isLocationInsideRectangularRegion") && (parameterList.size() == 2)) { 
            exec_isLocationInsideRectangularRegion(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_isLocationInsidePolygonalRegion") && (parameterList.size() == 2)) { 
            exec_isLocationInsidePolygonalRegion(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_isLocationInsideIdentifiedRegion") && (parameterList.size() == 2)) { 
            exec_isLocationInsideIdentifiedRegion(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_loadCertificates") && (parameterList.size() == 2)) { 
            exec_loadCertificates(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_unloadCertificates") && (parameterList.size() == 0)) { 
            exec_unloadCertificates(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_readCertificate") && (parameterList.size() == 2)) { 
            exec_readCertificate(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_readCertificateDigest") && (parameterList.size() == 2)) { 
            exec_readCertificateDigest(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_readSigningKey") && (parameterList.size() == 2)) { 
            exec_readSigningKey(parameterList, returnValue);
        } else if (functionId.getFunctionName().endsWith("fx_readEncryptingKey") && (parameterList.size() == 2)) { 
            exec_readEncryptingKey(parameterList, returnValue);
        } else {
            System.err.println("Unprocessed external function"); 
            result = m_triKO;
        }

        return result;
    }
    
    public void onConnectionClosed() {
        synchronized(this) { 
            notify();
        }
    }

    public void onConnectionEstablished() {
    }

    public void onError(String error) {
    }

    @Override
    public TriStatus triPAReset() {
        return m_triOK;
    }

    @Override
    public TriStatus triReadTimer(TriTimerId timerId, TriTimerDuration elapsedTime) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triStartTimer(TriTimerId timerId, TriTimerDuration timerDuration) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triStopTimer(TriTimerId timerId) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triTimerRunning(TriTimerId timerId, TriBoolean running) {
        throw new RuntimeException();
    }

    @Override
    public void triEnqueueCall(TriPortId tsiPortId, TriAddress SUTaddress, TriComponentId componentId, TriSignatureId signatureId, TriParameterList parameterList) {
        throw new RuntimeException();
    }

    @Override
    public void triEnqueueException(TriPortId tsiPortId, TriAddress sutAddress, TriComponentId componentId, TriSignatureId signatureId,
            TriException exception) {
        throw new RuntimeException();
    }

    @Override
    public void triEnqueueMsg(TriPortId tsiPortId, TriAddress sutAddress, TriComponentId componentId, TriMessage receivedMessage) {
        TriProvider.getInstance().getTriCommunicationTE().triEnqueueMsg(
            tsiPortId, 
            null, 
            componentId, 
            receivedMessage);
    }

    @Override
    public void triEnqueueReply(TriPortId tsiPortId, TriAddress address, TriComponentId componentId, TriSignatureId signatureId, TriParameterList parameterList, TriParameter returnValue) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triMapParam(TriPortId arg0, TriPortId arg1, TriParameterList arg2) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triUnmapParam(TriPortId arg0, TriPortId arg1, TriParameterList arg2) {
        throw new RuntimeException();
    }
    
    @Override
    public TriStatus xtriCall(TriComponentId arg0, TriPortId arg1, Value arg2, TriSignatureId arg3, TciParameterList arg4) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriCallBC(TriComponentId arg0, TriPortId arg1, TriSignatureId arg2, TciParameterList arg3) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriCallMC(TriComponentId arg0, TriPortId arg1, TciValueList arg2, TriSignatureId arg3, TciParameterList arg4) {
        throw new RuntimeException();
    }

    @Override
    public Value xtriConvert(Object arg0, org.etsi.ttcn.tci.Type arg1) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriMapParam(TriPortId arg0, TriPortId arg1, TciParameterList arg2) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriRaise(TriComponentId arg0, TriPortId arg1, Value arg2, TriSignatureId arg3, Value arg4) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriRaiseBC(TriComponentId arg0, TriPortId arg1, TriSignatureId arg2, Value arg3) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriRaiseMC(TriComponentId arg0, TriPortId arg1, TciValueList arg2, TriSignatureId arg3, Value arg4) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriReply(TriComponentId arg0, TriPortId arg1, Value arg2, TriSignatureId arg3, TciParameterList arg4, Value arg5) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriReplyBC(TriComponentId arg0, TriPortId arg1, TriSignatureId arg2, TciParameterList arg3, Value arg4) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriReplyMC(TriComponentId arg0, TriPortId arg1, TciValueList arg2, TriSignatureId arg3, TciParameterList arg4, Value arg5) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriSend(TriComponentId compPortId, TriPortId tsiPortId, Value arg2, Value arg3) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriSendBC(TriComponentId arg0, TriPortId arg1, Value arg2) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriSendMC(TriComponentId arg0, TriPortId arg1, TciValueList arg2, Value arg3) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus xtriUnmapParam(TriPortId arg0, TriPortId arg1, TciParameterList arg2) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triStaticMap(TriPortId arg0, TriPortId arg1) {
        throw new RuntimeException();
    }
    
    private void exec_computeIPv6CheckSum(TriParameterList parameterList, TriParameter returnValue) {
        
        // Source address (128 bits)
        byte[] value = parameterList.get(0).getEncodedParameter();
        OctetstringValue sourceAddress = (OctetstringValue)TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        sourceAddress.setLength(value.length);
        for (int i = 0; i < value.length; i++) {
            sourceAddress.setOctet(i, value[i]);
        }
        // Destination address (128 bits)
        value = parameterList.get(1).getEncodedParameter();
        OctetstringValue destinationAddress = (OctetstringValue)TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        destinationAddress.setLength(value.length);
        for (int i = 0; i < value.length; i++) {
            destinationAddress.setOctet(i, value[i]);
        }
        // Upper-Layer Packet Length (32 bits)
        value = parameterList.get(2).getEncodedParameter();
        IntegerValue payloadLength = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        payloadLength.setInteger(ByteHelper.byteArrayToInt(value));
        // Upper-Layer payload
        value = parameterList.get(3).getEncodedParameter();
        OctetstringValue payload = (OctetstringValue)TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        payload.setLength(value.length);
        for (int i = 0; i < value.length; i++) {
            payload.setOctet(i, value[i]);
        }
        // Next header value (e.g. 0x3a for ICMPv6) (8bits)
        value = parameterList.get(4).getEncodedParameter();
        IntegerValue nextHdr = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        nextHdr.setInteger(ByteHelper.byteArrayToInt(value));
        // Execute the function
        OctetstringValue checksum = _extfuncsImpl.fx_computeIPv6CheckSum(
            sourceAddress, 
            destinationAddress, 
            payloadLength, 
            payload, 
            nextHdr 
        );
        value = new byte[checksum.getLength()];
        for (int i = 0; i < value.length; i++) {
            value[i] = (byte)checksum.getOctet(i);
        }
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(value.length * Byte.SIZE);
        returnValue.setEncodedParameter(value);
        
    } // End of method exec_computeIPv6CheckSum
    
    private void exec_computeDistance(TriParameterList parameterList, TriParameter returnValue) {
        
        byte[] value = parameterList.get(0).getEncodedParameter();
        IntegerValue latitudeA = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        latitudeA.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(1).getEncodedParameter();
        IntegerValue longitudeA = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        longitudeA.setInteger(ByteHelper.byteArrayToInt(value));
        
       value = parameterList.get(2).getEncodedParameter();
        IntegerValue latitudeB = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        latitudeB.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(3).getEncodedParameter();
        IntegerValue longitudeB = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        longitudeB.setInteger(ByteHelper.byteArrayToInt(value));
        
        // Execute the function
        FloatValue distance = _extfuncsImpl.fx_computeDistance(
            latitudeA, 
            longitudeA, 
            latitudeB, 
            longitudeB 
        );
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        byte[] convert = ByteHelper.floatToByteArray(distance.getFloat());
        returnValue.setNumberOfBits(convert.length * Byte.SIZE);
        returnValue.setEncodedParameter(convert);
        
    } // End of method exec_computeDistance
    
    private void exec_computePositionUsingDistance(TriParameterList parameterList, TriParameter returnValue) {
        
        byte[] value = parameterList.get(0).getEncodedParameter();
        IntegerValue refLatitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        refLatitude.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(1).getEncodedParameter();
        IntegerValue refLongitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        refLongitude.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(2).getEncodedParameter();
        FloatValue distance = (FloatValue)TciProvider.getInstance().getTciCDRequired().getFloat().newInstance();
        distance.setFloat(ByteHelper.byteArrayToInt(value)); // FIXME Should be byteArrayToFloat???
        
        value = parameterList.get(3).getEncodedParameter();
        IntegerValue orientation = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        orientation.setInteger(ByteHelper.byteArrayToInt(value));
        
        IntegerValue latitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        IntegerValue longitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        _extfuncsImpl.fx_computePositionUsingDistance(refLatitude, refLongitude, distance, orientation, latitude, longitude);
        
        TriParameter param = parameterList.get(4);
        param.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        param.setNumberOfBits(Integer.SIZE);
        param.setEncodedParameter(ByteHelper.intToByteArray(latitude.getInteger(), Integer.SIZE / Byte.SIZE));
        
        param = parameterList.get(5);
        param.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        param.setNumberOfBits(Integer.SIZE);
        param.setEncodedParameter(ByteHelper.intToByteArray(longitude.getInteger(), Integer.SIZE / Byte.SIZE));
        
    } // End of method exec_computePositionUsingDistance
    
    private void exec_computePositionFromRotation(TriParameterList parameterList, TriParameter returnValue) {
        
        byte[] value = parameterList.get(0).getEncodedParameter();
        IntegerValue refLatitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        refLatitude.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(1).getEncodedParameter();
        IntegerValue refLongitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        refLongitude.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(2).getEncodedParameter();
        IntegerValue cenLatitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        cenLatitude.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(3).getEncodedParameter();
        IntegerValue cenLongitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        cenLongitude.setInteger(ByteHelper.byteArrayToInt(value));
        
        value = parameterList.get(4).getEncodedParameter();
        IntegerValue rotation = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        rotation.setInteger(ByteHelper.byteArrayToInt(value));
        
        IntegerValue latitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        IntegerValue longitude = (IntegerValue)TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
        _extfuncsImpl.fx_computePositionFromRotation(refLatitude, refLongitude, cenLatitude, cenLongitude, rotation, latitude, longitude);
        
        TriParameter param = parameterList.get(5);
        param.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        param.setNumberOfBits(Integer.SIZE);
        param.setEncodedParameter(ByteHelper.intToByteArray(latitude.getInteger(), Integer.SIZE / Byte.SIZE));
        
        param = parameterList.get(6);
        param.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        param.setNumberOfBits(Integer.SIZE);
        param.setEncodedParameter(ByteHelper.intToByteArray(longitude.getInteger(), Integer.SIZE / Byte.SIZE));
        
    }
    
    private void exec_computeGnTimestamp(TriParameter returnValue) {
        
        // Execute the function
        IntegerValue timestamp = _extfuncsImpl.fx_computeGnTimestamp();
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Integer.SIZE);
        returnValue.setEncodedParameter(
            ByteHelper.intToByteArray(
                timestamp.getInteger(),
                Integer.SIZE / Byte.SIZE
            ));
        
    } // End of method exec_computeGnTimestamp
    
    private void exec_getCurrentTime(TriParameter returnValue) {
        
        // Execute the function
        IntegerValue time = _extfuncsImpl.fx_getCurrentTime();
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Long.SIZE);
        returnValue.setEncodedParameter(
            ByteHelper.longToByteArray(
//                ((IntegerValueEx)time).getInt64() << 6, // Decoding starts from bit 0, byte 0, so we have to shift the value 6 bits to the left
  //              6/*Long.SIZE / Byte.SIZE*/              // Encoding on 48 bits
                ((IntegerValueEx)time).getInt64(),
                Long.SIZE / Byte.SIZE
            ));
        
    } // End of method exec_getCurrentTime

    private void exec_getCurrentTimeMark(TriParameter returnValue) {
        
        // Execute the function
        IntegerValue time = _extfuncsImpl.fx_getCurrentTimeMark();
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Long.SIZE);
        returnValue.setEncodedParameter(
            ByteHelper.longToByteArray(
                ((IntegerValueEx)time).getInt64(),
                4/*Integer.SIZE / Byte.SIZE*/              // Encoding on 32 bits
            ));
        
    } // End of method exec_getCurrentTimeMark

    private void exec_getMinuteOfTheYear(TriParameter returnValue) {
        
        // Execute the function
        IntegerValue time = _extfuncsImpl.fx_getMinuteOfTheYear();
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Long.SIZE);
        returnValue.setEncodedParameter(
            ByteHelper.longToByteArray(
                ((IntegerValueEx)time).getInt64(),
                2/*Short.SIZE / Byte.SIZE*/              // Encoding on 16 bits
            ));
        
    } // End of method exec_getMinuteOfTheYear

    private void exec_getDSecond(TriParameter returnValue) {
        
        // Execute the function
        IntegerValue time = _extfuncsImpl.fx_getDSecond();
        //_logger.info(String.format("exec_getDSecond returns %d", ((IntegerValueEx)time).getInt64())); 
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Long.SIZE);
        returnValue.setEncodedParameter(
            ByteHelper.longToByteArray(
                ((IntegerValueEx)time).getInt64(),
                2/*Short.SIZE / Byte.SIZE*/              // Encoding on 16 bits
            ));
        
    } // End of method exec_getDSecond

    private void exec_generateKeyPair(TriParameterList parameterList, TriParameter returnValue) {
        
        // Execute the function
        OctetstringValue privateKey = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        OctetstringValue publicKeyX = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        OctetstringValue publicKeyY = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        BooleanValue result = _extfuncsImpl.fx_generateKeyPair(privateKey, publicKeyX, publicKeyY);
        // privateKey
        TriParameter Key = parameterList.get(0);
        Key.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        byte[] ref = new byte[privateKey.getLength()];
        for (int i = 0; i < ref.length; i++) {
            ref[i] = (byte) privateKey.getOctet(i);
        } // End 'for' statement
        Key.setEncodedParameter(ref);
        // publicKeyX
        Key = parameterList.get(1);
        Key.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        Key.setNumberOfBits(publicKeyX.getLength() * Byte.SIZE);
        ref = new byte[publicKeyX.getLength()];
        for (int i = 0; i < ref.length; i++) {
            ref[i] = (byte) publicKeyX.getOctet(i);
        } // End 'for' statement
        Key.setEncodedParameter(ref);
        // publicKeyY
        Key = parameterList.get(2);
        Key.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        Key.setNumberOfBits(publicKeyY.getLength() * Byte.SIZE);
        ref = new byte[publicKeyY.getLength()];
        for (int i = 0; i < ref.length; i++) {
            ref[i] = (byte) publicKeyY.getOctet(i);
        } // End 'for' statement
        Key.setEncodedParameter(ref);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) } );
        
    } // End of method exec_generateKeyPair

    private void exec_verifyWithEcdsaNistp256WithSha256(TriParameterList parameterList, TriParameter returnValue) {
        
        // Execute the function
        byte[] value = parameterList.get(0).getEncodedParameter();
        OctetstringValue toBeVerifiedData = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        toBeVerifiedData.setLength(value.length);
        for (int i = 0; i < toBeVerifiedData.getLength(); i++) {
            toBeVerifiedData.setOctet(i, value[i]);
        } // End 'for' statement
        value = parameterList.get(1).getEncodedParameter();
        OctetstringValue signature = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        signature.setLength(value.length);
        for (int i = 0; i < signature.getLength(); i++) {
            signature.setOctet(i, value[i]);
        } // End 'for' statement
        value = parameterList.get(2).getEncodedParameter();
        OctetstringValue ecdsaNistp256PublicKeyX = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        ecdsaNistp256PublicKeyX.setLength(value.length);
        for (int i = 0; i < ecdsaNistp256PublicKeyX.getLength(); i++) {
            ecdsaNistp256PublicKeyX.setOctet(i, value[i]);
        } // End 'for' statement
        value = parameterList.get(3).getEncodedParameter();
        OctetstringValue ecdsaNistp256PublicKeyY = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        ecdsaNistp256PublicKeyY.setLength(value.length);
        for (int i = 0; i < ecdsaNistp256PublicKeyY.getLength(); i++) {
            ecdsaNistp256PublicKeyY.setOctet(i, value[i]);
        } // End 'for' statement
        BooleanValue result = _extfuncsImpl.fx_verifyWithEcdsaNistp256WithSha256(toBeVerifiedData, signature, ecdsaNistp256PublicKeyX, ecdsaNistp256PublicKeyY);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) } );
        
    } // End of method exec_fx_verifyWithEcdsaNistp256WithSha256

    private void exec_signWithEcdsaNistp256WithSha256(TriParameterList parameterList, TriParameter returnValue) {
        
        // Execute the function
        byte[] value = parameterList.get(0).getEncodedParameter();
        OctetstringValue toBeSignedData = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        toBeSignedData.setLength(value.length);
        for (int i = 0; i < toBeSignedData.getLength(); i++) {
            toBeSignedData.setOctet(i, value[i]);
        } // End 'for' statement
        value = parameterList.get(1).getEncodedParameter();
        OctetstringValue privateKey = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        privateKey.setLength(value.length);
        for (int i = 0; i < privateKey.getLength(); i++) {
            privateKey.setOctet(i, value[i]);
        } // End 'for' statement
//        IntegerValueEx privateKey = (IntegerValueEx) TciProvider.getInstance().getTciCDRequired().getInteger().newInstance();
//        privateKey.setInt64(ByteHelper.byteArrayToLong(value));
        OctetstringValue result = _extfuncsImpl.fx_signWithEcdsaNistp256WithSha256(toBeSignedData, privateKey);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(result.getLength() * Byte.SIZE);
        value = new byte[result.getLength()];
        for (int i = 0; i < value.length; i++) {
            value[i] = (byte) result.getOctet(i);
        } // End 'for' statement
        returnValue.setEncodedParameter(value);
        
    } // End of method exec_signWithEcdsaNistp256WithSha256
    
    private void exec_hashWithSha256(TriParameterList parameterList, TriParameter returnValue) {
        
        // Execute the function
        byte[] value = parameterList.get(0).getEncodedParameter();
        OctetstringValue toBeHashedData = (OctetstringValue) TciProvider.getInstance().getTciCDRequired().getOctetstring().newInstance();
        toBeHashedData.setLength(value.length);
        for (int i = 0; i < toBeHashedData.getLength(); i++) {
            toBeHashedData.setOctet(i, value[i]);
        } // End 'for' statement
        OctetstringValue result = _extfuncsImpl.fx_hashWithSha256(toBeHashedData);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(result.getLength() * Byte.SIZE);
        value = new byte[result.getLength()];
        for (int i = 0; i < value.length; i++) {
            value[i] = (byte) result.getOctet(i);
        } // End 'for' statement
        returnValue.setEncodedParameter(value);
        
    } // End of method exec_hashWithSha256
    
    private void exec_dms2dd(TriParameterList parameterList, TriParameter returnValue) {
        
        // Prepare arguments
        ITciCDWrapper wrapper = TciCDWrapperFactory.getTciCDInstance();
        IntegerValue degrees = wrapper.setInteger(new BigInteger(parameterList.get(0).getEncodedParameter()));
        IntegerValue minutes = wrapper.setInteger(new BigInteger(parameterList.get(1).getEncodedParameter()));
        FloatValue seconds = wrapper.getFloat();
        seconds.setFloat(ByteHelper.byteArrayToFloat(parameterList.get(2).getEncodedParameter()));
        OctetstringValue latlon = wrapper.getOctetstring();
        latlon.setLength(1);
        latlon.setOctet(0, parameterList.get(3).getEncodedParameter()[0]);
        FloatValue result = _extfuncsImpl.fx_dms2dd(degrees, minutes, seconds, latlon);
        
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Float.SIZE);
        returnValue.setEncodedParameter(ByteHelper.floatToByteArray(result.getFloat()));
        
    } // End of method exec_dms2dd
    
    private void exec_isValidPolygonalRegion(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode region parameter
        ITciCDWrapper wrapper = TciCDWrapperFactory.getTciCDInstance();
        RecordOfValue region = wrapper.getRecordOfValue("LibItsSecurity_TypesAndValues.PolygonalRegion");
        byte[] value = parameterList.get(0).getEncodedParameter();
        byte length = value[0];
        int offset = 1;
        while (offset < length) { // TODO Create a method to fill PolygonRegions
            // WGS84 Latitude
            int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            // WGS84 Longitude
            int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            RecordValue twoDLocation = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.TwoDLocation");
            twoDLocation.setField("latitude", wrapper.setInteger(latitude));
            twoDLocation.setField("longitude", wrapper.setInteger(longitude));
        }
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_isValidPolygonalRegion(region);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_isValidPolygonalRegion
    
    private void exec_isPolygonalRegionInside(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode parent parameter
        // Execute the function
        ITciCDWrapper wrapper = TciCDWrapperFactory.getTciCDInstance();
        RecordOfValue parent = wrapper.getRecordOfValue("LibItsSecurity_TypesAndValues.PolygonalRegion");
        byte[] value = parameterList.get(0).getEncodedParameter();
        byte length = value[0];
        int offset = 1;
        while (offset < length) { // TODO Create a method to fill PolygonRegions
            // WGS84 Latitude
            int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            // WGS84 Longitude
            int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            RecordValue twoDLocation = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.TwoDLocation");
            twoDLocation.setField("latitude", wrapper.setInteger(latitude));
            twoDLocation.setField("longitude", wrapper.setInteger(longitude));
            parent.appendField(twoDLocation);
        } // End of 'while' statement
        // Decode region parameter
        RecordOfValue region = wrapper.getRecordOfValue("LibItsSecurity_TypesAndValues.PolygonalRegion");
        value = ByteHelper.extract(parameterList.get(0).getEncodedParameter(), offset, parameterList.get(0).getEncodedParameter().length - offset);
        length = value[0];
        offset = 1;
        while (offset < length) { // TODO Create a method to fill PolygonRegions
            // WGS84 Latitude
            int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            // WGS84 Longitude
            int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            RecordValue twoDLocation = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.TwoDLocation");
            twoDLocation.setField("latitude", wrapper.setInteger(latitude));
            twoDLocation.setField("longitude", wrapper.setInteger(longitude));
            region.appendField(twoDLocation);
        } // End of 'while' statement
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_isPolygonalRegionInside(parent, region);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_isPolygonalRegionInside
    
    private void exec_isLocationInsideCircularRegion(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode rectangularRegions parameter
        ITciCDWrapper wrapper = TciCDWrapperFactory.getTciCDInstance();
        byte[] value = parameterList.get(0).getEncodedParameter();
        int offset = 0;
        // Center
        RecordValue center = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.TwoDLocation");
        // WGS84 Latitude
        int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        // WGS84 Longitude
        int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        center.setField("latitude", wrapper.setInteger(latitude));
        center.setField("longitude", wrapper.setInteger(longitude));
        
        // Radius
        int radius = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Short.SIZE / Byte.SIZE));
        
        RecordValue circularRegion = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.CircularRegion");
        circularRegion.setField("center", center);
        circularRegion.setField("radius", wrapper.setInteger(radius));
        // Decode location parameter
        value = parameterList.get(1).getEncodedParameter();
        offset = 0;
        // WGS84 Latitude
        latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        // WGS84 Longitude
        longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        RecordValue location = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.ThreeDLocation");
        location.setField("latitude", wrapper.setInteger(latitude));
        location.setField("longitude", wrapper.setInteger(longitude));
        OctetstringValue elevation = wrapper.getOctetstring();
        elevation.setLength(2);
        elevation.setOctet(0, 0x00); elevation.setOctet(1, 0x00); 
        location.setField("elevation", elevation);
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_isLocationInsideCircularRegion(circularRegion, location);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_isLocationInsideCircularRegion
    
    private void exec_isLocationInsideRectangularRegion(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode rectangularRegions parameter
        ITciCDWrapper wrapper = TciCDWrapperFactory.getTciCDInstance();
        RecordOfValue rectangularRegions = wrapper.getRecordOfValue("LibItsSecurity_TypesAndValues.RectangularRegions");
        byte[] value = parameterList.get(0).getEncodedParameter();
        byte length = value[0];
        int offset = 1;
        while (offset < length) { // TODO Create a method to fill RectangularRegions
            // northwest
            RecordValue northwest = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.TwoDLocation");
            // WGS84 Latitude
            int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            // WGS84 Longitude
            int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            northwest.setField("latitude", wrapper.setInteger(latitude));
            northwest.setField("longitude", wrapper.setInteger(longitude));
            
            // southeast
            RecordValue southeast = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.TwoDLocation");
            // WGS84 Latitude
            latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            // WGS84 Longitude
            longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            southeast.setField("latitude", wrapper.setInteger(latitude));
            southeast.setField("longitude", wrapper.setInteger(longitude));
            
            RecordValue rectangularRegion = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.RectangularRegion");
            rectangularRegion.setField("northwest", northwest);
            rectangularRegion.setField("southeast", southeast);
            
            rectangularRegions.appendField(rectangularRegion);
        } // End of 'while' statement
        // Decode location parameter
//        ByteHelper.dump("exec_isLocationInsidePolygonalRegion: ", parameterList.get(1).getEncodedParameter());
        value = parameterList.get(1).getEncodedParameter();
        offset = 0;
        // WGS84 Latitude
        int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        // WGS84 Longitude
        int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        RecordValue location = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.ThreeDLocation");
        location.setField("latitude", wrapper.setInteger(latitude));
        location.setField("longitude", wrapper.setInteger(longitude));
        OctetstringValue elevation = wrapper.getOctetstring();
        elevation.setLength(2);
        elevation.setOctet(0, 0x00); elevation.setOctet(1, 0x00); 
        location.setField("elevation", elevation);
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_isLocationInsideRectangularRegion(rectangularRegions, location);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_isLocationInsideRectangularRegion
    
    private void exec_isLocationInsidePolygonalRegion(TriParameterList parameterList, TriParameter returnValue) {
        
        // Execute the function
        ITciCDWrapper wrapper = TciCDWrapperFactory.getTciCDInstance();
        RecordOfValue polygonalArea = wrapper.getRecordOfValue("LibItsSecurity_TypesAndValues.PolygonalRegion");
        byte[] value = parameterList.get(0).getEncodedParameter();
        byte length = value[0];
        int offset = 1;
        while (offset < length) { // TODO Create a method to fill PolygonRegions
            // WGS84 Latitude
            int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            // WGS84 Longitude
            int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
            offset += Integer.SIZE / Byte.SIZE;
            RecordValue twoDLocation = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.TwoDLocation");
            twoDLocation.setField("latitude", wrapper.setInteger(latitude));
            twoDLocation.setField("longitude", wrapper.setInteger(longitude));
            polygonalArea.appendField(twoDLocation);
        } // End of 'while' statement
//        ByteHelper.dump("exec_isLocationInsidePolygonalRegion: ", parameterList.get(1).getEncodedParameter());
        value = parameterList.get(1).getEncodedParameter();
        offset = 0;
        // WGS84 Latitude
        int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        // WGS84 Longitude
        int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        RecordValue location = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.ThreeDLocation");
        location.setField("latitude", wrapper.setInteger(latitude));
        location.setField("longitude", wrapper.setInteger(longitude));
        OctetstringValue elevation = wrapper.getOctetstring();
        elevation.setLength(2);
        elevation.setOctet(0, 0x00); elevation.setOctet(1, 0x00); 
        location.setField("elevation", elevation);
        BooleanValue result = _extfuncsImpl.fx_isLocationInsidePolygonalRegion(polygonalArea, location);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_isLocationInsidePolygonalRegion
    
    private void exec_isLocationInsideIdentifiedRegion(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode rectangularRegions parameter
        ITciCDWrapper wrapper = TciCDWrapperFactory.getTciCDInstance();
        byte[] value = parameterList.get(0).getEncodedParameter();
        int offset = 0;
        // RegionDictionary
        EnumeratedValue region_dictionary = wrapper.getEnumValue("LibItsSecurity_TypesAndValues.RegionDictionary");
        region_dictionary.setInt(value[offset++]);
        // region_identifier
        int region_identifier = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Short.SIZE / Byte.SIZE));
        offset += Short.SIZE / Byte.SIZE;
        // local_region
        int local_region;
        if ((value[offset] & 0x80) == 0x00) { // One byte length
            local_region = value[offset];
        } else {
            // TODO Process it as IntX, not as 4 bytes integer
            value[offset] &= 0x7f;
            local_region = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Short.SIZE / Byte.SIZE));
        }
        RecordValue identifiedRegion = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.IdentifiedRegion");
        identifiedRegion.setField("region_dictionary", region_dictionary);
        identifiedRegion.setField("region_identifier", wrapper.setInteger(region_identifier));
        identifiedRegion.setField("local_region", wrapper.setInteger(local_region));
        
        // Decode location parameter
        value = parameterList.get(1).getEncodedParameter();
        offset = 0;
        // WGS84 Latitude
        int latitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        // WGS84 Longitude
        int longitude = ByteHelper.byteArrayToInt(ByteHelper.extract(value, offset, Integer.SIZE / Byte.SIZE));
        offset += Integer.SIZE / Byte.SIZE;
        RecordValue location = wrapper.getRecordValue("LibItsSecurity_TypesAndValues.ThreeDLocation");
        location.setField("latitude", wrapper.setInteger(latitude));
        location.setField("longitude", wrapper.setInteger(longitude));
        OctetstringValue elevation = wrapper.getOctetstring();
        elevation.setLength(2);
        elevation.setOctet(0, 0x00); elevation.setOctet(1, 0x00); 
        location.setField("elevation", elevation);
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_isLocationInsideIdentifiedRegion(identifiedRegion, location);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_isLocationInsideIdentifiedRegion
    
    private void exec_loadCertificates(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode parameter
        CharstringValue rootDirectory = TciCDWrapperFactory.getTciCDInstance().getCharstringValue();
        String str = new String(parameterList.get(0).getEncodedParameter());
        rootDirectory.setString(str);
        
        CharstringValue configId = TciCDWrapperFactory.getTciCDInstance().getCharstringValue();
        str = new String(parameterList.get(1).getEncodedParameter());
        configId.setString(str); 
        
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_loadCertificates(rootDirectory, configId);
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_parseIpv6Address
    
    private void exec_unloadCertificates(TriParameterList parameterList, TriParameter returnValue) {
        
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_unloadCertificates();
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_parseIpv6Address
    
    private void exec_readCertificate(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode parameter
        CharstringValue certificateId = TciCDWrapperFactory.getTciCDInstance().getCharstringValue();
        String str = new String(parameterList.get(0).getEncodedParameter());
        certificateId.setString(str);
        
        OctetstringValue certificate = TciCDWrapperFactory.getTciCDInstance().getOctetstring();
        
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_readCertificate(certificateId, certificate);
        
        parameterList.get(1).setNumberOfBits(certificate.getLength() * Byte.SIZE);
        byte[] value = new byte[certificate.getLength()];
        for (int i = 0; i < value.length; i++) {
            value[i] = (byte) certificate.getOctet(i);
        }// End of 'for'statement
        parameterList.get(1).setEncodedParameter(value);
            
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_parseIpv6Address
    
    private void exec_readCertificateDigest(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode parameter
        CharstringValue certificateId = TciCDWrapperFactory.getTciCDInstance().getCharstringValue();
        String str = new String(parameterList.get(0).getEncodedParameter());
        certificateId.setString(str);
        
        OctetstringValue digest = TciCDWrapperFactory.getTciCDInstance().getOctetstring();
        
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_readCertificateDigest(certificateId, digest);
        
        parameterList.get(1).setNumberOfBits(digest.getLength() * Byte.SIZE);
        byte[] value = new byte[digest.getLength()];
        for (int i = 0; i < value.length; i++) {
            value[i] = (byte) digest.getOctet(i);
        }// End of 'for'statement
        parameterList.get(1).setEncodedParameter(value);
            
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_readCertificateDigest
    
    private void exec_readSigningKey(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode parameter
        CharstringValue keysId = TciCDWrapperFactory.getTciCDInstance().getCharstringValue();
        String str = new String(parameterList.get(0).getEncodedParameter());
        keysId.setString(str);
        
        OctetstringValue privateKey = TciCDWrapperFactory.getTciCDInstance().getOctetstring();
        
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_readSigningKey(keysId, privateKey);
        
        parameterList.get(1).setNumberOfBits(privateKey.getLength() * Byte.SIZE);
        byte[] value = new byte[privateKey.getLength()];
        for (int i = 0; i < value.length; i++) {
            value[i] = (byte) privateKey.getOctet(i);
        }// End of 'for'statement
        parameterList.get(1).setEncodedParameter(value);
        
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_readSigningKeys
    
    private void exec_readEncryptingKey(TriParameterList parameterList, TriParameter returnValue) {
        
        // Decode parameter
        CharstringValue keysId = TciCDWrapperFactory.getTciCDInstance().getCharstringValue();
        String str = new String(parameterList.get(0).getEncodedParameter());
        keysId.setString(str);
        
        OctetstringValue privateKey = TciCDWrapperFactory.getTciCDInstance().getOctetstring();
        
        // Execute the function
        BooleanValue result = _extfuncsImpl.fx_readEncryptingKey(keysId, privateKey);
        
        parameterList.get(1).setNumberOfBits(privateKey.getLength() * Byte.SIZE);
        byte[] value = new byte[privateKey.getLength()];
        for (int i = 0; i < value.length; i++) {
            value[i] = (byte) privateKey.getOctet(i);
        }// End of 'for'statement
        parameterList.get(1).setEncodedParameter(value);
        
        // Return code
        returnValue.setParameterPassingMode(TriParameterPassingMode.TRI_OUT);
        returnValue.setNumberOfBits(Byte.SIZE);
        returnValue.setEncodedParameter(new byte[] { (byte)((result.getBoolean() == true) ? 0x01 : 0x00) });
        
    } // End of method exec_readSigningKeys
    
    private void exec_parseIpv6Address(TriParameterList parameterList, TriParameter returnValue) {
        
    } // End of method exec_parseIpv6Address
    
    @Override
    public TriStatus triBeginWait(long arg0, TriComponentId arg1) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triNextSampling(long arg0, TriPortId arg1) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triReadClock(TriLong arg0) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triStartClock(long arg0) {
        throw new RuntimeException();
    }

    @Override
    public void triSAErrorReq(String arg0) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triGetStreamValue(TriComponentId arg0, TriPortId arg1, TriAddress arg2, TriMessage arg3) {
        throw new RuntimeException();
    }

    @Override
    public TriStatus triSetStreamValue(TriComponentId arg0, TriPortId arg1, TriAddress arg2, TriMessage arg3) {
        throw new RuntimeException();
    }
    
} // End of class PluginAdapter
