package org.etsi.ttcn.extfunc.its;

import org.etsi.common.ByteHelper;
import org.etsi.ttcn.codec.core.ITciCDWrapper;
import org.etsi.ttcn.codec.core.TciCDWrapperFactory;
import org.etsi.ttcn.tci.BooleanValue;
import org.etsi.ttcn.tci.EnumeratedValue;
import org.etsi.ttcn.tci.OctetstringValue;
import org.etsi.ttcn.tci.RecordOfValue;
import org.etsi.ttcn.tci.RecordValue;
import org.etsi.ttcn.tri.TriParameter;
import org.etsi.ttcn.tri.TriParameterList;
import org.etsi.ttcn.tri.TriParameterPassingMode;

// This class implements complex-to-decode xfs
public class ExecItsExternalFunctionsProvider extends ItsExternalFunctionsProvider {

    public void exec_fx_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 = 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
    
    public void exec_fx_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 = 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
    
    public void exec_fx_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 = 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
    
    public void exec_fx_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 = 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
    
    public void exec_fx_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 = 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
    
    public void exec_fx_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 = 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
}
