local LAM = LibStub:GetLibrary("LibAddonMenu-2.0")


RaidNotifier = RaidNotifier or {}

RaidNotifier.name		   = 'RaidNotifier'
RaidNotifier.slash		   = '/rn'
RaidNotifier.version	   = '1.3.6'
RaidNotifier.versionDB	   = 1
RaidNotifier.loaded	       = false
RaidNotifier.author        = 'silentgecko'
RaidNotifier.savedVarsName = 'RNVars'

RaidNotifier.variables = {
    sanctum_ophidia = {
        magicka_deto      = true,
        poison            = true,
        mantikora_spear   = true,
    },
    maelstrom = {
        stage7_poison  = true,
        stage9_synergy = true,
    },
    general = {
        buffFood_reminder = true,
        buffFood_reminder_interval = 60,
    },
    debug = false
}

RaidNotifier.pingEventCodes = {
    mantikora_spear = 51816
}

RaidNotifier.tempVars = {
    last_notify = {
        sanctum_ophidia = {
            magicka_deto    = 0,
            poison          = 0,
            mantikora_spear = 0,
        },
        general = {
            buffFood_reminder = 0,
        },
        maelstrom = {
            stage7_poison  = 0,
            stage9_synergy = 0,
        }
    },
    last_ping = 0,
}

local function CreateSettingsMenu()
    local self = RaidNotifier
    ------Creating the menu with LibAddonMenu-2.0 (thank you Seerah)-------
    self.panelData = {
        type = "panel",
        name = self.name,
        author = self.author,
        version = self.version,
        registerForRefresh = false,
    }

    self.optionsData = {
        {
            type = "description",
            text = GetString(RAIDNOTIFIER_DESCRIPTION),
        },
        {
            type = "header",
            name = GetString(RAIDNOTIFIER_SETTINGS_GENERAL_HEADER),
        },
        {
            type = "checkbox",
            name = GetString(RAIDNOTIFIER_SETTINGS_GENERAL_BUFFFOOD_REMINDER),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_GENERAL_BUFFFOOD_REMINDER_TT),
            getFunc = function() return RaidNotifier.savedVariables.general.buffFood_reminder end,
            setFunc = function(value)
                RaidNotifier.savedVariables.general.buffFood_reminder = value
            end,
        },
        {
            type = "slider",
            name = GetString(RAIDNOTIFIER_SETTINGS_GENERAL_BUFFFOOD_REMINDER_INTERVAL),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_GENERAL_BUFFFOOD_REMINDER_INTERVAL_TT),
            min = 30,
            max = 120,
            step = 5,
            getFunc = function() return RaidNotifier.savedVariables.general.buffFood_reminder_interval end,
            setFunc = function(value)
                RaidNotifier.savedVariables.general.buffFood_reminder_interval = value
            end,
            default = 60,
            width = "full",
        },
        {
            type = "header",
            name = GetString(RAIDNOTIFIER_SETTINGS_SANCTUM_HEADER),
        },
        {
            type = "checkbox",
            name = GetString(RAIDNOTIFIER_SETTINGS_SANCTUM_MAGICKA_DETONATION),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_SANCTUM_MAGICKA_DETONATION_TT),
            getFunc = function() return RaidNotifier.savedVariables.sanctum_ophidia.magicka_deto end,
            setFunc = function(value)
                RaidNotifier.savedVariables.sanctum_ophidia.magicka_deto = value
            end,
        },
        {
            type = "checkbox",
            name = GetString(RAIDNOTIFIER_SETTINGS_SANCTUM_POISON),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_SANCTUM_POISON_TT),
            getFunc = function() return RaidNotifier.savedVariables.sanctum_ophidia.poison end,
            setFunc = function(value)
                RaidNotifier.savedVariables.sanctum_ophidia.poison = value
            end,
        },
        {
            type = "checkbox",
            name = GetString(RAIDNOTIFIER_SETTINGS_SANCTUM_MANTIKORA_SPEAR),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_SANCTUM_MANTIKORA_SPEAR_TT),
            getFunc = function() return RaidNotifier.savedVariables.sanctum_ophidia.mantikora_spear end,
            setFunc = function(value)
                RaidNotifier.savedVariables.sanctum_ophidia.mantikora_spear = value
            end,
        },
        {
            type = "header",
            name = GetString(RAIDNOTIFIER_SETTINGS_MAELSTROM_HEADER),
        },
        {
            type = "checkbox",
            name = GetString(RAIDNOTIFIER_SETTINGS_MAELSTROM_STAGE7_POISON),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_MAELSTROM_STAGE7_POISON_TT),
            getFunc = function() return RaidNotifier.savedVariables.maelstrom.stage7_poison end,
            setFunc = function(value)
                RaidNotifier.savedVariables.maelstrom.stage7_poison = value
            end,
        },
        {
            type = "checkbox",
            name = GetString(RAIDNOTIFIER_SETTINGS_MAELSTROM_STAGE9_SYNERGY),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_MAELSTROM_STAGE9_SYNERGY_TT),
            getFunc = function() return RaidNotifier.savedVariables.maelstrom.stage9_synergy end,
            setFunc = function(value)
                RaidNotifier.savedVariables.maelstrom.stage9_synergy = value
            end,
        },

        {
            type = "header",
            name = GetString(RAIDNOTIFIER_SETTINGS_DEBUG_HEADER),
        },
        {
            type = "checkbox",
            name = GetString(RAIDNOTIFIER_SETTINGS_DEBUG),
            tooltip = GetString(RAIDNOTIFIER_SETTINGS_DEBUG_TT),
            getFunc = function() return RaidNotifier.savedVariables.debug end,
            setFunc = function(value)
                RaidNotifier.savedVariables.debug = value
            end,
        },
    }

    LAM:RegisterAddonPanel("RaidNotifierPanel", self.panelData)
    LAM:RegisterOptionControls("RaidNotifierPanel", self.optionsData)
end


---------Passing saved variables to the labels at initialize-------
function RaidNotifier.Initialize(_, addonName)
    local self = RaidNotifier

    if addonName ~= self.name then return end

    EVENT_MANAGER:UnregisterForEvent(self.name, EVENT_ADD_ON_LOADED)

    self.savedVariables = ZO_SavedVars:NewAccountWide(self.savedVarsName, self.versionDB, nil, self.variables)

    CreateSettingsMenu()

    EVENT_MANAGER:RegisterForEvent(self.name, EVENT_RAID_TRIAL_STARTED,  RaidNotifier.addEventListeners)
    EVENT_MANAGER:RegisterForEvent(self.name, EVENT_RAID_TRIAL_COMPLETE, RaidNotifier.removeEventListeners)
    EVENT_MANAGER:RegisterForEvent(self.name, EVENT_RAID_TRIAL_FAILED,   RaidNotifier.removeEventListeners)

    if IsRaidInProgress() then
        self.addEventListeners()
    else
        self.removeEventListeners()
    end
end

-- Add Events
function RaidNotifier.addEventListeners()
    local self   = RaidNotifier
    local raidId = GetCurrentParticipatingRaidId()

    -- add general
    if raidId > 0 then
        EVENT_MANAGER:RegisterForEvent(self.name .. "_BUFFFOOD", EVENT_EFFECT_CHANGED, self.generalBuffFoodReminder)
        EVENT_MANAGER:RegisterForEvent(self.name .. "_PINGS",    EVENT_MAP_PING,       self.receivePing)
    end

    -- add sanctum
    if raidId == 3 then
        EVENT_MANAGER:RegisterForEvent(self.name .. "_SANCTUM", EVENT_EFFECT_CHANGED, self.sanctumDebuffs)
    end

    -- add maelstrom
    if raidId == 6 then
        EVENT_MANAGER:RegisterForEvent(self.name .. "_MAELSTROM", EVENT_EFFECT_CHANGED, self.maelstromDebuffs)
    end
end

-- Remove all Events
function RaidNotifier.removeEventListeners()
    local self   = RaidNotifier

    EVENT_MANAGER:UnregisterForEvent(self.name .. "_BUFFFOOD",  EVENT_EFFECT_CHANGED)
    EVENT_MANAGER:UnregisterForEvent(self.name .. "_SANCTUM",   EVENT_EFFECT_CHANGED)
    EVENT_MANAGER:UnregisterForEvent(self.name .. "_MAELSTROM", EVENT_EFFECT_CHANGED)
    EVENT_MANAGER:UnregisterForEvent(self.name .. "_PINGS",     EVENT_MAP_PING)
end


-- Sanctum Ophidia Events
function RaidNotifier.sanctumDebuffs(_, change, _, name, unit, _, _, _, _, _, _, _, _, _, _, abilityId)
    -- only take care of player
    if (unit ~= 'player') then return end

    local self = RaidNotifier
    -- remove event listener, the api won't get it on zoning
    if IsRaidInProgress() == false then
        self.removeEventListeners()
        return
    end

    local buffsDebuffs = self.BuffsDebuffs.sanctum_ophidia

    -- only notice on effect added
    if (change == EFFECT_RESULT_GAINED) then

        -- Sanctum Serpent Poison Alert
        if self.savedVariables.sanctum_ophidia.poison then
            -- only notify when the last one is at least 15 seconds ago and it is poison
            local currentTime = GetTimeStamp()
            local lastNotify  = self.tempVars.last_notify.sanctum_ophidia.poison
            local timeDiff = GetDiffBetweenTimeStamps(currentTime, lastNotify)
            if (buffsDebuffs.poison == abilityId) and timeDiff > 15 then
                self.tempVars.last_notify.sanctum_ophidia.poison = currentTime
                CENTER_SCREEN_ANNOUNCE:AddMessage(2, CSA_EVENT_COMBINED_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, GetString(RAIDNOTIFIER_ALERTS_SANCTUM_POISON), nil, nil, nil, nil, nil, 2000)
            end
        end

        -- Sanctum Serpent Magicka Detonation Alert
        if self.savedVariables.sanctum_ophidia.magicka_deto then
            -- only notify when the last one is at least 10 seconds ago and it is poison
            -- debug abilityId: 30255 / Boundless Storm
            -- Real abilityId: 59036 / "Serp Target"
            local currentTime = GetTimeStamp()
            local lastNotify  = self.tempVars.last_notify.sanctum_ophidia.magicka_deto
            local timeDiff = GetDiffBetweenTimeStamps(currentTime, lastNotify)
            if (abilityId == buffsDebuffs.magicka_deto) and timeDiff > 10 then
                self.tempVars.last_notify.sanctum_ophidia.magicka_deto = currentTime

                -- get current magicka percentage
                local current, maximum, _ = GetUnitPower("player", POWERTYPE_MAGICKA)
                local magickaPercentage   = zo_roundToNearest(current/maximum,0.01) * 100

                -- only notify if the current magicka is over 15%
                if magickaPercentage > 15 then
                    CENTER_SCREEN_ANNOUNCE:AddMessage(1, CSA_EVENT_COMBINED_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, GetString(RAIDNOTIFIER_ALERTS_SANCTUM_MAGICKA_DETONATION), nil, nil, nil, nil, nil, 5000)
                end
            end
        end

        -- Sanctum Mantikora Spear Throw Alert
        -- @todo send map ping and listen to it
        if self.savedVariables.sanctum_ophidia.mantikora_spear then
            -- only notify when the last one is at least 5 seconds ago and it is the spear
            local currentTime = GetTimeStamp()
            local lastNotify  = self.tempVars.last_notify.sanctum_ophidia.mantikora_spear
            local timeDiff = GetDiffBetweenTimeStamps(currentTime, lastNotify)
            if (buffsDebuffs.spear == abilityId) and timeDiff > 5 then
                self.tempVars.last_notify.sanctum_ophidia.mantikora_spear = currentTime
                CENTER_SCREEN_ANNOUNCE:AddMessage(2, CSA_EVENT_SMALL_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, GetString(RAIDNOTIFIER_ALERTS_SANCTUM_MANTIKORA_SPEAR))
                self.sendPing('manti_spear')
            end
        end
    end
end


-- Maelstrom Arena Events
function RaidNotifier.maelstromDebuffs(_, change, _, name, unit, _, _, _, _, _, _, _, _, _, _, abilityId)
    -- only take care of player
    if (unit ~= 'player') then return end

    local self = RaidNotifier
    -- remove event listener, the api won't get it on zoning
    if IsRaidInProgress() == false then
        self.removeEventListeners()
        return
    end
    local buffsDebuffs = self.BuffsDebuffs.maelstrom

    -- only notice on effect added
    if (change == EFFECT_RESULT_GAINED) then

        -- Maelstrom Poison Alert
        if self.savedVariables.maelstrom.stage7_poison then
            -- only notify when the last one is at least 15 seconds ago and it is poison
            local currentTime = GetTimeStamp()
            local lastNotify  = self.tempVars.last_notify.maelstrom.stage7_poison
            local timeDiff = GetDiffBetweenTimeStamps(currentTime, lastNotify)
            if (buffsDebuffs.stage7_poison[abilityId]) and timeDiff > 15 then
                self.tempVars.last_notify.maelstrom.stage7_poison = currentTime
                CENTER_SCREEN_ANNOUNCE:AddMessage(2, CSA_EVENT_COMBINED_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, GetString(RAIDNOTIFIER_ALERTS_MAELSTROM_STAGE7_POISON), nil, nil, nil, nil, nil, 2000)
            end
        end

        -- Maelstrom Stage 9 Synergy Alert
        if self.savedVariables.sanctum_ophidia.mantikora_spear then
            -- only notify when the last one is at least 15 seconds ago and it is the synergy
            local currentTime = GetTimeStamp()
            local lastNotify  = self.tempVars.last_notify.maelstrom.stage9_synergy
            local timeDiff = GetDiffBetweenTimeStamps(currentTime, lastNotify)
            if (buffsDebuffs.stage9_synergy == abilityId) and timeDiff > 15 then
                self.tempVars.last_notify.maelstrom.stage9_synergy = currentTime
                CENTER_SCREEN_ANNOUNCE:AddMessage(2, CSA_EVENT_SMALL_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, GetString(RAIDNOTIFIER_ALERTS_MAELSTROM_STAGE9_SYNERGY), nil, nil, nil, nil, nil, 2000)
            end
        end
    end
end

-- General Events
function RaidNotifier.generalBuffFoodReminder(_, _, _, _, unit)
    -- only take care of player
    if (unit ~= 'player') then return end

    local self = RaidNotifier
    -- remove event listener, the api won't get it on zoning
    if IsRaidInProgress() == false then
        self.removeEventListeners()
        return
    end

    -- return if the player don't want the reminder
    if self.savedVariables.general.buffFood_reminder == false then
        return
    end

    local buffsDebuffs = self.BuffsDebuffs.buffFood
    local buffFoodFound = false
    local reminderInterval = self.savedVariables.general.buffFood_reminder_interval

    local numAuras = GetNumBuffs('player')
    if (numAuras > 0) then
        local currentTime = GetTimeStamp()

        for x = 1, numAuras do
            local name, _, finish, _, _, _, _, _, _, _, abilityId, _ = GetUnitBuffInfo('player', x)
            if buffsDebuffs[abilityId] then
                -- set bufffoodfound to true
                buffFoodFound = true

                local bufffood_remaining = finish - (GetFrameTimeMilliseconds() / 1000.0)
                local lastNotify         = self.tempVars.last_notify.general.buffFood_reminder
                local lastNotifyDiff     = GetDiffBetweenTimeStamps(currentTime, lastNotify)
                local formatedTime       = ZO_FormatTime(bufffood_remaining, TIME_FORMAT_STYLE_COLONS, TIME_FORMAT_PRECISION_SECONDS)

                -- new set interval
                if bufffood_remaining <= 600 and (lastNotifyDiff > reminderInterval) then
                    -- every set interval
                    self.tempVars.last_notify.general.buffFood_reminder = currentTime
                    CENTER_SCREEN_ANNOUNCE:AddMessage(3, CSA_EVENT_SMALL_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, zo_strformat(GetString(RAIDNOTIFIER_ALERTS_GENERAL_BUFFFOOD_MINUTES), name, formatedTime))
                end
                if self.savedVariables.debug and buffFoodFound == false then
                    RaidNotifier.debug('bufffood id forgot?', abilityId)
                    RaidNotifier.debug('ability name', name)
                end
            end
        end

        -- no bufffood found, alert every interval
        if buffFoodFound == false then
            local lastNotify     = self.tempVars.last_notify.general.buffFood_reminder
            local lastNotifyDiff = GetDiffBetweenTimeStamps(currentTime, lastNotify)
            if lastNotifyDiff > reminderInterval then
                self.tempVars.last_notify.general.buffFood_reminder = currentTime
                CENTER_SCREEN_ANNOUNCE:AddMessage(3, CSA_EVENT_SMALL_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, GetString(RAIDNOTIFIER_ALERTS_GENERAL_NO_BUFFFOOD))
            end
        end
    else
        -- no buffs found, we can directly alert
        CENTER_SCREEN_ANNOUNCE:AddMessage(3, CSA_EVENT_SMALL_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, GetString(RAIDNOTIFIER_ALERTS_GENERAL_NO_BUFFFOOD))
    end
end

-- send pings
function RaidNotifier.sendPing(event)
    local self           = RaidNotifier
    local realEventCodes = self.pingEventCodes

    if event == 'manti_spear' then
        local lastPing     = self.tempVars.last_ping
        local currentTime  = GetTimeStamp()
        local lastPingDiff = GetDiffBetweenTimeStamps(currentTime, lastPing)
        local eventCode    = realEventCodes.mantikora_spear

        -- only ping every 5 seks
        if lastPingDiff > 5 then
            local eventCoordX = eventCode/100000
            local eventCoordY = eventCode/1000000

            --send the ping
            PingMap(MAP_PIN_TYPE_PING, MAP_TYPE_LOCATION_CENTERED, eventCoordX , eventCoordY)
            self.tempVars.last_ping = currentTime
        end
    end
end

-- receive pings
function RaidNotifier.receivePing(_, pingEventType, _, pingTag, offsetX, offsetY, isOwner)
    -- ignore
    if (offsetX == 0 and offsetY == 0) then return end
    if (pingEventType == PING_EVENT_REMOVED) then return end
    if (isOwner == true) then return end

    local self           = RaidNotifier
    local realEventCodes = self.pingEventCodes

    --ping data
    local unitName        = GetUnitName(pingTag)
    local eventCodeA      = math.abs(offsetX * 100000)
    local eventCodeB      = math.abs(offsetY * 1000000)
    local currentTime     = GetTimeStamp()
    local realEventCode   = realEventCodes.mantikora_spear -- atm we only have this one, @todo iterate over them and then switch for the different alerts
    local zosEventCodeMin = realEventCode - 100
    local zosEventCodeMax = realEventCode + 100

    -- make sure we have a correct eventCode
    -- the pingmap make some rounding errors, so we have to define a range and check if the ping is inside this range
    if (eventCodeA >= zosEventCodeMin and eventCodeA <= zosEventCodeMax) and (eventCodeB >= zosEventCodeMin and eventCodeB <= zosEventCodeMax) then
        local eventCode = realEventCode
        self.debug('eventCode matched', eventCode)
        self.debug('eventCode for manti spear')
        -- set last ping to now to prevent spamming
        self.tempVars.last_ping = currentTime

        CENTER_SCREEN_ANNOUNCE:AddMessage(10, CSA_EVENT_SMALL_TEXT, SOUNDS.CHAMPION_POINTS_COMMITTED, zo_strformat(GetString(RAIDNOTIFIER_ALERTS_SANCTUM_MANTIKORA_SPEAR_PLAYER), unitName))
    end
end

-- debug func
function RaidNotifier.debug(message, data)
    local self = RaidNotifier
    if self.savedVariables.debug then
        d('RaidNotifier Debug:')
        if data ~= nil then
            d(message, data)
        else
            d(message)
        end
    end
end

---------Events-------
EVENT_MANAGER:RegisterForEvent(RaidNotifier.name, EVENT_ADD_ON_LOADED, RaidNotifier.Initialize)