------------------------------------------------------------------
-- VEAF transport mission command and functions for DCS World
-- By zip (2018)
--
-- Features:
-- ---------
-- * Listen to marker change events and creates a transport training mission, with optional parameters
-- * Possibilities :
-- *    - create a zone with cargo to pick up, another with friendly troops awaiting their cargo, and optionaly enemy units on the way
--
-- See the documentation : https://veaf.github.io/documentation/
------------------------------------------------------------------

veafTransportMission = {}

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Global settings. Stores the script constants
-------------------------------------------------------------------------------------------------------------------------------------------------------------

--- Identifier. All output in DCS.log will start with this.
veafTransportMission.Id = "TRANSPORTMISSION"

--- Version.
veafTransportMission.Version = "1.8.0"

-- trace level, specific to this module
--veafTransportMission.LogLevel = "trace"

veaf.loggers.new(veafTransportMission.Id, veafTransportMission.LogLevel)

--- Key phrase to look for in the mark text which triggers the command.
veafTransportMission.Keyphrase = "_transport"

veafTransportMission.CargoTypes = {"ammo_cargo", "barrels_cargo", "m117_cargo", "oiltank_cargo", "uh1h_cargo" } --, "container_cargo", "fueltank_cargo" }

--- Number of seconds between each check of the friendly group ADF loop function
veafTransportMission.SecondsBetweenAdfLoops = 30

--- Number of seconds between each check of the friendly group watchdog function
veafTransportMission.SecondsBetweenWatchdogChecks = 15

--- Number of seconds between each smoke request on the target
veafTransportMission.SecondsBetweenSmokeRequests = 180

--- Number of seconds between each flare request on the target
veafTransportMission.SecondsBetweenFlareRequests = 120

--- Name of the friendly group that waits for the cargo
veafTransportMission.BlueGroupName = "Transport - Allied Group"

--- Name of the cargo units
veafTransportMission.BlueCargoName = "Cargo - Cargo unit"

--- Name of the enemy group that defends the way to the friendlies
veafTransportMission.RedDefenseGroupName = "Cargo - Enemy Air Defense Group"

--- Name of the enemy group that blocades the friendlies
veafTransportMission.RedBlocadeGroupName = "Cargo - Enemy Blocade Group"

veafTransportMission.RadioMenuName = "TRANSPORT MISSION"

veafTransportMission.AdfRadioSound = "l10n/DEFAULT/beacon.ogg"

veafTransportMission.AdfFrequency = 550000 -- in hz

veafTransportMission.AdfPower = 1000 -- in Watt

veafTransportMission.DoRadioTransmission = false -- set to true when radio transmissions will work

-- minimum authorized route distance ; missions shorter than this will not be authorized
veafTransportMission.MinimumRouteDistance = 15000 -- 15 km

-- size of the safe zone (no enemy group before this distance, in % of the total distance)
veafTransportMission.SafeZoneDistance = 0.6 -- 60%

-- size of the sqfe zone near drop zone (no enemy group after this distance from the drop zone)
veafTransportMission.DropZoneSafeZoneDistance = 5000 -- 5 km

-- an enemy group every xxx meters of the way (randomized)
veafTransportMission.EnemyDefenseDistanceStep = 3000 

-- enemies groups generated along the way are offset to xxx meters max (left or right, randomized)
veafTransportMission.LeftOrRightMaxOffset = 1500

-- enemies groups generated along the way are offset to xxx meters min (left or right, randomized)
veafTransportMission.LeftOrRightMinOffset = 500

-- enemies groups generated far from the way are offset to xxx meters max (left or right, randomized)
veafTransportMission.LeftOrRightMaxFarOffset = 7000

-- enemies groups generated far from the way are offset to xxx meters min (left or right, randomized)
veafTransportMission.LeftOrRightMinFarOffset = 3000

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Do not change anything below unless you know what you are doing!
-------------------------------------------------------------------------------------------------------------------------------------------------------------

-- Friendly group watchdog function id
veafTransportMission.friendlyGroupAliveCheckTaskID = 'none'

-- Friendly group ADF transmission loop function id
veafTransportMission.friendlyGroupAdfLoopTaskID = 'none'

--- Radio menus paths
veafTransportMission.targetMarkersPath = nil
veafTransportMission.targetInfoPath = nil
veafTransportMission.rootPath = nil

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Utility methods
-------------------------------------------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Event handler functions.
-------------------------------------------------------------------------------------------------------------------------------------------------------------

--- Function executed when a mark has changed. This happens when text is entered or changed.
function veafTransportMission.onEventMarkChange(eventPos, event)
    -- Check if marker has a text and the veafTransportMission.keyphrase keyphrase.
    if event.text ~= nil and event.text:lower():find(veafTransportMission.Keyphrase) then

        -- Analyse the mark point text and extract the keywords.
        local options = veafTransportMission.markTextAnalysis(event.text)

        if options then
            -- Check options commands
            if options.transportmission then
                -- check security
                if not veafSecurity.checkSecurity_L1(options.password) then return end
                -- create the mission
                veafTransportMission.generateTransportMission(eventPos, options.size, options.defense, options.blocade, options.from)
            end
        else
            -- None of the keywords matched.
            return
        end

        -- Delete old mark.
        veaf.loggers.get(veafTransportMission.Id):trace(string.format("Removing mark # %d.", event.idx))
        trigger.action.removeMark(event.idx)
    end
end

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Analyse the mark text and extract keywords.
-------------------------------------------------------------------------------------------------------------------------------------------------------------

--- Extract keywords from mark text.
function veafTransportMission.markTextAnalysis(text)

    -- Option parameters extracted from the mark text.
    local switch = {}
    switch.transportmission = false

    -- size ; number of cargo to be transported
    switch.size = 1

    -- defense [1-5] : air defense cover on the way (1 = light, 5 = heavy)
    switch.defense = 0

    -- blocade [1-5] : enemy blocade around the drop zone (1 = light, 5 = heavy)
    switch.blocade = 0

    -- start position, named point
    switch.from = nil

    -- password
    switch.password = nil

    -- Check for correct keywords.
    if text:lower():find(veafTransportMission.Keyphrase) then
        switch.transportmission = true
    else
        return nil
    end

    -- keywords are split by ","
    local keywords = veaf.split(text, ",")

    for _, keyphrase in pairs(keywords) do
        -- Split keyphrase by space. First one is the key and second, ... the parameter(s) until the next comma.
        local str = veaf.breakString(veaf.trim(keyphrase), " ")
        local key = str[1]
        local val = str[2]

        if key:lower() == "password" then
            -- Unlock the command
            veaf.loggers.get(veafTransportMission.Id):debug(string.format("Keyword password", val))
            switch.password = val
        end

        if switch.transportmission and key:lower() == "size" then
            -- Set size.
            veaf.loggers.get(veafTransportMission.Id):debug(string.format("Keyword size = %d", val))
            local nVal = tonumber(val)
            if nVal <= 5 and nVal >= 1 then
                switch.size = nVal
            end
        end

        if switch.transportmission and key:lower() == "defense" then
            -- Set defense.
            veaf.loggers.get(veafTransportMission.Id):debug(string.format("Keyword defense = %d", val))
            local nVal = tonumber(val)
            if nVal <= 5 and nVal >= 0 then
                switch.defense = nVal
            end
        end

        if switch.transportmission and key:lower() == "blocade" then
            -- Set blocade.
            veaf.loggers.get(veafTransportMission.Id):debug(string.format("Keyword blocade = %d", val))
            local nVal = tonumber(val)
            if nVal <= 5 and nVal >= 0 then
                switch.blocade = nVal
            end
        end

        if switch.transportmission and key:lower() == "from" then
            -- Set armor.
            veaf.loggers.get(veafTransportMission.Id):debug(string.format("Keyword from = %s", val))
            switch.from = val
        end
    end

    return switch
end

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- CAS target group generation and management
-------------------------------------------------------------------------------------------------------------------------------------------------------------

function veafTransportMission.doRadioTransmission(groupName)
    veaf.loggers.get(veafTransportMission.Id):trace("doRadioTransmission("..groupName..")")
    local group = Group.getByName(groupName)
    if group then
        veaf.loggers.get(veafTransportMission.Id):trace("Group is transmitting")
        local averageGroupPosition = veaf.getAveragePosition(groupName)
        veaf.loggers.get(veafTransportMission.Id):trace("averageGroupPosition=" .. veaf.vecToString(averageGroupPosition))
        trigger.action.radioTransmission(veafTransportMission.AdfRadioSound, averageGroupPosition, 0, false, veafTransportMission.AdfFrequency, veafTransportMission.AdfPower)
    end
    
    veafTransportMission.friendlyGroupAdfLoopTaskID = mist.scheduleFunction(veafTransportMission.doRadioTransmission, { groupName }, timer.getTime() + veafTransportMission.SecondsBetweenAdfLoops)
end

function veafTransportMission.generateFriendlyGroup(groupPosition)
    veafSpawn.doSpawnGroup(groupPosition, 0, "US infgroup", nil, "USA", 0, 0, 0, veafTransportMission.BlueGroupName, true, false, true, true)

    if veafTransportMission.DoRadioTransmission then
        veafTransportMission.doRadioTransmission(veafTransportMission.BlueGroupName)
    end
end

--- Generates an enemy defense group on the way to the drop zone
--- defenseLevel = 1 : 3-7 soldiers, GAZ-3308 transport
--- defenseLevel = 2 : 3-7 soldiers, BTR-80 APC
--- defenseLevel = 3 : 3-7 soldiers, chance of BMP-1 IFV, chance of Igla manpad
--- defenseLevel = 4 : 3-7 soldiers, big chance of BMP-1 IFV, big chance of Igla-S manpad, chance of ZU-23 on a truck
--- defenseLevel = 5 : 3-7 soldiers, BMP-1 IFV, big chance of Igla-S manpad, chance of ZSU-23-4 Shilka
function veafTransportMission.generateEnemyDefenseGroup(groupPosition, groupName, defenseLevel)
    local groupDefinition = {
            disposition = { h = 6, w = 6},
            units = {},
            description = groupName,
            groupName = groupName,
        }

    -- generate an infantry group
    local groupCount = math.random(3, 7)
    for _ = 1, groupCount do
        local rand = math.random(3)
        local unitType = nil
        if rand == 1 then
            unitType = 'Soldier RPG'
        elseif rand == 2 then
            unitType = 'Soldier AK'
        else
            unitType = 'Infantry AK'
        end
        table.insert(groupDefinition.units, { unitType })
    end

    -- add a transport vehicle or an APC/IFV
    if defenseLevel > 4 or (defenseLevel > 3 and math.random(100) > 33) or (defenseLevel > 2 and math.random(100) > 66) then
        table.insert(groupDefinition.units, { "BMP-1", cell=11, random = true })
    elseif defenseLevel > 1 then
        table.insert(groupDefinition.units, { "BTR-80", cell=11, random = true })
    else
        table.insert(groupDefinition.units, { "GAZ-3308", cell=11, random = true })
    end

    -- add manpads if needed
    if defenseLevel > 3 and math.random(100) > 33 then
        -- for defenseLevel = 4-5, spawn a modern Igla-S team
        table.insert(groupDefinition.units, { "SA-18 Igla-S comm", random = true })
        table.insert(groupDefinition.units, { "SA-18 Igla-S manpad", random = true })
    elseif defenseLevel > 2 and math.random(100) > 66 then
        -- for defenseLevel = 3, spawn an older Igla team
        table.insert(groupDefinition.units, { "SA-18 Igla comm", random = true })
        table.insert(groupDefinition.units, { "SA-18 Igla manpad", random = true })
    else
        -- for defenseLevel = 0, don't spawn any manpad
    end

    -- add an air defenseLevel vehicle
    if defenseLevel > 4 and math.random(100) > 66 then
        -- defenseLevel = 3-5 : add a Shilka
        table.insert(groupDefinition.units, { "ZSU-23-4 Shilka", cell = 3, random = true })
    elseif defenseLevel > 3 and math.random(100) > 66 then
        -- defenseLevel = 1 : add a ZU23 on a truck
        table.insert(groupDefinition.units, { "Ural-375 ZU-23", cell = 3, random = true })
    end

    groupDefinition = veafUnits.processGroup(groupDefinition)
    veafSpawn.doSpawnGroup(groupPosition, 0, groupDefinition, nil, "RUSSIA", 0, math.random(359), math.random(3,6), groupName, true, false, true, true)
end


--- Generates a transport mission
function veafTransportMission.generateTransportMission(targetSpot, size, defense, blocade, from)
    veaf.loggers.get(veafTransportMission.Id):debug("generateTransportMission(size = %s, defense=%s, blocade=%d, from=%s)", veaf.p(size), veaf.p(defense), veaf.p(blocade), veaf.p(from))
    veaf.loggers.get(veafTransportMission.Id):debug("generateTransportMission: targetSpot ", veaf.p(targetSpot))

    if veafTransportMission.friendlyGroupAliveCheckTaskID ~= 'none' then
        trigger.action.outText("A transport mission already exists !", 5)
        return
    end

    if not from then
        trigger.action.outText("The \"from\" keyword is mandatory !", 5)
        return
    end

    local startPoint = veafNamedPoints.getPoint(from)
    if not(startPoint) then
        trigger.action.outText("A point named "..from.." cannot be found !", 5)
        return
    end
    
    local friendlyUnits = {}
    local routeDistance = 0

    -- generate a friendly group around the target target spot
    local groupPosition = veaf.findPointInZone(targetSpot, 100, false)
    if groupPosition ~= nil then
        veaf.loggers.get(veafTransportMission.Id):trace("groupPosition=" .. veaf.vecToString(groupPosition))
        groupPosition = { x = groupPosition.x, z = groupPosition.y, y = 0 }
        groupPosition = veaf.placePointOnLand(groupPosition)
        veaf.loggers.get(veafTransportMission.Id):trace("groupPosition on land=" .. veaf.vecToString(groupPosition))

        -- compute player route to friendly group
        local vecAB = {x = groupPosition.x +- startPoint.x, y = 0, z = groupPosition.z - startPoint.z}
        routeDistance = mist.vec.mag(vecAB)
        veaf.loggers.get(veafTransportMission.Id):trace("routeDistance="..routeDistance)
        if routeDistance < veafTransportMission.MinimumRouteDistance then
            trigger.action.outText("This drop zone is too close ; you have to place it at least " .. veafTransportMission.MinimumRouteDistance / 1000 .. " km away from point "..from.." !", 5)
            return
        end

        veafTransportMission.generateFriendlyGroup(groupPosition)
    else
        veaf.loggers.get(veafTransportMission.Id):info("cannot find a suitable position for friendly group")
        return
    end

    -- generate cargo to be picked up near the player helo
    veaf.loggers.get(veafTransportMission.Id):debug("Generating cargo")
    local startPosition = veaf.placePointOnLand(startPoint)
    veaf.loggers.get(veafTransportMission.Id):trace("startPosition=" .. veaf.vecToString(startPosition))
    for i = 1, size do
        local spawnSpot = { x = startPosition.x + 50, z = startPosition.z + i * 10, y = startPosition.y }
        veaf.loggers.get(veafTransportMission.Id):trace("spawnSpot=" .. veaf.vecToString(spawnSpot))
        local cargoType = veafTransportMission.CargoTypes[math.random(#veafTransportMission.CargoTypes)]
        local cargoName = veafTransportMission.BlueCargoName .. " #" .. i
        veafSpawn.doSpawnCargo(spawnSpot, 0, cargoType, "USA")
    end
    veaf.loggers.get(veafTransportMission.Id):debug("Done generating cargo")

    -- generate enemy air defense on the way
    if defense > 0 then
        veaf.loggers.get(veafTransportMission.Id):debug("Generating air defense")

         -- place groups on the way
         local startingDistance = routeDistance * veafTransportMission.SafeZoneDistance -- enemy presence start after the safe zone
         local defendedDistance = routeDistance - veafTransportMission.DropZoneSafeZoneDistance - startingDistance
         local distanceStep = veafTransportMission.EnemyDefenseDistanceStep
         local nbSteps = math.floor(defendedDistance / distanceStep) 
         local groupNum = 1
         for stepNum = 1, nbSteps do
            local distanceFromStartingPoint = startingDistance + stepNum * distanceStep + math.random(distanceStep/5, 4*distanceStep/5)
            veaf.loggers.get(veafTransportMission.Id):trace("distanceFromStartingPoint="..distanceFromStartingPoint)

            -- place an enemy defense group along the way
            local offset = math.random(veafTransportMission.LeftOrRightMinOffset, veafTransportMission.LeftOrRightMaxOffset)
            if math.random(100) < 51 then 
               offset = -offset 
            end
            veaf.loggers.get(veafTransportMission.Id):trace("offset="..offset)
            local spawnPoint = veaf.computeCoordinatesOffsetFromRoute(startPoint, groupPosition, distanceFromStartingPoint, offset)
            local groupName = veafTransportMission.RedDefenseGroupName .. " #"  .. groupNum
            veafTransportMission.generateEnemyDefenseGroup(spawnPoint, groupName, defense)
            groupNum = groupNum + 1

            -- place a random number of defense groups further away
            local nbFarGroups = math.random(0,1)
            if defense > 4 then 
                nbFarGroups = math.random(1,3)
            end
            for _ = 1, nbFarGroups do
                local offset = math.random(veafTransportMission.LeftOrRightMinFarOffset, veafTransportMission.LeftOrRightMaxFarOffset)
                if math.random(100) < 51 then 
                   offset = -offset 
                end
                veaf.loggers.get(veafTransportMission.Id):trace("offset="..offset)
                local spawnPoint = veaf.computeCoordinatesOffsetFromRoute(startPoint, groupPosition, distanceFromStartingPoint, offset)
                local groupName = veafTransportMission.RedDefenseGroupName .. " #"  .. groupNum
                veafTransportMission.generateEnemyDefenseGroup(spawnPoint, groupName, defense)
                groupNum = groupNum + 1
            end

         end

        veaf.loggers.get(veafTransportMission.Id):debug("Done generating air defense")
    end

    -- generate enemy blocade forces
    if blocade > 0 then
        veaf.loggers.get(veafTransportMission.Id):debug("Generating blocade")
        -- TODO
        veaf.loggers.get(veafTransportMission.Id):debug("Done generating blocade")
    end

    -- add radio menu for drop zone information (by player group)
    veafRadio.addCommandToSubmenu('Drop zone information', veafTransportMission.rootPath, veafTransportMission.reportTargetInformation, nil, veafRadio.USAGE_ForGroup)

    -- add radio menus for commands
    veafRadio.addSecuredCommandToSubmenu('Skip current objective', veafTransportMission.rootPath, veafTransportMission.skip)
    veafTransportMission.targetMarkersPath = veafRadio.addSubMenu("Drop zone markers", veafTransportMission.rootPath)
    veafRadio.addCommandToSubmenu('Request smoke on drop zone', veafTransportMission.targetMarkersPath, veafTransportMission.smokeTarget)
    veafRadio.addCommandToSubmenu('Request illumination flare over drop zone', veafTransportMission.targetMarkersPath, veafTransportMission.flareTarget)

    local message = "See F10 radio menu for details\n" -- TODO
    trigger.action.outText(message,5)
    
    veafRadio.refreshRadioMenu()
    
    -- start checking for targets destruction
    veafTransportMission.friendlyGroupWatchdog()
end

--- Checks if the friendly group is still alive, and if not announces the failure of the transport mission
function veafTransportMission.friendlyGroupWatchdog() 
    local nbVehicles, nbInfantry = veafUnits.countInfantryAndVehicles(veafTransportMission.BlueGroupName)
    if nbVehicles + nbInfantry > 0 then
        ----veaf.loggers.get(veafTransportMission.Id):trace("Group is still alive with "..nbVehicles.." vehicles and "..nbInfantry.." soldiers")
        veafTransportMission.friendlyGroupAliveCheckTaskID = mist.scheduleFunction(veafTransportMission.friendlyGroupWatchdog,{},timer.getTime()+veafTransportMission.SecondsBetweenWatchdogChecks)
    else
        trigger.action.outText("Friendly group has been destroyed! The mission is a failure!", 5)
        veafTransportMission.cleanupAfterMission()
    end
end

function veafTransportMission.reportTargetInformation(unitName)
    -- generate information dispatch
    local nbVehicles, nbInfantry = veafUnits.countInfantryAndVehicles(veafTransportMission.BlueGroupName)

    local message =      "DROP ZONE : ressuply a group of " .. nbVehicles .. " vehicles and " .. nbInfantry .. " soldiers.\n"
    message = message .. "\n"
    if veafTransportMission.DoRadioTransmission then 
        message = message .. "NAVIGATION: They will transmit on 550 kHz every " .. veafTransportMission.SecondsBetweenAdfLoops .. " seconds.\n"
    end

    -- add coordinates and position from bullseye
    local averageGroupPosition = veaf.getAveragePosition(veafTransportMission.BlueGroupName)
    local lat, lon = coord.LOtoLL(averageGroupPosition)
    local mgrsString = mist.tostringMGRS(coord.LLtoMGRS(lat, lon), 3)
    local bullseye = mist.utils.makeVec3(mist.DBs.missionData.bullseye.blue, 0)
    local vec = {x = averageGroupPosition.x - bullseye.x, y = averageGroupPosition.y - bullseye.y, z = averageGroupPosition.z - bullseye.z}
    local dir = mist.utils.round(mist.utils.toDegree(mist.utils.getDir(vec, bullseye)), 0)
    local dist = mist.utils.get2DDist(averageGroupPosition, bullseye)
    local distMetric = mist.utils.round(dist/1000, 0)
    local distImperial = mist.utils.round(mist.utils.metersToNM(dist), 0)
    local fromBullseye = string.format('%03d', dir) .. ' for ' .. distMetric .. 'km /' .. distImperial .. 'nm'

    message = message .. "LAT LON (decimal): " .. mist.tostringLL(lat, lon, 2) .. ".\n"
    message = message .. "LAT LON (DMS)    : " .. mist.tostringLL(lat, lon, 0, true) .. ".\n"
    message = message .. "MGRS/UTM         : " .. mgrsString .. ".\n"
    message = message .. "FROM BULLSEYE    : " .. fromBullseye .. ".\n"
    message = message .. "\n"

    -- get altitude, qfe and wind information
    local altitude = veaf.getLandHeight(averageGroupPosition)
    --local qfeHp = mist.utils.getQFE(averageGroupPosition, false)
    --local qfeinHg = mist.utils.getQFE(averageGroupPosition, true)
    local windDirection, windStrength = veaf.getWind(veaf.placePointOnLand(averageGroupPosition))

    message = message .. 'DROP ZONE ALT       : ' .. altitude .. " meters.\n"
    --message = message .. 'TARGET QFW       : ' .. qfeHp .. " hPa / " .. qfeinHg .. " inHg.\n"
    local windText =     'no wind.\n'
    if windStrength > 0 then
        windText = string.format(
                         'from %s at %s m/s.\n', windDirection, windStrength)
    end
    message = message .. 'WIND OVER DROP ZONE : ' .. windText

    -- send message only for the unit
    veaf.outTextForUnit(unitName, message, 30)
end

--- add a smoke marker over the drop zone
function veafTransportMission.smokeTarget()
    veaf.loggers.get(veafTransportMission.Id):debug("smokeTarget()")
    veafSpawn.spawnSmoke(veaf.getAveragePosition(veafTransportMission.BlueGroupName), trigger.smokeColor.Green)
	trigger.action.outText('Copy smoke requested, GREEN smoke marks the drop zone!',5)
    veafRadio.delCommand(veafTransportMission.targetMarkersPath, 'Request smoke on drop zone')
    veafRadio.addCommandToSubmenu('Drop zone is marked with GREEN smoke', veafTransportMission.targetMarkersPath, veaf.emptyFunction)
    veafTransportMission.smokeResetTaskID = mist.scheduleFunction(veafTransportMission.smokeReset,{},timer.getTime()+veafTransportMission.SecondsBetweenSmokeRequests)
    veafRadio.refreshRadioMenu()
end

--- Reset the smoke request radio menu
function veafTransportMission.smokeReset()
    veaf.loggers.get(veafTransportMission.Id):debug("smokeReset()")
    veafRadio.delCommand(veafTransportMission.targetMarkersPath, 'Drop zone is marked with GREEN smoke')
    veafRadio.addCommandToSubmenu('Request smoke on drop zone', veafTransportMission.targetMarkersPath, veafTransportMission.smokeTarget)
    trigger.action.outText('Smoke marker over drop zone available',5)
    veafRadio.refreshRadioMenu()
end

--- add an illumination flare over the target area
function veafTransportMission.flareTarget()
    veaf.loggers.get(veafTransportMission.Id):debug("flareTarget()")
    veafSpawn.spawnIlluminationFlare(veaf.getAveragePosition(veafTransportMission.BlueGroupName))
	trigger.action.outText('Copy illumination flare requested, illumination flare over target area!',5)
    veafRadio.delCommand(veafTransportMission.targetMarkersPath, 'Request illumination flare over drop zone')
    veafRadio.addCommandToSubmenu('Drop zone is lit with illumination flare', veafTransportMission.targetMarkersPath, veaf.emptyFunction)
    veafTransportMission.flareResetTaskID = mist.scheduleFunction(veafTransportMission.flareReset,{},timer.getTime()+veafTransportMission.SecondsBetweenFlareRequests)
    veafRadio.refreshRadioMenu()
end

--- Reset the flare request radio menu
function veafTransportMission.flareReset()
    veaf.loggers.get(veafTransportMission.Id):debug("flareReset()")
    veafRadio.delCommand(veafTransportMission.targetMarkersPath, 'Drop zone is lit with illumination flare')
    veafRadio.addCommandToSubmenu('Request illumination flare over drop zone', veafTransportMission.targetMarkersPath, veafTransportMission.flareTarget)
    trigger.action.outText('Illumination flare over drop zone available',5)
    veafRadio.refreshRadioMenu()
end


--- Called from the "Skip delivery" radio menu : remove the current transport mission
function veafTransportMission.skip()
    veafTransportMission.cleanupAfterMission()
    trigger.action.outText("Transport mission cleaned up.", 5)
end

--- Cleanup after either mission is ended or aborted
function veafTransportMission.cleanupAfterMission()
    veaf.loggers.get(veafTransportMission.Id):trace("cleanupAfterMission()")

    -- destroy groups
    veaf.loggers.get(veafTransportMission.Id):trace("destroy friendly group")
    local group = Group.getByName(veafTransportMission.BlueGroupName)
    if group and group:isExist() == true then
        group:destroy()
    end

    veaf.loggers.get(veafTransportMission.Id):trace("destroy cargos")
    local unitNum = 1
    local doIt = true
    while doIt do
        local cargo = StaticObject.getByName(veafTransportMission.BlueCargoName.." #"..unitNum)
        if cargo and cargo:isExist() == true then
            cargo:destroy()
            unitNum = unitNum + 1
        else
            doIt = false
        end
    end

    veaf.loggers.get(veafTransportMission.Id):trace("destroy enemy defense group")
    local groupNum = 1
    local doIt = true
    while doIt do
        group = Group.getByName(veafTransportMission.RedDefenseGroupName.." #"..groupNum)
        if group and group:isExist() == true then
            group:destroy()
            groupNum = groupNum + 1
        else
            doIt = false
        end
    end

    veaf.loggers.get(veafTransportMission.Id):trace("destroy enemy blocade group")
    group = Group.getByName(veafTransportMission.RedBlocadeGroupName)
    if group and group:isExist() == true then
        group:destroy()
    end

    -- remove the watchdog function
    veaf.loggers.get(veafTransportMission.Id):trace("remove the watchdog function")
    if veafTransportMission.friendlyGroupAliveCheckTaskID ~= 'none' then
        mist.removeFunction(veafTransportMission.friendlyGroupAliveCheckTaskID)
    end
    veafTransportMission.friendlyGroupAliveCheckTaskID = 'none'

    -- remove the watchdog function
    veaf.loggers.get(veafTransportMission.Id):trace("remove the adf loop function")
    if veafTransportMission.friendlyGroupAdfLoopTaskID ~= 'none' then
        mist.removeFunction(veafTransportMission.friendlyGroupAdfLoopTaskID)
    end
    veafTransportMission.friendlyGroupAdfLoopTaskID = 'none'

    veafRadio.delCommand(veafTransportMission.rootPath, 'Skip current objective')
    veafRadio.delCommand(veafTransportMission.rootPath, 'Get current objective situation')
    veafRadio.delCommand(veafTransportMission.rootPath, 'Drop zone markers')
    veafRadio.delSubmenu(veafTransportMission.targetMarkersPath, veafTransportMission.rootPath)

    veafRadio.refreshRadioMenu()
    veaf.loggers.get(veafTransportMission.Id):trace("cleanupAfterMission DONE")

end

-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Radio menu and help
-------------------------------------------------------------------------------------------------------------------------------------------------------------

--- Build the initial radio menu
function veafTransportMission.buildRadioMenu()
    veafTransportMission.rootPath = veafRadio.addSubMenu(veafTransportMission.RadioMenuName)
    veafRadio.addCommandToSubmenu("HELP", veafTransportMission.rootPath, veafTransportMission.help, nil, veafRadio.USAGE_ForGroup)
    -- TODO add this command when the respawn will work (see veafTransportMission.resetAllCargoes)
    -- missionCommands.addCommand('Respawn all cargoes', veafTransportMission.rootPath, veafTransportMission.resetAllCargoes)
end

function veafTransportMission.help(unitName)
    local text =
        'Create a marker and type "_transport" in the text\n' ..
        'This will create a default friendly group awaiting cargo that you need to transport\n' ..
        'You can add options (comma separated) :\n' ..
        '   "defense [0-5]" to specify air defense cover on the way (1 = light, 5 = heavy)\n' ..
        '        defense = 1 : 3-7 soldiers, GAZ-3308 transport\n' ..
        '        defense = 2 : 3-7 soldiers, BTR-80 APC\n' ..
        '        defense = 3 : 3-7 soldiers, chance of BMP-1 IFV, chance of Igla manpad\n' ..
        '        defense = 4 : 3-7 soldiers, big chance of BMP-1 IFV, big chance of Igla-S manpad, chance of ZU-23 on a truck\n' ..
        '        defense = 5 : 3-7 soldiers, BMP-1 IFV, big chance of Igla-S manpad, chance of ZSU-23-4 Shilka\n' ..
        '   "size [1-5]" to change the number of cargo items to be transported (1 per participating helo, usually)\n' ..
        '   "blocade [0-5]" to specify enemy blocade around the drop zone (1 = light, 5 = heavy)'

    veaf.outTextForUnit(unitName, text, 30)
end


function veafTransportMission.endTransportOfCargo(cargoName)
    local text = 
    'Congratulations on a job well done ! Cargo ' .. cargoName .. ' has been delivered safely'
    trigger.action.outText(text, 15)
    -- TODO reset cargo position
    -- mist.respawnGroup(cargoName, 15) 
    -- does not work yet because 1. the unit name is changed by mist and 2. the trigger zone condition does not work with the new unit (maybe bc of 1. ?)
end

function veafTransportMission.resetAllCargoes()
    -- does not work yet (see veafTransportMission.endTransportOfCargo)
    local lunits = mist.DBs.unitsByNum
    if lunits then
        for i = 1, #lunits do
            if lunits[i] and lunits[i].unitName and lunits[i].unitName:lower():find('cargo - ') then 
                local name = lunits[i].unitName
                -- destroy cargo static unit
                local c = StaticObject.getByName(name)
                if c then
                    StaticObject.destroy(c)
                end
                mist.respawnGroup(name, true)
            end
        end
    end
    trigger.action.outText("All cargoes have been respawned", 15)
end

function veafTransportMission.initializeAllHelosInCTLD()
    veaf.loggers.get(veafTransportMission.Id):warn("Please use ctld.autoInitializeAllHumanTransports - it's automatically run by the veaf.lua script")
end

function veafTransportMission.initializeAllLogisticInCTLD()
    veaf.loggers.get(veafTransportMission.Id):warn("Please use ctld.autoInitializeAllLogistic - it's automatically run by the veaf.lua script")
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------
-- initialisation
-------------------------------------------------------------------------------------------------------------------------------------------------------------

function veafTransportMission.initialize()
    veafTransportMission.buildRadioMenu()
    veafMarkers.registerEventHandler(veafMarkers.MarkerChange, veafTransportMission.onEventMarkChange)
end

veaf.loggers.get(veafTransportMission.Id):info(string.format("Loading version %s", veafTransportMission.Version))

--- Enable/Disable error boxes displayed on screen.
env.setErrorMessageBoxEnabled(false)

