DPS.watcher = {}
-- holsd all handler functions
DPS.watcher.eventHandler = {}


DPS.watcher.unit_lut = {}
DPS.watcher.player_stats = {}
DPS.watcher.display_alias = {}


function DPS.watcher.show_stats()
    local time = timer.getTime()

    local out_str = "Combat Metrics:"

    for u_name, value in pairs(DPS.watcher.player_stats) do
        local delta = value["since"] - time
        local dps = value["damage"]/delta
        local dpm = value["damage"]*60.0/delta
        local dph = value["damage"]*3600.0/delta

        local disp_name = u_name
        if DPS.watcher.display_alias[u_name] ~= nil then
            disp_name = DPS.watcher.display_alias[u_name]
        end

        out_str = out_str.."\n"..disp_name..": "..string.format('%.2f', dps).." DPS ("
        out_str = out_str..string.format('%.2f', dpm).." DPM, "
        out_str = out_str..string.format('%.2f', dph).." DPH)"
    end
    trigger.action.outText(out_str, 10, false)
end

--- event_hit handler
---@param event event table
function DPS.watcher._event_hit(event)
    -- this event requires target and initiator being valid
    local t_unit = event.target
    local i_unit = event.initiator

    if i_unit == nil or t_unit == nil then
        return
    end

    -- only return on invalid target
    -- an invalid initiator still leads to changes in unit lut
    if t_unit:getCategory() ~= Object.Category.UNIT then
        return
    end

    local u_id = t_unit:getID()
    local live = t_unit:getLife()
    local diff

    -- if this is a first hit on the unit, previous life is assumed 
    -- to be the initial life on the unit
    -- this could potentially ignore damage dealt by non-units 
    -- and accredit it to the first unit triggering the event
    if DPS.watcher.unit_lut[u_id] ~= nil then
        local last_live = DPS.watcher.unit_lut[u_id]
        diff = last_live - live
    else
        local live0 = t_unit:getLife0()
        diff = live0 - live
    end
    -- update the unit registry with the current health
    -- this is done in any case (first hit, repeated hit)
    DPS.watcher.unit_lut[u_id] = live

    if i_unit:getCategory() == Object.Category.UNIT then
        -- get the name of the client, this allows aircraft change
        -- and easire readability
        -- returns nil if unit is AI
        local i_name = i_unit:getPlayerName()

        -- give an alias to ai units to make it look nicer
        -- but use its name as a key for unambiguous keys
        if i_name == nil then
            i_name = i_unit:getName()
            if DPS.watcher.display_alias[i_name] == nil then
                local f_name = DPS.names.first[math.random(#DPS.names.first)]
                local l_name = DPS.names.last[math.random(#DPS.names.last)]
                DPS.watcher.display_alias[i_name] = f_name.." "..l_name
            end
        end

        if DPS.watcher.player_stats[i_name] == nil then
            DPS.watcher.player_stats[i_name] = {
                ["since"] = timer.getTime(),
                ["damage"] = 0,
            }
        end

        local current_dmg = DPS.watcher.player_stats[i_name]["damage"]
        DPS.watcher.player_stats[i_name]["damage"] = current_dmg + (-diff)
    end
end

--- event_dead handler
---@param event event table
function DPS.watcher._event_dead(event)
    -- when the unit is destroyed, the lut can be cleaned up
    local t_unit = event.initiator

    if t_unit ~= nil and t_unit:getCategory() == Object.Category.UNIT then
        local u_id = t_unit:getID()
        DPS.watcher.unit_lut[u_id] = nil
    end
end

--- event_birth handler
---@param event event table
function DPS.watcher._event_birth(event)
    -- it's possible to spawn a new unit with the same name as an old unit
    -- in that case, the game deletes the old unit
    -- therefore the lut needs to be updated
    local t_unit = event.target

    if t_unit ~= nil and t_unit:getCategory() == Object.Category.UNIT then
        local u_id = t_unit:getID()
        DPS.watcher.unit_lut[u_id] = nil
    end
end

---@param event table event table
---@return boolean always true
function DPS.watcher.eventHandler:onEvent(event)
    -- there was an error in the dcs event engine => return with satus code false
    if event == nil then 
        return false
    end

    if (event.id == world.event.S_EVENT_DEAD) then
        DPS.watcher._event_dead(event)
    end

    if (event.id == world.event.S_EVENT_BIRTH) then
        DPS.watcher._event_birth(event)
    end

    if (event.id == world.event.S_EVENT_HIT) then
        DPS.watcher._event_hit(event)
    end 

    return true
end