convoy = {}

-- requires => all modules that are required by this module. contains => all submodules this module contains.
-- This difference is made, to prevent the import of submodules without its parent
convoy.requires = {"util"}
convoy.contains = {"convoy.DATA", "convoy.helpers"}

-- holds all active convoys as well as the number of convoys generated since last reload
convoy.active = {}

-- holds all event handler funcitons for the convoy module
convoy.eventHandler = {}

--- this function is registered as an DCS event listener (see DCS event listeners)
---
--- INFO: This funcion is meant to only be called by the DCS scripting engine.
--- @param event table event attributed supplied by the dcs scripting engine
--- @return boolean
function convoy.eventHandler:onEvent(event)
    -- there was an error in the dcs event engine => return with satus code false
    if event== nil or event.initiator == nil or event.target == nil then
        return false 
    end

    if (event.target:getCategory() ~= Object.Category.UNIT) then
        return false
    end

    -- any unit was killed
    if event.id == world.event.S_EVENT_KILL then
        -- get the callers group
        local group = event.target:getGroup()
        if not group then error("error in event --> could not find group for DCS UNIT " .. tostring(event.target)) end

        -- check if this group was registerd as a convoy and if the entire groop is dead => mission finished
        if convoy.active[group:getName()] ~= nil and not util.group.isGroupAlive(group) then
            convoy.success(group)
        end
    end

    return true
end

--- used to mark a convoy as succeeded (all units destroyed before failure)
--- @param group table group of the caller (see DCS Group)
function convoy.success(group)
    if type(group) ~= "table" then error(util.error.argErr(group, "table", 1)) end

    -- create event for this action
    local event = util.misc.deepCopy(convoy.DATA.EVENT_TEMPLATE)
        event.id = convoy.DATA.EVENT_TYPES.C_SUCCESS
        event.group = group

    -- throw event
    util.mEvent.throwME("CONVOY", event)

    convoy.active[group:getName()] = nil
    trigger.action.outText("CONVOY SUCCESS: mission #" .. util.group.getGroupId(group:getName()) .. " completed", 20, false)
end

--- used to mark a convoy as failed (destination reached)
--- @param group table group of the caller
function convoy.failure(group)
    if type(group) ~= "table" then error(util.error.argErr(group, "table", 1)) end

    -- create event for this action
    local event = util.misc.deepCopy(convoy.DATA.EVENT_TEMPLATE)
        event.id = convoy.DATA.EVENT_TYPES.C_FAILURE
        event.group = group

    -- throw event
    util.mEvent.throwME("CONVOY", event)

    convoy.active[group:getName()] = nil
    trigger.action.outText("ATTACK ZONE FAILURE: mission #" .. util.group.getGroupId(group:getName()) .. " reached its destination", 20, false)
end

--- spawns a convoy. This function includes all generation of data and is meant to be called without parameters
function convoy.spawn()
    -- DEBUG: genrate relation position. Replace this with flexible implementation
    local airbase = Airbase.getByName("Senaki-Kolkhi")
    if not airbase then error("could not find airbase Senaki-Kolkhi") end

    local relPoint = airbase:getPoint()
    local relPos = {x = relPoint.x, y = relPoint.z}

    -- DEBUG: genreate start and target position. Replace this with flexible implementation
    local ok1, start = util.error.wpcall(function() return convoy.helpers.generateStrt(relPos, {min = 50000, max = 100000}) end)
    if not ok1 then util.error.raise(start) return nil end
    local ok2, tar = util.error.wpcall(function() return convoy.helpers.generateTar(start, {min = 30000, max = 50000}) end)
    if not ok2 then util.error.raise(tar) return nil  end

    -- generate unit types and shuffle every but lead for more randomness
    local ok3, typs, poss = util.error.wpcall(function() return convoy.helpers.getTypPos(start) end)
    if not ok3 then util.error.raise(typs) return nil end

    -- determine convoy speed and its traveling route
    local ok4, speed = util.error.wpcall(function() return convoy.helpers.generateSpeed(typs) end)
    if not ok4 then util.error.raise(speed) return nil end
    local ok5, route = util.error.wpcall(function() return convoy.helpers.generateRoute(poss[1], tar, speed) end)
    if not ok5 then util.error.raise(ok5) return nil end

    -- generate unit and group data
    local ok6, units = util.error.wpcall(function() return util.unit.generateUnitTable(typs, poss, nil ,convoy.DATA.UNIT_TEMPLATE) end)
    if not ok6 then util.error.raise(units) return nil end
    local ok7, group = util.error.wpcall(function() return util.group.generateGroupTable(units, {route = route}, convoy.DATA.GROUP_TEMPLATE) end)
    if not ok7 then util.error.raise(group) return nil end

    -- save generated group
    convoy.active[group.name] = true

    -- spawn convoy and trigger notification
    coalition.addGroup(country.id.CJTF_RED, Group.Category.GROUND, group)
    trigger.action.outText("CONVOY ACTIVE: mission #" .. util.group.getGroupId(group.name) .. " Spawned", 20, false)
    trigger.action.outSoundForCoalition(coalition.side.BLUE, convoy.DATA.SOUND)

    -- create event for this action
    local event = util.misc.deepCopy(convoy.DATA.EVENT_TEMPLATE)
        event.id = convoy.DATA.EVENT_TYPES.C_CREATE
        event.group = Group.getByName(group.name)

     -- TODO: fix group to group name because group does not exist here

     -- throw event
    util.mEvent.throwME("CONVOY", event)
end

function convoy.init()
    util.missionCommands.add("/MISSIONS", "CREATE Convoy Mission", convoy.spawn)
    world.addEventHandler(convoy.eventHandler)
end

function convoy.cleanup()
    util.missionCommands.remove("/MISSIONS", "CREATE Convoy Mission")
    world.removeEventHandler(convoy.eventHandler)
end
