convoy.helpers = {}

--- calculates the speed the convoy should travel at. (minimum max speed of all units)
--- @param types table unit type table
--- @return number @min speed of all units max speed 
function convoy.helpers.generateSpeed(types)
    if type(types) ~= "table" then error(util.error.argErr(types, "table", 1)) end

    -- for all types get max speeds
    local mapFunc = function(v, k)
        local unit = Unit.getDescByName(v)

        return unit.speedMax
    end

    local speeds = util.misc.map(types, mapFunc)

    -- return the minimum value
    return math.min(unpack(speeds))
end

--- generates a init position for the convoy
--- @param rel table relation (origin) from wich the coordintaes are randomized (see DCS vec2)
--- @param dist table distance information including dist.min and dist.max from the relation
--- @return table @init pos
function convoy.helpers.generateStrt(rel, dist)
    if type(rel) ~= "table" then error(util.error.argErr(rel, "table", 1)) end
    if type(dist) ~= "table" then error(util.error.argErr(dist, "table", 2)) end
    if not tonumber(dist.min) then error(util.error.argErr(dist.min, "table [\"min\"] = number", 2)) end
    if not tonumber(dist.max) then error(util.error.argErr(dist.max, "table [\"max\"] = number", 2)) end

    local valid = {
        [land.SurfaceType.LAND] = true,
        [land.SurfaceType.ROAD] = true,
        [land.SurfaceType.RUNWAY] = true,
    }

    -- choose a random position on LAND (no water) since closest pos on roads could otherwise return positions too close to our rel point
    -- NOTE: we dont use util.land.getCloseValidPos because it does not guarantee a min dist to our rel point
    local x, y
    repeat
        x, y = util.math.polToCar(math.random() * 2*math.pi, math.random(dist.min, dist.max))
        local surface = land.getSurfaceType({x = rel.x+x, y = rel.y+y})
    until(valid[surface])

    local strtX, strtY = land.getClosestPointOnRoads("roads", rel.x + x, rel.y + y) -- already valid no need to check
    return {x = strtX, y = strtY}
end

--- generates a target position for the convoy
--- @param rel table relation (origin) from wich the coordintaes are randomized (see DCS vec2)
--- @param dist table distance information including dist.min and dist.max from the relation
--- @return table @target pos (see DCS vec2)
function convoy.helpers.generateTar(rel, dist)
    if type(rel) ~= "table" then error(util.error.argErr(rel, "table", 1)) end
    if type(dist) ~= "table" then error(util.error.argErr(dist, "table", 2)) end
    if not tonumber(dist.min) then error(util.error.argErr(dist.min, "table [\"min\"] = number", 2)) end
    if not tonumber(dist.max) then error(util.error.argErr(dist.max, "table [\"max\"] = number", 2)) end

    local x, y = util.math.polToCar(math.random() * (2*math.pi), math.random(dist.min, dist.max))
    local tarX, tarY = land.getClosestPointOnRoads("roads", rel.x + x, rel.y + y) -- already valid no need to check

    return {x = tarX, y = tarY}
end

--- generates a types and a positions table (tuple) used for unit generation
---
--- WARNING: this function does NOT check for spawnpoint validity so it is possible seperate functionality in the future
--- @param initPos table init position of the convoy
--- @return table[], table[] @types, positions
function convoy.helpers.getTypPos(initPos)
    if type(initPos) ~= "table" then error(util.error.argErr(initPos, "table", 1)) end

    -- generate amount of generated units out of max and min
    local amount = math.random(convoy.DATA.COUNT.MIN, convoy.DATA.COUNT.MAX)
    local typs, poss = {}, {}

    -- add the lead in the first position of the unit table and set his position to initpos
    typs[1], poss[1] = convoy.DATA.DEFINITION.LEAD, initPos

    -- add all special units wich have to be present in a certain quanitity min and max
    for typ, amt in pairs(convoy.DATA.DEFINITION.SPECIAL) do
        -- generate a random quanitity amount
        local c =  math.random(amt.min, amt.max)

        -- add this quanitity to the units
        for i=1, c do
            typs[#typs + 1], poss[#poss + 1] = typ, initPos
        end
    end

    -- get the names of all possible main types (used for random index)
    local mainTypes = util.misc.getKeys(convoy.DATA.DEFINITION.MAIN)

    -- generate main part of the convoy unil it is full
    repeat
        -- random type and probability to spawn
        local typ = mainTypes[math.random(1, #mainTypes)]
        local p = math.random()

        -- check if the probability meets condition or not and add unit if it meets
        if p <= convoy.DATA.DEFINITION.MAIN[typ].prob then
            typs[#typs + 1], poss[#poss + 1] = typ, initPos
        end
    until #typs >= amount

    -- shuffle for more randomness
    util.misc.shuffle(typs, 2, #typs, true)

    return typs, poss
end

--- generates the route for a specified convoy
--- @param strtPos table init position of the convoy (see DCS vec2)
--- @param tarPos table target position of the convoy (see DCS vec2)
--- @param speed number speed the convoy is going to traval at
--- @return table[] @(see DCS waypoints for Group)
function convoy.helpers.generateRoute(strtPos, tarPos, speed)
    if type(strtPos) ~= "table" then error(util.error.argErr(strtPos, "table", 1)) end
    if type(tarPos) ~= "table" then error(util.error.argErr(tarPos, "table", 2)) end
    if not tonumber(speed) then error(util.error.argErr(speed, "number", 3)) end

    -- set trigger to call convoy.failure() if second waypoint is reached
    local onArrival = {
        id = "WrappedAction",
        params = {
            action = { id = "Script", params = { command = "convoy.failure(...)" }}
        }
    }

    -- 1st waypoint is nessacary otherwise the units wont start moving. Seems to be a DCS implementation specific thing
    -- This might coalate to waypoint 0 in ME (I hate lua for using table index 1 instead of 0 -.-)
    local route = { 
        -- waypoints have to be packed inside the points attribute
        points  = {
            [1] = { x = strtPos.x, y = strtPos.y, action = "On Road", speed = speed },
            [2] = { x = tarPos.x, y = tarPos.y, action = "On Road", speed = speed, task = onArrival }
        }
    }

    return route
end