local _AddonName, _EchoRaidTools = ...;
local _Cooldowns = _EchoRaidTools:NewModule("cooldowns")



function _Cooldowns:ADDON_LOADED(addonName)
    if addonName ~= _AddonName then return end

    local debug = false --DEBUG
    _Cooldowns.debug = function(...)
        if debug then
            print(...)
        end
    end
    _Cooldowns.VDT = function(...)
        if debug then
            ViragDevTool_AddData(...)
        end
    end

    _Cooldowns.configShown = false
    _Cooldowns.IsConfigOpen = function()
        return _Cooldowns.configShown
    end

    local types = _Cooldowns.types
    EchoRaidToolsDB.Cooldowns = EchoRaidToolsDB.Cooldowns or {}
    local data = EchoRaidToolsDB.Cooldowns
    data.groups = data.groups or {}

    if not EchoCooldowns then
        local LibReadOnly = LibStub:GetLibrary("LibReadOnly")
        local openRaidLib = LibStub:GetLibrary("LibOpenRaid-1.0")
        local LibSharedMedia = LibStub:GetLibrary ("LibSharedMedia-3.0")

        _EchoRaidTools.Cooldowns = _EchoRaidTools.Cooldowns or {}

        local addonLoaded = false

        local SendEchoSpellUpdate = function(...)
            _Cooldowns.UpdateSpell(...)
        end
        local SendEchoUnitRefresh = function(unit, index)
            _Cooldowns.RemoveUnit(unit, index)
        end

        local SendEchoRemoveSpell = function(...)
            _Cooldowns.SendEchoRemoveSpell(...)
        end

        -- Add main lib functions to the module
        _Cooldowns.AllUnitsCooldown = function()
            return openRaidLib.GetAllUnitsCooldown()
        end

        _Cooldowns.UnitCooldowns = function(unit, filter, ...)
            return openRaidLib.GetUnitCooldowns(unit, filter, ...)
        end

        _Cooldowns.CooldownInfo = function(unit, spellID)
            return openRaidLib.GetUnitCooldownInfo(unit, spellID)
        end

        _Cooldowns.GetCooldownStatusFromCooldownInfo = function(cooldownInfo)
            return openRaidLib.GetCooldownStatusFromCooldownInfo(cooldownInfo)
        end
        local calculatePercent = function(timeOffset, duration, updateTime, charges, buffDuration)
            timeOffset = abs(timeOffset)
            local minValue = updateTime - timeOffset
            local maxValue = minValue + duration
            local currentValue = GetTime()
            local timeLeft = max(maxValue - currentValue, 0)

            --lag compensation
            if (timeLeft <= 2) then
                timeLeft = 0
                if (charges == 0) then
                    charges = 1
                end
                minValue = currentValue
                maxValue = 1
                currentValue = 1
            end

            local bIsReady = timeLeft <= 2
            return bIsReady, timeLeft, charges, minValue, maxValue, min(currentValue, maxValue), duration, buffDuration
        end
        local processCooldownInfo = function(cooldownInfo)
            local timeLeft, charges, timeOffset, duration, updateTime, buffDuration = unpack(cooldownInfo)
            if (not updateTime) or type(duration) ~= "number" then
                return
            else
                return calculatePercent(timeOffset, duration, updateTime, charges, buffDuration)
            end
        end

        _Cooldowns.AddCooldownFilter = function(filterName, spells)
            openRaidLib.AddCooldownFilter(filterName, spells)
        end

        -- transfer all the spells into our own table so we can easily get a spell's filter
        _Cooldowns.spellToFilterIndex = {}
        local topIndex = 0
        for spellID, details in pairs(LIB_OPEN_RAID_COOLDOWNS_INFO) do
            _Cooldowns.spellToFilterIndex[spellID] = details.type
            topIndex = max(topIndex, details.type)
        end
        --_Cooldowns.numFilters = topIndex
        _Cooldowns.numFilters = #types.typeToFilterString

        local AddTestDisplays = function(groupIndex)
            if not types.fullSpellList then
                _Cooldowns.GenerateSpellList()
            end
            local unitTable = {}
            for unit in _EchoRaidTools.IterateGroup() do
                table.insert(unitTable, unit)
            end
            local now = GetTime()
            for i = 1, _Cooldowns.GetNumTestDisplays() do
                local spell = types.fullSpellList[fastrandom(1, #types.fullSpellList)]
                local exp = random() > 0.5 and now + fastrandom(30, 200) or now
                local start = now - fastrandom(10, 100)
                local timeLeft = exp - now
                local unit = "test" 
                local ready = exp <= now and true or false
                _Cooldowns.UpdateSpell(groupIndex, unit, spell.spellID, ready, timeLeft, fastrandom(1,3), start, exp, now, exp - start)
            end
        end

        local WA_ScanEvents_Enabled = true
        local WA_Loaded = false
        local SendScanEvents = function(name, ...)
            if WA_Loaded or IsAddOnLoaded("WeakAuras") then
                WA_Loaded = true
                if WA_ScanEvents_Enabled then
                    WeakAuras.ScanEvents(name, ...)
                end
            end
        end
        local EnableScanEvents = function()
            if IsAddOnLoaded("WeakAuras") then
                WA_ScanEvents_Enabled = true
            end
        end
        local DisableScanEvents = function()
            WA_ScanEvents_Enabled = false
        end
        _EchoRaidTools.Cooldowns.EnableScanEvents = EnableScanEvents
        _EchoRaidTools.Cooldowns.DisableScanEvents = DisableScanEvents

        _Cooldowns.OnUnitLostCooldown = function(unit, spellID)
            _Cooldowns.debug("unit lost CD", unit, spellID)
            if addonLoaded then
                SendEchoRemoveSpell(unit, spellID)
                SendScanEvents("ECHO_CD_WIPE_SPELL", unit, spellID)
            end
        end
        openRaidLib.RegisterCallback(_Cooldowns, "CooldownRemoved", "OnUnitLostCooldown")

        _Cooldowns.OnCooldownListWipe = function()
            _Cooldowns.debug("Cooldown list wipe")
            if addonLoaded then
                _Cooldowns.ClearAllEchoCooldowns()
                SendScanEvents("ECHO_CD_WIPE_ALL", true)
            end
        end
        openRaidLib.RegisterCallback(_Cooldowns, "CooldownListWipe", "OnCooldownListWipe")

        _Cooldowns.OnUnitInfoUpdate = function()
            _Cooldowns.debug("OnUnitInfoUpdate")
        end
        openRaidLib.RegisterCallback(_Cooldowns, "UnitInfoUpdate", "OnUnitInfoUpdate")
        _Cooldowns.OnUnitInfoWipe = function()
            _Cooldowns.debug("OnUnitInfoWipe")
        end
        openRaidLib.RegisterCallback(_Cooldowns, "UnitInfoWipe", "OnUnitInfoWipe")

        local checkCooldownInfo = function(cooldownInfo)
            return cooldownInfo and #cooldownInfo >= 5
        end

        --Register callback for a major list update
        _Cooldowns.OnReceiveCooldownListUpdate = function(unit, unitCooldowns, AllUnitsCooldowns)
            _Cooldowns.debug("OnReceiveCooldownListUpdate", unit)
            if addonLoaded then
                unit = _EchoRaidTools.DisambiguateUnit(unit)
                SendScanEvents("ECHO_CD_WIPE_UNIT", unit)
                _Cooldowns.SetDisplaysDirtyByUnit(unit)
                for spellID, cooldownInfo in pairs(unitCooldowns) do
                    local spellFilterIndex = _Cooldowns.spellToFilterIndex[spellID]
                    if spellFilterIndex then
                        SendScanEvents("ECHO_CD_UPDATE", unit, spellID, spellFilterIndex, processCooldownInfo(cooldownInfo))
                        local numGroups = _Cooldowns.GetNumGroups()
                        if (not numGroups) then return end
                        for i = 1, numGroups do
                            _Cooldowns.debug("OnReceiveCooldownListUpdate", unit, spellID, spellFilterIndex)
                            if _Cooldowns.GetFilterByFilterIndex(spellFilterIndex, i) or _Cooldowns.IsSpellInGroupFilters(spellID, i) then
                                SendEchoSpellUpdate(i, unit, spellID, processCooldownInfo(cooldownInfo))
                            end
                        end
                    end
                end
                _Cooldowns.RemoveDirtyDisplaysByUnit(unit)
            end
        end
        openRaidLib.RegisterCallback(_Cooldowns, "CooldownListUpdate", "OnReceiveCooldownListUpdate")

        --Register callback for single spell update
        _Cooldowns.OnReceiveCooldownUpdate = function(unit, spellID, cooldownInfo, unitCooldowns, AllUnitsCooldowns)
            _Cooldowns.debug("OnReceiveCooldownUpdate", unit, spellID, (GetSpellInfo(spellID)))
            if addonLoaded and checkCooldownInfo(cooldownInfo) then
                unit = _EchoRaidTools.DisambiguateUnit(unit)
                local spellFilterIndex = _Cooldowns.spellToFilterIndex[spellID]
                if spellFilterIndex then
                    SendScanEvents("ECHO_CD_UPDATE", unit, spellID, spellFilterIndex, processCooldownInfo(cooldownInfo))
                    local numGroups = _Cooldowns.GetNumGroups()
                    if (not numGroups) then return end
                    for i = 1, numGroups do
                        if _Cooldowns.GetFilterByFilterIndex(spellFilterIndex, i) or _Cooldowns.IsSpellInGroupFilters(spellID, i) then
                            SendEchoSpellUpdate(i, unit, spellID, processCooldownInfo(cooldownInfo))
                        end
                    end
                end
            end
        end
        openRaidLib.RegisterCallback(_Cooldowns, "CooldownUpdate", "OnReceiveCooldownUpdate")

        --Register callback for single spell update
        _Cooldowns.OnReceiveCooldownAdded = function(unit, spellID, cooldownInfo, unitCooldowns, AllUnitsCooldowns)
            _Cooldowns.debug("OnReceiveCooldownAdded", unit)
            if addonLoaded and checkCooldownInfo(cooldownInfo) then
                unit = _EchoRaidTools.DisambiguateUnit(unit)
                local spellFilterIndex = _Cooldowns.spellToFilterIndex[spellID]
                if spellFilterIndex then
                    SendScanEvents("ECHO_CD_UPDATE", unit, spellID, spellFilterIndex, processCooldownInfo(cooldownInfo))
                    local numGroups = _Cooldowns.GetNumGroups()
                    if (not numGroups) then return end
                    for i = 1, numGroups do
                        if _Cooldowns.GetFilterByFilterIndex(spellFilterIndex, i) or _Cooldowns.IsSpellInGroupFilters(spellID, i) then
                            SendEchoSpellUpdate(i, unit, spellID, processCooldownInfo(cooldownInfo))
                        end
                    end
                end
            end
        end
        openRaidLib.RegisterCallback(_Cooldowns, "CooldownAdded", "OnReceiveCooldownAdded")

        _Cooldowns.RequestNewCooldownInfo = function(index)
            _Cooldowns.debug("RequestNewCooldownInfo","internal", index)
            if addonLoaded then
                local allCDs = _Cooldowns.AllUnitsCooldown()
                _Cooldowns.SetDisplaysDirtyByGroupIndex(index)
                for unit, info in pairs(allCDs) do
                    unit = _EchoRaidTools.DisambiguateUnit(unit)
                    for spellID, cooldownInfo in pairs(info) do
                        if checkCooldownInfo(cooldownInfo) then
                            local spellFilterIndex = _Cooldowns.spellToFilterIndex[spellID]
                            if spellFilterIndex then
                                if index then
                                    if _Cooldowns.GetFilterByFilterIndex(spellFilterIndex, index) or _Cooldowns.IsSpellInGroupFilters(spellID, index) then
                                        SendEchoSpellUpdate(index, unit, spellID, processCooldownInfo(cooldownInfo))
                                    end
                                else
                                    local numGroups = _Cooldowns.GetNumGroups()
                                    if (not numGroups) then return end
                                    for i = 1, numGroups do
                                        if _Cooldowns.GetFilterByFilterIndex(spellFilterIndex, i) or _Cooldowns.IsSpellInGroupFilters(spellID, i) then
                                            SendEchoSpellUpdate(i, unit, spellID, processCooldownInfo(cooldownInfo))
                                        end
                                    end
                                end
                            end
                        end
                    end
                end
                _Cooldowns.RemoveDirtyDisplaysByGroupIndex(index)
                if _Cooldowns.GetTestDisplays() then
                    AddTestDisplays(index)
                end
            end
        end

        _Cooldowns.RequestAllCooldownInfo = function()
            if addonLoaded then
                local allCDs = _Cooldowns.AllUnitsCooldown()
                return allCDs
            end
        end        
        _EchoRaidTools.Cooldowns.RequestAllCooldownInfo = _Cooldowns.RequestAllCooldownInfo

        _Cooldowns.RequestCooldownInfoBySpellID = function(querySpellID)
            if addonLoaded then
                local ret = {}
                if querySpellID then
                    local allCDs = _Cooldowns.AllUnitsCooldown()
                    if LIB_OPEN_RAID_COOLDOWNS_INFO[querySpellID] then
                        for unit, info in pairs(allCDs) do
                            for spellID, cooldownInfo in pairs(info) do
                                if checkCooldownInfo(cooldownInfo) and LIB_OPEN_RAID_COOLDOWNS_INFO[spellID] then
                                    if spellID == querySpellID then
                                        table.insert(ret, {unit, spellID, processCooldownInfo(cooldownInfo)})
                                        --SendScanEvents("ECHO_CD_UPDATE", unit, spellID, processCooldownInfo(cooldownInfo))
                                    end
                                end
                            end
                        end
                    end
                end
                return #ret > 0 and ret
            end
        end
        _EchoRaidTools.Cooldowns.RequestCooldownInfoBySpellID = _Cooldowns.RequestCooldownInfoBySpellID

        _Cooldowns.RequestCooldownInfoByFilter = function(filterIndex)
            if addonLoaded then
                local ret = {}
                if type(filterIndex) == "number" and filterIndex <= _Cooldowns.numFilters then
                    local allCDs = _Cooldowns.AllUnitsCooldown()
                    for unit, info in pairs(allCDs) do
                        for spellID, cooldownInfo in pairs(info) do
                            if checkCooldownInfo(cooldownInfo) then
                                if LIB_OPEN_RAID_COOLDOWNS_INFO[spellID] and LIB_OPEN_RAID_COOLDOWNS_INFO[spellID].type == filterIndex then
                                    table.insert(ret, {unit, spellID, processCooldownInfo(cooldownInfo)})
                                    --SendScanEvents("ECHO_CD_UPDATE", unit, spellID, spellOrFilter, processCooldownInfo(cooldownInfo))
                                end
                            end
                        end
                    end
                end
                return #ret > 0 and ret or {}
            end
        end
        _EchoRaidTools.Cooldowns.RequestCooldownInfoByFilter = _Cooldowns.RequestCooldownInfoByFilter

        _Cooldowns.RequestCooldownInfoByUnit = function(unit, spellID)
            if addonLoaded then
                if spellID then
                    local CD = _Cooldowns.CooldownInfo(unit, spellID)
                    return CD and {unit, spellID, processCooldownInfo(CD)}
                else
                    local ret = {}
                    local unitCDs = _Cooldowns.UnitCooldowns(unit)
                    for spellID, cooldownInfo in pairs(unitCDs) do
                        table.insert(ret, {unit, spellID, processCooldownInfo(cooldownInfo)})
                    end
                    return #ret > 0 and ret
                end
            end
        end
        _EchoRaidTools.Cooldowns.RequestCooldownInfoByUnit = _Cooldowns.RequestCooldownInfoByUnit

        _Cooldowns.CreateTestDisplays = function(groupIndex)
            _Cooldowns.RequestNewCooldownInfo(groupIndex)
            --AddTestDisplays(groupIndex)
        end

        _Cooldowns:LoadEvents()
        _Cooldowns:LoadFrames()
        _Cooldowns:LoadSettings()
        _Cooldowns:LoadOptions()
        addonLoaded = true
        local delayAndRecheck
        delayAndRecheck = function()
            local CDs = _Cooldowns.AllUnitsCooldown()
            if not next(CDs) then
                C_Timer.After(2, delayAndRecheck)
            else
                if not next(_Cooldowns.displays) then
                    _Cooldowns.RequestNewCooldownInfo()
                end
                _Cooldowns.CheckLoads()
            end
        end
        delayAndRecheck()
        C_Timer.After(10, function()
            _Cooldowns.QueueCheckSubGroups()
        end)

        LibSharedMedia.RegisterCallback(_Cooldowns, "LibSharedMedia_Registered", "RefreshMedia" )
        LibSharedMedia.RegisterCallback(_Cooldowns, "LibSharedMedia_SetGlobal", "RefreshMedia" )

        -- TODO: 
        -- redo backdrops
        -- test displays when anchored to unit frames

        -- Add healthstone/healthpot (combat pot?)

        -- copy/paste section settings

        
        --ARROW KEYS NAVIGATE FILTER SELECTION
        -- ICON COLOUR

        -- fix
        -- prioritise state PROPERTIES 

        -- future additions:
        -- anchor to specified frame
        -- filters spells by spec - e.g. only show wings for Holy Paladins
        -- profiles?
        -- show charges as separate displays
        -- Blacklist in spell filter section
        -- Icon "zoom"?

        -- ADD:
        -- "Use AblityX Now" message (not part of cooldowns but add it as a new module)

        --_G["EchoCooldowns"] = LibReadOnly:New(_EchoRaidTools.Cooldowns)
        _G["EchoCooldowns"] = _EchoRaidTools.Cooldowns
    end
end