CooldownsComm = AsheylaLib:NewModule("CooldownsComm", "KBLib");
local listener = CooldownsComm;
local Cooldowns = Cooldowns;
local format = string.format;
local find = string.find;
local sub = string.sub;
local match = string.match;
local lower = string.lower;
local tostring, tonumber = tostring, tonumber;
local GetTime = GetTime;
local GetNumRaidMembers, GetNumPartyMembers = GetNumRaidMembers, GetNumPartyMembers;

function listener:MakeTimer(timer)
    if self:Get("sending") and (GetNumRaidMembers() > 0 or GetNumPartyMembers() > 0 or self:Get("debugging")) and timer.module == Cooldowns and timer.duration > 5 then 
        SendAddonMessage("DTMR", format("CN`%s`%s`%.2f`%.2f`%s", tostring(timer), timer.name, timer.duration, GetTime() - timer.time, timer.texture), self:Get("debugging") and "WHISPER" or "RAID", UnitName("player")); 
    end
end

function listener:RemoveTimer(timer)
    if self:Get("sending") and (GetNumRaidMembers() > 0 or GetNumPartyMembers() > 0 or self:Get("debugging")) and timer.module == Cooldowns then 
        SendAddonMessage("DTMR", format("CR`%s", tostring(timer)), self:Get("debugging") and "WHISPER" or "RAID", UnitName("player")); 
    end
end

function listener:UpdateTimerData(timer)
    if self:Get("sending") and (GetNumRaidMembers() > 0 or GetNumPartyMembers() > 0 or self:Get("debugging")) and timer.module == Cooldowns then 
        SendAddonMessage("DTMR", format("CUT`%s`%s`%s", tostring(timer), timer.name, timer.texture), self:Get("debugging") and "WHISPER" or "RAID", UnitName("player")); 
    end
end

local timers = {};

local updateScript = function(self, elapsed)
    local time = GetTime();
    local timersRunning = false;
    for str, timer in pairs(timers) do
        if time >= timer.time + timer.duration then
            timer.target:RemoveTimer(timer);
            timers[str] = nil;
        else
            timersRunning = true;
        end
    end
    
    if not timersRunning then
        self:SetScript("OnUpdate", nil);
    end
end

local settings = {
    ["sending"] = true,
    ["receiving"] = {},
    ["standardAnnounce"] = "%p's cooldown for %s will expire in %d.",
    ["timerSettings"] = nil,
};
listener:AddDefaultSettings(settings);
local f = CreateFrame("Frame");
f:RegisterEvent("PLAYER_ENTERING_WORLD");
f:SetScript("OnEvent", function(self, event)
    self:UnregisterEvent("PLAYER_ENTERING_WORLD");
    
    self:SetScript("OnEvent", function(self, event, prefix, msg, loc, sender)
        if event == "CHAT_MSG_ADDON" then
            if prefix == "DTMR" and (sender ~= UnitName("player") or listener:Get("debugging")) then
                local msgType = sub(msg, 1, (find(msg, "`") or 1) - 1);
                if msgType == "CN" then
                    local str, name, duration, elapsed, texture = match(msg, "CN`(.-)`(.-)`(.-)`(.-)`(.-)$");
                    if not (listener:Get("debugging") or listener:Get("receiving", lower(name))) then return; end
                    str = sender..str;
                    timers[str] = Cooldowns:AddExternalTimer(listener, name, texture, GetTime() - tonumber(elapsed), tonumber(duration));
                    timers[str].origModule = Cooldowns;
                    timers[str].sender = sender;
                    self:SetScript("OnUpdate", updateScript);
                elseif msgType == "CR" then
                    local str = match(msg, "CR`(.-)$");
                    str = sender..str;
                    local timer = timers[str];
                    if timer then
                        timer.target:RemoveTimer(timer);
                        timers[str] = nil;
                    end
                elseif msgType == "CUT" then
                    local str, name, texture = match(msg, "CUT`(.-)`(.-)`(.-)$");
                    str = sender..str;
                    local timer = timers[str];
                    if timer then
                        timer:UpdateInfo(name, texture);
                    end
                end
            end
        end
    end);
    
    listener:AddSettingsUpdateScript(function(self)
        if self:Get("enabled") then
            self:RegisterEvent("COOLDOWNS_NEW_TIMER", self.MakeTimer);
            self:RegisterEvent("COOLDOWNS_DONE_TIMER", self.RemoveTimer);
            self:RegisterEvent("COOLDOWNS_UPDATE_TIMERDATA", self.UpdateTimerData);
            f:RegisterEvent("CHAT_MSG_ADDON");
            
            for id, timer in pairs(timers) do
                if self:Get("receiving", lower(timer.name)) then
                    timer.target:RemoveTimer(timer);
                    timers[id] = nil;
                end
            end
        else
            self:RegisterEvent("COOLDOWNS_NEW_TIMER");
            self:RegisterEvent("COOLDOWNS_DONE_TIMER");
            self:RegisterEvent("COOLDOWNS_UPDATE_TIMERDATA");
            f:UnregisterEvent("CHAT_MSG_ADDON");
            
            for id, timer in pairs(timers) do
                timer.target:RemoveTimer(timer);
                timers[id] = nil;
            end
        end
    end);
    
    listener:UpdateSettings();
end);

function listener:GetTimerSettings(module, name)
    return self:Get("timerSettings");
end

listener:SetScript("OnTimerEnter", function(self, target, timer, id, frame)
    if timer then
        local SpellLib = AsheylaLib:GetLibrary("SpellLib");
        target = timer.target;
        Ash_GameTooltip:SetOwner(frame, "ANCHOR_RIGHT");
        Ash_GameTooltip:ClearLines();
        local time = GetTime();
        
        if timer.foundTooltip == nil then
            timer.foundTooltip = false;
            local _, book, index = SpellLib:FindSpellInfo(timer.name, timer.rank, "spell");
            if book and index then
                timer.foundTooltip = true;
                timer.tooltipType = "spell";
                timer.tooltipBook = book;
                timer.tooltipIndex = index;
            else
                local _, _, itemID = SpellLib:FindSpellInfo(timer.name, nil, "item");
                if itemID then
                    timer.foundTooltip = true;
                    timer.tooltipType = "item";
                    timer.tooltipItemID = itemID;
                end
            end
        end
        
        if timer.foundTooltip then
            if timer.tooltipType == "spell" then
                Ash_GameTooltip:SetSpell(timer.tooltipIndex, timer.tooltipBook);
            else
                Ash_GameTooltip:SetHyperlink(SpellLib:ReturnItemLink(timer.tooltipItemID));
            end
            Ash_GameTooltip:AddLine(" ");
        end
        
        Ash_GameTooltip:AddLine(string.format("|cff00ff00%s|r %s", timer.name, timer.rank and "(|cff00ffff" .. timer.rank .. "|r)" or ""));
        Ash_GameTooltip:AddLine(string.format("From: |cffff00ff%s", timer.sender)); 
        Ash_GameTooltip:AddLine("Remaining: |cff6666ff" .. id:FormatTime(timer.duration - time + timer.time));
        
        Ash_GameTooltip:AddLine(" ");
        Ash_GameTooltip:AddLine("|cffffffff" .. listener:GetKeyBinding("Timer", "Announce") .. "|r to announce.");
        Ash_GameTooltip:AddLine("|cffffffff" .. listener:GetKeyBinding("Timer", "Remove") .. "|r to remove.");
        Ash_GameTooltip:AddLine("|cffffffff" .. listener:GetKeyBinding("Timer", "Block") .. "|r to block.");
        
        Ash_GameTooltip:Show();
    end
end)

listener:SetScript("OnTimerLeave", function(self, target, timer, id, frame)
    Ash_GameTooltip:Hide();
end)

listener:SetScript("OnTimerClick",function(self, target, timer, id, button)
    if timer then
        listener:ProcessClick("Timer", button, timer, id);
    end
end)
listener:SetClickAction("Timer", "Remove", function(timer, id)
    TimerLib:DeleteTimer(id, timer, 1);
end)
listener:SetClickAction("Timer", "Block", function(timer)
    if timer.name then
        local frame = StaticPopup_Show("COOLDOWNSCOMM", timer.name)
        frame.data = timer.name;
    end
end)
local subTimer, subID;
local subFunc = function(a)
    if a == "%s" then
        return subTimer.name;
    elseif a == "%d" then
        return subID:FormatTime(subTimer.duration - GetTime() + subTimer.time);
    elseif a == "%p" then
        return subTimer.sender;
    else
        return "";
    end
end
listener:SetClickAction("Timer", "Announce", function(timer, id)
    if listener:Get("isAnnouncing") then
        subTimer, subID = timer, id;
        local subStr = listener:Get("standardAnnounce");
        local msg = string.gsub(subStr, "(%%%a)", subFunc);
        local loc = GetNumRaidMembers() > 0 and "RAID" or GetNumPartyMembers() > 0 and "PARTY" or "SAY";
        SendChatMessage(msg, loc);
    end
end)
StaticPopupDialogs["COOLDOWNSCOMM"] = {
    text = "Are you sure you want to stop receiving |cff00ff00%s|r?  This can be undone from the options panel.",
    button1 = "Yes",
    button2 = "No",
    OnAccept = function(self, name, module) listener:Set("receiving", string.lower(name), nil); end,
    whileDead = 1,
    hideOnEscape = 1,
    timeout = 0,
}

local settings = {
    enabled = true,
    debugging = false,
    isAnnouncing = true,
    keybindings = {},
};
listener:AddDefaultSettings(settings);
listener:AddSettingsUpdateScript(function(self)
    if self:GetUserSet("keybindings") == nil then
        self:Set("keybindings", {
    		["Timer"] = {
    			["Announce"] = "1",
    			["Remove"] = "2",
    			["Block"] = "s-2",
    		},
    	});
    end
end);
