Notifications = AsheylaLib:NewModule("Notifications");

function Notifications:FireEvent(event, ...)
    local events = self:Get("events", event);
    if not events then return; end
    local comp = select(1, ...);
    if events.exact then
        for _, subEvent in ipairs(events.exact) do
            if (string.lower(comp) == string.lower(subEvent.comp)) then
                self:FireAnnouncements(subEvent, ...);
                return;
            end
        end
    end
    
    if events.partial then
        for _, subEvent in ipairs(events.partial) do
            if (string.find(string.lower(comp), string.lower(subEvent.comp))) then
                self:FireAnnouncements(subEvent, ...);
                return;
            end
        end
    end
    
    if events.all then
        self:FireAnnouncements(events.all, ...);
    end
end

function Notifications:FireAnnouncements(entries, ...)
    if entries.always then
        for _, data in ipairs(entries.always) do
            self:AnnounceOrDelay(data, ...);
        end
    end
    
    if entries.random and math.random() <= entries.rand / 100 then
        self:AnnounceOrDelay(entries.random[math.random(1, #entries.random)], ...);
    end
end

function Notifications:AnnounceOrDelay(data, ...)
    if data.delay and data.delay > 0 then
        self:Delay(data, ...);
    else
        self:Announce(data, ...);
    end
end

local delayFrame = CreateFrame("Frame");
delayFrame.delayedEvents = {};
local delayScript = function(self, elapsed)
    for i, delay in ipairs(self.delayed) do
        delay.elapsed = delay.elapsed + delay;
        if delay.elapsed >= delay.duration then
            Notifications:Announce(delay.data, delay.args);
            table.remove(self.delayed, i);
            Notifications:ReleaseTable(delay);
            if #self.delayed == 0 then
                self:SetScript("OnUpdate", nil);
            end
        end
    end
end
function Notifications:Delay(data, ...)
    local delay = self:AcquireTable();
    delay.elapsed = 0;
    delay.duration = data.delay;
    delay.data = data;
    delay.args = self:AcquireTable();
    for i = 1, select("#", ...) do
        delay.args[i] = select(i, ...);
    end
    table.insert(delayFrame.delayedEvents, delay);
    delayFrame:SetScript("OnUpdate", delayScript);
end

local subEntries = {};
local subFunc = function(num) return subEntries[tonumber(num)] or num; end
function Notifications:Announce(data, ...)
    local msg, target = data.msg, data.target;
    if (data.msg and string.find(data.msg, "%%%d")) or (data.target and string.find(data.target, "%%%d")) then
        if type(...) == "table" then
            self:ReleaseTable(subEntries);
            subEntries = ...;
        else
            local num = select("#", ...);
            for i = 1, num do
                subEntries[i] = select(i, ...);
            end
            for i = num + 1, #subEntries do
                table.remove(subEntries, num + 1);
            end
        end
        if data.msg then msg = string.gsub(data.msg, "%%(%d)", subFunc); end
        if data.target then target = string.gsub(data.target, "%%(%d)", subFunc); end
    end
    
    if data.type == "sound" then
        self:PlaySound("Interface\\AddOns\\DoTimer\\Core\\Files\\" .. data.file);
    elseif data.type == "sct" then
        self:SCTAlert(msg, data.color);
    elseif data.type == "alert" then
        self:Alert(msg, data.color.r, data.color.g, data.color.b);
    elseif data.type == "text" then
        self:Text(msg, data.color.r, data.color.g, data.color.b);
    elseif data.type == "flash" then
        self:Flash(data.colorName);
    elseif data.type == "chat" then
        self:ChatMsg(msg, data.chatType, target);
    elseif data.type == "timer" then
        self:Timer(msg, data.duration);
    end
end

local defaultSound = "Interface\\AddOns\\DoTimer\\Core\\Files\\alert.wav";
function Notifications:PlaySound(soundFile)
    PlaySoundFile(soundFile or defaultSound);
end

local defaultColor = {r = 1, g = 1, b = 1};
function Notifications:SCTAlert(msg, color)
    msg = msg or "Alert!";
    color = color or defaultColor;
    if SCT then 
        SCT:DisplayText(msg, color, nil , nil, 1);
    elseif MikSBT then
        MikSBT.DisplayMessage(msg, nil, nil, (color.r * 255), (color.g * 255), (color.b * 255));
    elseif IsAddOnLoaded("Blizzard_CombatText") then
        CombatText_AddMessage(msg, COMBAT_TEXT_SCROLL_FUNCTION, color.r, color.g, color.b);
    end
end

local alertFrame = CreateFrame("Frame");
local f = alertFrame:CreateFontString();
local anchor = CreateFrame("Frame");
anchor:SetWidth(1);
anchor:SetHeight(1);
alertFrame.text = f;
alertFrame.anchor = anchor;
f:SetParent(alertFrame);
local name, height = NumberFontNormal:GetFont();
f:SetFont(name, 20, "THICKOUTLINE");
f:ClearAllPoints();
f:SetPoint("CENTER", anchor);
f:SetText("");
alertFrame:SetAllPoints(f);
alertFrame.updateFunc = function(self, elapsed)
    self.elapsed = self.elapsed + elapsed;
    elapsed = self.elapsed;
    local alpha, scale = 1, 1;
    if elapsed <= .2 then
        alpha = 5 * elapsed;
        scale = .8 + (2 * elapsed);
    elseif elapsed <= .4 then
        scale = 1.2 - elapsed + .2;
    elseif elapsed >= self.duration + 1 then
        alpha = 0;
        self:SetScript("OnUpdate", nil);
    elseif elapsed >= self.duration then
        alpha = 1 - elapsed + self.duration;
    end
    self:SetScale(scale);
    self:SetAlpha(alpha);
end
function alertFrame.SetAlertText(self, text, r, g, b, duration, height)
    self.text:SetText(text or "Alert!");
    self.text:SetTextColor(r or 1, g or 1, b or 1);
    self.anchor:SetPoint("CENTER", UIParent, "CENTER", 0, height or 75);
    self.elapsed = 0;
    self.duration = duration or 3;
    self:SetScale(.8);
    self:SetAlpha(0);
    self:SetScript("OnUpdate", self.updateFunc);
end

function Notifications:Alert(msg, r, g, b, duration)
    alertFrame:SetAlertText(msg, r, g, b, duration);
end

function Notifications:Text(msg, r, g, b)
    msg = msg or "Default Msg";
    DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b);
end

local flashFrame = CreateFrame("Frame", "NotificationsFlashFrame", WorldFrame);
flashFrame:Hide();
flashFrame:SetAllPoints(true);
flashFrame:SetToplevel(true);
local t = flashFrame:CreateTexture(nil, "BACKGROUND");
flashFrame.texture = t;
t:SetBlendMode("ADD");
t:SetAllPoints(true);
function flashFrame.SetFlashColor(self, color)
    self.texture:SetTexture("Interface\\AddOns\\DoTimer\\Core\\Files\\Flash" .. color);
end

function Notifications:Flash(color, fadeInTime, fadeOutTime, duration)
    flashFrame:SetFlashColor(color or "Red");
    UIFrameFlash(flashFrame, fadeInTime or .5, fadeOutTime or .5, duration or 2);
end

function Notifications:ChatMsg(msg, chat, target)
    msg = msg or "Default Msg";
    chat = chat or "say";
    if type(tonumber(chat)) == "number" then
        chat = tonumber(chat);
        local id, name = GetChannelName(chat);
        if id > 0 then
            SendChatMessage(msg, "CHANNEL", nil, id);
        end
        return;
    end
    if GetChannelName(chat) > 0 then
        local id, name = GetChannelName(chat);
        SendChatMessage(msg, "CHANNEL", nil, id);
        return;
    end
    chat = string.lower(chat);
    if chat == "auto" then
        if GetNumRaidMembers() > 0 then 
            chat = "raid";
        elseif GetNumPartyMembers() > 0 then 
            chat = "party"; 
        else 
            return; 
        end
    end
    if chat == "whisper" then
        SendChatMessage(msg, chat, nil, target);
    elseif chat == "emote" then
        DoEmote(msg);
    else
        SendChatMessage(msg, chat);
    end
end

function Notifications:Timer(msg, duration)
    msg = msg or "Default Msg";
    duration = duration or 5;
    local timer = {name = msg, duration = duration, time = GetTime()};
    TimerLib:NewTimer(timer, "Notifications", self, self:Get("timerSetting"), nil, nil, "Notifications Anchor", nil, nil);
end

local ttnmatches = {
	["s"] = 1,
	["m"] = 60,
	["h"] = 3600,
	["d"] = 86400,
}
function Notifications:TextToNum(text)
	local total = tonumber(text) or 0;
    for num, letter in string.gmatch(text, "(%d+)([smhd])") do
        total = total + (num * ttnmatches[letter]);
    end
    return total;
end

local nttmatches = {
	{len = 86400,letter = "d"},
	{len = 3600,letter = "h"},
	{len = 60,letter = "m"},
	{len = 1,letter = "s"},
}
function Notifications:NumToText(num)
    if num == 0 then return "0s"; end
	local text = "";
    for _, info in ipairs(nttmatches) do
        local n = math.floor(num / info.len);
        if n > 0 then
            text = text .. n .. info.letter .. " ";
            num = num - (n * info.len);
        end
    end
    return text;
end

local defaultSettings = {
    enabled = false,
    timerSetting = nil,
    events = {},
};
Notifications:AddDefaultSettings(defaultSettings);

function Notifications:Commands()
    local loaded = LoadAddOn("DoTimer_Options");
    if loaded then
        GUILib:ShowPanel("Notifications", "main");
    end
end
Notifications:MakeSlashCmd("/notifications", "/not");

TimerLib:AddSettingsUpdateScript(function(self)
    if not self:GetUserSet("anchorSettings", "Notifications Anchor") then
        self:Set("anchorSettings", "Notifications Anchor", {});
    end
end);
