divine_intelligence.waypoint = {}

divine_intelligence.waypoint.active = {}

--- this function gets called whenever a mission event occurs
--- @param event table event attributed supplied by the dcs scripting engine
function divine_intelligence.waypoint.MEventHandler(event)
    if type(event) ~= "table" then error(util.error.argErr(event, "table", 1)) end

    -- this will cut down on if else cases if many multiple events are possible
    local identifier = tostring(event.name) .. tostring(event.id)

    -- event attack zone create
    if identifier == "AZ1" then
        divine_intelligence.waypoint.addAZ(event)

    -- event attack zone success
    elseif identifier == "AZ2" then
        divine_intelligence.waypoint.remove(event.group:getName())

    -- event convoy create
    elseif identifier == "CONVOY1" then
        divine_intelligence.waypoint.addCONVOY(event)

    -- event convoy success or failure
    elseif identifier == "CONVOY2" or identifier == "CONVOY3" then
        divine_intelligence.waypoint.remove(event.group:getName())
    end
end

--- adds an attack zone to the supervised (active) missions
--- @param event table the event thrown by the attack zone module (see AZ event)
function divine_intelligence.waypoint.addAZ(event)
    if type(event) ~= "table" then error(util.error.argErr(event, "table", 1)) end

    -- retrieve referece point + alt (since the trigger zone does not provide this info)
    local point = (trigger.misc.getZone(event.zName or error(util.error.argErr(event.zName, "table [\"zName\"] = string", 1)))).point
    point.y = land.getHeight({x = point.x, y = point.z})

    -- useage of an attribute so its expandable in the future
    local gName = (event.group or error(util.error.argErr(event.group, "table [\"group\"] = table", 1))):getName()
    divine_intelligence.waypoint.active[gName] = {
        desc = "ATTACK ZONE (mission #" .. util.group.getGroupId(event.group:getName()) .. " " .. event.zType .. ")",
        point = point,
        coalition = coalition.side.BLUE,
    }
end

--- adds a convoy to the supervised (active) missions
--- @param event table the event thrown by the convoy module (see CONVOY event)
function divine_intelligence.waypoint.addCONVOY(event)
    if type(event) ~= "table" then error(util.error.argErr(event, "table", 1)) end

    -- retrieve referece point (alt is provided by the unit point)
    local point = (event.group or error(util.error.argErr(event.group, "table [\"group\"] = table", 1))):getUnit(1):getPoint()

    -- useage of an attribute so its expandable in the future
    divine_intelligence.waypoint.active[event.group:getName()] = {
        desc = "CONVOY (mission #" .. util.group.getGroupId(event.group:getName()) .. ")",
        point = point,
        coalition = coalition.side.BLUE,
    }

    -- shedule a refresh for the convoy
    timer.scheduleFunction(
        divine_intelligence.waypoint.refreshCONVOY,
        event.group:getName(),
        timer.getTime() + divine_intelligence.DATA.MOV_SCAN_FREQ)
end

--- refreshes a convoys position and reshedules itself if the convoy is still alive
--- @param identifier string identifier of the convoy inside the active module
--- @return nil @(reshedules itself tho)
function divine_intelligence.waypoint.refreshCONVOY(identifier)
    if type(identifier) ~= "string" then error(util.error.argErr(identifier, "string", 2)) end

    -- retrive convoy arguments
    local args = divine_intelligence.waypoint.active[identifier]

    -- if args are nil => convoy is dead (removed) so dont refresh
    if args ~= nil then
        args.point = Group.getByName(identifier):getUnit(1):getPoint()

        -- reshedule a refresh
        timer.scheduleFunction(
            divine_intelligence.waypoint.refreshCONVOY,
            identifier,
            timer.getTime() + divine_intelligence.DATA.MOV_SCAN_FREQ)
    end

    return nil
end

--- removes (sets nil) a waypoint from the active list
--- @param identifier string identifier of the convoy inside the active module
function divine_intelligence.waypoint.remove(identifier)
    if type(identifier) ~= "string" then error(util.error.argErr(identifier, "string", 1)) end

    divine_intelligence.waypoint.active[identifier] = nil
    return nil
end

--- displays all active waypoints inside DCS
function divine_intelligence.waypoint.display()
    -- shortcut so its easier to refrence active table with shorter name
    local active = divine_intelligence.waypoint.active or {}

    -- if there is no active mission (no active keys) => display message and return
    if #util.misc.getKeys(active) <= 0 then
        trigger.action.outText("DIVINE INTELIGENCE could not detect any enemy activity (no active missions)", 60, false)
        do return end
    end

    -- get all active missions and sort them by id
    local activeKeys = util.misc.getKeys(active)
    activeKeys = util.sort.selctSort(activeKeys)

    -- for every waypoint display its arguments
    for _i, key in ipairs(activeKeys) do
        local args = active[key]

        -- position in lat long DMDM and mrgs
        local lat, long = coord.LOtoLL(args.point)
        local latStr, longStr = util.coords.DMDMString(util.coords.DDtoDMDM(lat, long))
        local mgrsStr = util.coords.MGRSString(coord.LLtoMGRS(lat, long))

        -- altitude in meter and feet
        local altm = string.format("%.0f", args.point.y)
        local altft = string.format("%.0f", util.conversion.fromM(args.point.y, "i_ft"))

        -- vector from Bullseye in degrees, meter and feet
        local bulls =  coalition.getMainRefPoint(args.coalition)
        if not bulls then error("Could not retrive Main Ref Point of coalition " .. tostring(args.coalition)) end

        local bBear, bDist = util.math.carToPol(args.point.x - bulls.x, args.point.z - bulls.z, false)
        bBear = util.math.radToDeg(bBear)

        -- render message
        local message = "ACTIVE: " .. args.desc .. "\n"
        .. "        Lat: " .. latStr .. "\n"
        .. "        Lng: " .. longStr .. "\n"
        .. "        MGRS: " .. mgrsStr .. "\n"
        .. "        Altitude: " .. altm .. " m  (" .. altft .. " ft)" .. "\n"
        .. "        Vector from Bullseye: " .. string.format("%.2f", bBear) .. "° for " 
                                            .. string.format("%.2f", util.conversion.fromM(bDist, "m_km")) .. " km  "
                                            .. "(" .. string.format("%.2f", util.conversion.fromM(bDist, "i_nm")) .. " nm)"

        -- trigger message inside DCS for 1 minute
        trigger.action.outText(message, 120, false)
    end
end