module ItsRSUsSimulator_TestCases {
    
    // Libcommon
    import from LibCommon_VerdictControl all;
    import from LibCommon_Sync all;
    
    // LibIts
    import from ITS_Container language "ASN.1:1997" all;
    import from CAM_PDU_Descriptions language "ASN.1:1997" all;
    import from IEEE1609dot2BaseTypes language "ASN.1:1997" all;
    import from IEEE1609dot2 language "ASN.1:1997" all;
    import from EtsiTs103097Module language "ASN.1:1997" all;
    
    // LibItsCommon
    import from LibItsCommon_Functions all;
    
    // LibItsGeoNetworking
    import from LibItsGeoNetworking_TestSystem all;
    import from LibItsGeoNetworking_TypesAndValues all;
    import from LibItsGeoNetworking_Templates all;
    
    // LibItsCam
    import from LibItsCam_EncdecDeclarations all;
    
    // LibItsDenm
    import from LibItsDenm_Templates all;
    
    // LibItsSecurity
    import from LibItsSecurity_TypesAndValues all;
    import from LibItsSecurity_Functions all;
    
    // AtsRSUsSimulator
    import from ItsRSUsSimulator_TypesAndValues all;
    import from ItsRSUsSimulator_Templates all;
    import from ItsRSUsSimulator_TestSystem all;
    import from ItsRSUsSimulator_Functions all;
    import from ItsRSUsSimulator_Pics all;
    import from ItsRSUsSimulator_Pixits all;
    
    testcase TC_RSUSIMU_BV_01() runs on ItsRSUsSimulator system ItsRSUsSimulatorSystem {
        // Local variables
        var template (value) GnRawPayload v_payload;
        var GeoNetworkingInd v_gnInd;
        //var CfEvent v_cfEvent;
        
        // Test control
        
        // Test component configuration
        ItsRSUsSimulator_Functions.f_cf01Up();
            
        // Test adapter configuration
        
        // Preamble
        f_selfOrClientSyncAndVerdictPreamble(c_prDone, e_success);
        
        // Test Body
        if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].beacon)) {
            tc_beacon.start;
        }
        if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].cam)) {
            tc_cam.start;
        }
        if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].denms)) {
            tc_denm.start;
        }
        if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].mapem)) {
            tc_mapem.start;
        }
        if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].spatems)) {
            tc_spatem.start;
        }
        if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].ivim)) {
            tc_ivim.start;
        }
        /* if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].evcsn)) { */
        /*     tc_evcsn.start; */
        /* } */

        geoNetworkingPort.clear;
        alt {
            /*[] geoNetworkingPort.receive(
                mw_geoNwInd(
                    mw_geoNwPdu(
                        mw_geoNwTsbPacketWithNextHeader_srem(
                            ?,
                            ?,
                            e_btpB
            )))) -> value v_gnInd { // Receive a SREM message
                //log("*** " & testcasename() & ": DEBUG: Processing SREM ***");
                f_processSrem(v_gnInd.msgIn);
                repeat;
            }
            [] geoNetworkingPort.receive(
                mw_geoNwInd(
                    mw_geoNwPdu(
                        mw_geoNwTsbPacketWithNextHeader_denm(
                            ?,
                            ?,
                            e_btpB // TODO Refined to exclude RSU StationID
            )))) -> value v_gnInd { // Receive a DENM message
                //log("*** " & testcasename() & ": DEBUG: Processing DENM ***");
                // Nothing to do, just for logging purposes
                repeat;
                }*/
            [] geoNetworkingPort.receive(mw_geoNwInd(?)) -> value v_gnInd { // Receive a message
                log("*** " & testcasename() & ": DEBUG: Recieving unsollicited message ***");
                // Nothing to do, just for logging purposes
                repeat;
            }
            /*[] cfPort.receive(CfEvent:?) -> value v_cfEvent {
                //log("*** " & testcasename() & ": DEBUG: Configuration port command:", v_cfEvent, " ***");
                if (f_process_cf_event(v_cfEvent) == true) {
                    repeat;
                }
            }*/
            [vc_beacon == true] tc_beacon.timeout {
                log("*** " & testcasename() & ": DEBUG: Processing BEACON ***");
                f_prepare_beacon(v_payload);
                f_send(v_payload, PICS_BEACON_ITS_AID);
                tc_beacon.start;
                repeat;
            }
            [vc_cam == true] tc_cam.timeout {
                //log("*** " & testcasename() & ": DEBUG: Processing CAM ***");
                f_prepare_cam(v_payload);
                f_send(v_payload, PICS_CAM_ITS_AID);
                tc_cam.start;
                repeat;
            }
            [vc_denm == true] tc_denm.timeout {
                //log("*** " & testcasename() & ": DEBUG: Processing DENM ***");
                for (var integer v_i := 0; v_i < lengthof(vc_rsuMessagesValueList[vc_rsu_id].denms); v_i := v_i + 1) {
                    f_prepare_denm(v_payload);
                    f_send(v_payload, PICS_DENM_ITS_AID);
                } // End of 'for' 
                tc_denm.start;
                repeat;
            }
            [vc_mapem == true] tc_mapem.timeout {
                log("*** " & testcasename() & ": DEBUG: Processing MAPEM ***");
                f_prepare_mapem(v_payload);
                f_send(v_payload, PICS_MAPEM_ITS_AID);
                tc_mapem.start;
                repeat;
            }
            [vc_spatem == true] tc_spatem.timeout {
                log("*** " & testcasename() & ": DEBUG: Processing SPATEM ***");
                for (var integer v_counter := 0; v_counter < lengthof(vc_rsuMessagesValueList[PX_RSU_ID - 1].spatems); v_counter := v_counter + 1) {
                    f_prepare_spatem(vc_rsuMessagesValueList[PX_RSU_ID - 1].spatems[v_counter], v_payload);
                    f_send(v_payload, PICS_SPATEM_ITS_AID);
                }
                tc_spatem.start;
                repeat;
            }
            [vc_ivim == true] tc_ivim.timeout {
                //log("*** " & testcasename() & ": DEBUG: Processing IVIM ***");
                f_prepare_ivim(v_payload);
                f_send(v_payload, PICS_IVIM_ITS_AID);
                tc_ivim.start;
                repeat;
            }
            /* [vc_evcsn == true] tc_evcsn.timeout { */
            /*     //log("*** " & testcasename() & ": DEBUG: Processing EVCSN ***"); */
            /*     f_prepare_evcsn(v_payload); */
            /*     f_send(v_payload, PICS_EVCSN_ITS_AID); */
            /*     tc_evcsn.start; */
            /*     repeat; */
            /* } */
        } // End of 'alt' statement
        f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_success);
        
        // Postamble
        if (ispresent(vc_rsuMessagesValueList[vc_rsu_id].denms)) {
            for (var integer v_i := 0; v_i < lengthof(vc_rsuMessagesValueList[vc_rsu_id].denms); v_i := v_i + 1) {
                f_prepare_denm(v_payload, true);
                f_send(v_payload, PICS_DENM_ITS_AID);
            } // End of 'for' statement
        }
        ItsRSUsSimulator_Functions.f_cf01Down();
        
    } // End of TC_RSUSIMU_BV_01
    
    /**
     * @remark: All PICS_GENERATE_xxx = false
     *          PICS_CAM_FREQUENCY = 0.1
     */
    testcase TC_RSUSIMU_BV_02() runs on ItsRSUsSimulator system ItsRSUsSimulatorSystem {
        // Local variables
        const SequenceOfRectangularRegion c_detectionArea := { valueof(PICS_UC6_CAM_DETECTION_AREA_Z2) }; // PICS_UC6_CAM_DETECTION_AREA_Z1
        var VehiclesSimulator v_vehicles := {};
        var integer v_stationID;
        var boolean v_isInDetectionZone;
        var boolean v_notProcessed;
        var integer v_idx;
        var ThreeDLocation v_location;
        var GeoNetworkingInd v_gnInd;
        //var CfEvent v_cfEvent;
        
        // Test control
        if (f_isLocationInsideRectangularRegion(c_detectionArea, PICS_UC6_COLLISION_POINT_Z2) == false) { //PICS_UC6_COLLISION_POINT_Z1
            log("Collision location is outside of the area");
            stop;
        }
        
        // Test component configuration
        ItsRSUsSimulator_Functions.f_cf01Up();
            
        // Test adapter configuration
        
        // Preamble
        for (v_idx := 0; v_idx < SIMULTANEOUS_VEHICLE_NUM; v_idx := v_idx + 1) { 
            v_vehicles[v_idx].detected := false;
            v_vehicles[v_idx].detectionCounter := 0;
            v_vehicles[v_idx].stationId := 0;
        } // End of 'for' statement
        f_selfOrClientSyncAndVerdictPreamble(c_prDone, e_success);
        
        // Test Body
        tc_ac.start;
        geoNetworkingPort.clear;
        alt {
            [] geoNetworkingPort.receive(
                mw_geoNwInd(
                    mw_geoNwPdu(
                        mw_geoNwShbPacketWithNextHeader_cam(
                            -,
                            -,
                            e_btpB
            )))) -> value v_gnInd { // Receive a CAM message
                var CAM v_camValue; // Assume BTP/CAM payload is well formed
                var bitstring v_payload;
                var integer v_result;
                
                tc_ac.stop;
                //log("v_gnInd = ", v_gnInd);
                v_payload := oct2bit(substr(v_gnInd.msgIn.gnPacket.packet.payload, 4, lengthof(v_gnInd.msgIn.gnPacket.packet.payload) - 4));
                v_result := decvalue(v_payload, v_camValue);
                if (isbound(v_camValue)) {
                    if (PICS_USE_LPV == true) {
                        v_location := {
                            v_gnInd.msgIn.gnPacket.packet.extendedHeader.shbHeader.srcPosVector.latitude,
                            v_gnInd.msgIn.gnPacket.packet.extendedHeader.shbHeader.srcPosVector.longitude,
                            0
                        };
                    } else {
                        v_location := {
                            v_camValue.cam.camParameters.basicContainer.referencePosition.latitude,
                            v_camValue.cam.camParameters.basicContainer.referencePosition.longitude,
                            v_camValue.cam.camParameters.basicContainer.referencePosition.altitude.altitudeValue
                        };
                    }
                    v_stationID := v_camValue.header.stationID;
                    
                    // Check if it already processed
                    v_notProcessed := true;
                    for (v_idx := 0; v_idx < SIMULTANEOUS_VEHICLE_NUM and v_vehicles[v_idx].stationId != 0; v_idx := v_idx + 1) { 
                        if (v_vehicles[v_idx].stationId == v_stationID) {
                            v_notProcessed := not(v_vehicles[v_idx].detected);
                            break;
                        }
                    } // End of 'for' statement
                    if (v_idx < SIMULTANEOUS_VEHICLE_NUM) {
                        v_vehicles[v_idx].stationId := v_stationID;
                    } else if (v_idx == SIMULTANEOUS_VEHICLE_NUM) {
                        log("*** " & testcasename() & ": ERROR: Increase the size of the file ***");
                        stop;
                    }
                    //v_isInDetectionZone := f_isLocationInsideRectangularRegion(c_detectionArea, v_location);
                    v_isInDetectionZone := f_isInApproach(PX_UC6_APPROACH_POINT_Z2, v_location, 100.0);
                    log("v_idx:               ", v_idx);
                    log("v_vehicles[v_idx]:   ", v_vehicles[v_idx]);
                    log("v_notProcessed:      ", v_notProcessed);
                    log("v_isInDetectionZone: ", v_isInDetectionZone);
                    if (v_notProcessed == true) { // Vehicle not processed yet                    
                        if (v_isInDetectionZone == true) { // Check if it entered into the rectangular area
                            /*if (v_vehicles[v_idx].detectionCounter == 0) { // First detection, wait one CAM to confrm detection
                                // Prepare component
                                v_vehicles[v_idx].detectionCounter := v_vehicles[v_idx].detectionCounter + 1;*/
                                v_vehicles[v_idx].component_ := ItsRSUsSimulator.create("Node" & int2char(v_idx + 65)) alive;
                                // Wait next message in detection area
                            /*} else {*/
                                log("*** " & testcasename() & ": DEBUG: StationID: " & int2str(v_stationID) & " is entering in area ***");
                                v_vehicles[v_idx].detectionCounter := 1;/*0;*/
                                v_vehicles[v_idx].detected := true;
                                v_vehicles[v_idx].component_.start(f_startVehicleSimulator(v_vehicles[v_idx].component_, v_idx, v_camValue));
                            /*}*/
                        } else {
                            // Nothing to do
    //                        log("*** " & testcasename() & ": DEBUG: StationID: " & int2str(v_stationID) & " is outside of area ***");
                            v_vehicles[v_idx].detectionCounter := 0;
                            v_vehicles[v_idx].detected := false;
                            //v_vehicles[v_idx].stationId := 0;
                        }
                    } else if (v_isInDetectionZone == false) {
                        if (v_vehicles[v_idx].detected == true) { // Remove station if from v_stationIDs
                            log("*** " & testcasename() & ": DEBUG: Remove stationID: " & int2str(v_stationID) & " ***");
                            v_vehicles[v_idx].detectionCounter := 0;
                            v_vehicles[v_idx].detected := false;
                            //v_vehicles[v_idx].stationId := 0;
                            v_vehicles[v_idx].component_.kill;
                        } else {
                            v_vehicles[v_idx].detectionCounter := 0;
                            v_vehicles[v_idx].detected := false;
                            //v_vehicles[v_idx].stationId := 0;
                        }
                    } else {
                        // Nothing to do
                        log("*** " & testcasename() & ": DEBUG: StationID: " & int2str(v_stationID) & " already processed ***");
                    }
                    tc_ac.start;
                    repeat;
                }
            }
            [] geoNetworkingPort.receive(
                mw_geoNwInd(
                    mw_geoNwPdu(
                       ?
            ))) -> value v_gnInd { // Receive a CAM message
                tc_ac.stop;
//                log("v_gnInd = ", v_gnInd);
                tc_ac.start;
                repeat;
            }
            /*[] cfPort.receive {
                tc_cam.stop;
                all component.stop;
            }*/
            [] tc_ac.timeout {
                log("*** " & testcasename() & ": DEBUG: No CAM message received ***");
                //all component.stop;
                tc_ac.start;
                repeat;
            }
        } // End of 'alt' statement
        f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_success);
        
        // Postamble
        for (v_idx := 0; v_idx < lengthof(v_vehicles); v_idx := v_idx + 1) {
            if (isbound(v_vehicles[v_idx].component_)) {
                v_vehicles[v_idx].component_.kill;
            }
        } // End of 'for' statement
        ItsRSUsSimulator_Functions.f_cf01Down();
        
    } // End of TC_RSUSIMU_BV_02
    
    group tc_RSUSIMU_BV_02 {
        
        function f_startVehicleSimulator(
                                         in ItsRSUsSimulator p_component,
                                         in integer p_vehicleIndex,
                                         in CAM p_camVehicle
        ) runs on ItsRSUsSimulator {
            
            // Local variables
            const SequenceOfRectangularRegion c_detectionArea := { valueof(PICS_UC6_CAM_DETECTION_AREA_Z2) }; // PICS_UC6_CAM_DETECTION_AREA_Z1
            var GeoNetworkingInd v_gnInd;
            var template (value) CAM v_camSimu;
            var ThreeDLocation v_location;
            var boolean v_inArea;
            
            f_initialiseVehicleSimulatorComponent(p_component, p_vehicleIndex, v_camSimu);
            
            tc_ac.start;
            geoNetworkingPort.clear;
            alt {
                [] geoNetworkingPort.receive(
                    mw_geoNwInd(
                        mw_geoNwPdu(
                            mw_geoNwShbPacketWithNextHeader_cam(
                                -,
                                -,
                                e_btpB,
                                mw_cam_stationID(
                                    -,
                                    p_camVehicle.header.stationID
                ))))) -> value v_gnInd { // Receive a CAM message from the processed vehicle
                    var CAM v_camValue; // Assume BTP/CAM payload is well formed
                    var bitstring v_payload;
                    var integer v_result;
                    
                    tc_ac.stop;
//                    log("*** " & testcasename() & ": DEBUG: Processing CAM #" & int2str(p_camVehicle.header.stationID) & " ***");
                    v_payload := oct2bit(substr(v_gnInd.msgIn.gnPacket.packet.payload, 4, lengthof(v_gnInd.msgIn.gnPacket.packet.payload) - 4));
                    v_result := decvalue(v_payload, v_camValue);
                    if (isbound(v_camValue)) {
                        if (PICS_USE_LPV == true) {
                            v_location := {
                                v_gnInd.msgIn.gnPacket.packet.extendedHeader.shbHeader.srcPosVector.latitude,
                                v_gnInd.msgIn.gnPacket.packet.extendedHeader.shbHeader.srcPosVector.longitude,
                                0
                            };
                        } else {
                            v_location := {
                                v_camValue.cam.camParameters.basicContainer.referencePosition.latitude,
                                v_camValue.cam.camParameters.basicContainer.referencePosition.longitude,
                                v_camValue.cam.camParameters.basicContainer.referencePosition.altitude.altitudeValue
                            };
                        }
                        f_mirror_and_send_vehicle_cam(v_camSimu, v_camValue, v_location/*To be removed*/);
                        // Check if the behicule leave the area
                        //v_inArea := f_isLocationInsideRectangularRegion(c_detectionArea, v_location);
                        v_inArea := f_isInApproach(PX_UC6_APPROACH_POINT_Z2, v_location, 100.0);
    
                        log("*** " & testcasename() & ": DEBUG: In area: ", v_inArea, " ***");
                        if (v_inArea == true) {
                            tc_ac.start;
                            repeat;
                        } else {
                            log("*** " & testcasename() & ": PASS: Terminate component for #" & int2str(p_camVehicle.header.stationID) & " ***");
                            f_setVerdict(e_success);
                        }
                    }
                }
                /*[] cfPort.receive {
                    tc_wait.stop;
                    repeat;
                }*/
                [] tc_ac.timeout {
                    log("*** " & testcasename() & ": DEBUG: No CAM message received ***");
                }
            } // End of 'altstep' statement
            
            f_uninitialiseVehicleSimulatorComponent(p_component);
            log("*** " & testcasename() & ": DEBUG: Exit ***");
            
        } // End of f_startVehicleSimulator
        
    } // End of group tc_RSUSIMU_BV_02 
    
} // End of module ItsRSUsSimulator_TestCases
