MEGA_DAMAGE_VERSION = GetAddOnMetadata("MegaDamage", "Version");
MEGA_DAMAGE_VERSION_ID = 10600;
MEGA_DAMAGE_LAST_SOUND = 0;
MEGA_DAMAGE_UNUSED = false;
MEGA_DAMAGE_BACKGROUND_FADETIMER = 0;
MEGA_DAMAGE = {};
MEGA_DAMAGE_PROFILE = "";
MEGA_DAMAGE_UNLOCKED = false;
MEGA_DAMAGE_SOUNDS = {
    [1] = {file = "Sound\\INTERFACE\\AlarmClockWarning2.ogg"},
    [2] = {file = "Sound\\Events\\ArchaniteRipper.ogg", cooldown = 12},
    [3] = {file = "Sound\\Spells\\avengingwrath_impact_base.ogg"},
    [4] = {file = "Sound\\Spells\\Bonk2.ogg"},
    [5] = {file = "Sound\\Spells\\Clearcasting_Impact_Chest.ogg", cooldown = 1},
    [6] = {file = "Sound\\Spells\\CrowdCheerAlliance1.ogg", cooldown = 5},
    [7] = {file = "Sound\\Spells\\DeathKnight_Deathgate.ogg", cooldown = 2},
    [8] = {file = "Sound\\Spells\\SPELL_DoomBolt_Impact_01.ogg", cooldown = 2},
    [9] = {file = "Sound\\Spells\\CallLightning.ogg", cooldown = 1},
    [10] = {file = "Sound\\Spells\\Tranquility.ogg", cooldown = 2},
    [11] = {file = "Sound\\event\\Event_Operation_Gnomergan_Explosion2.ogg", cooldown = 4},
    [12] = {file = "Sound\\Spells\\FX_Guitar_MetalRiffs_01.ogg", cooldown = 10},
    [13] = {file = "Sound\\Spells\\FX_Guitar_MetalRiffs_02.ogg", cooldown = 10},
    [14] = {file = "Sound\\Spells\\FX_Guitar_MetalRiffs_03.ogg", cooldown = 10},
    [15] = {file = "Sound\\Spells\\FX_Guitar_MetalRiffs_04.ogg", cooldown = 10},
    [16] = {file = "Sound\\Spells\\FX_Guitar_MetalRiffs_05.ogg", cooldown = 10},
    [17] = {file = "Sound\\INTERFACE\\LevelUp.ogg", cooldown = 3},
    [18] = {file = "Sound\\Spells\\MidSummer-TorchGameComplete.ogg", cooldown = 2},
    [19] = {file = "Sound\\INTERFACE\\RaidBossWarning.ogg", cooldown = 2},
    [20] = {file = "Sound\\Events\\scourge_horn.ogg", cooldown = 8},
    [21] = {file = "Sound\\Event\\EVENT_TippingTheBalance_HarrisonWhip_01.ogg", cooldown = 2},
    [22] = {file = "Sound\\Spells\\BrutallasBreakIceDoor.ogg", cooldown = 3},
    [23] = {file = "Sound\\Spells\\Decimate_StateStand.ogg", cooldown = 2},
    [24] = {file = "Sound\\Spells\\FairyTale_SFX_01.ogg", cooldown = 2},
    [25] = {file = "Sound\\Spells\\Frost_Cannon_Cast_01.ogg", cooldown = 2},
    [26] = {file = "Sound\\Spells\\SPELL_HeroicStrike_Impact_01.ogg", cooldown = 1},
};
MEGA_DAMAGE_SOUND_LASTPLAYED = {};

local L = LibStub("AceLocale-3.0"):GetLocale("MegaDamage", true);

--========================================
-- Initial load routine
--========================================
function MegaDamage_OnLoad(self)
    -- register events
    self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED");
    self:RegisterEvent("ADDON_LOADED");
	self.slot1 = MegaDamageFrameSlot1;
	self.slot2 = MegaDamageFrameSlot2;
    -- set fade defaults
	MegaDamage_FadeInit(self.slot1);
	MegaDamage_FadeInit(self.slot2);
    -- setup timings
	self.timings = { };
	self.timings["MEGA_DAMAGE_MIN_HEIGHT"] = 36.0;
	self.timings["MEGA_DAMAGE_MAX_HEIGHT"] = 48.0;
	self.timings["MEGA_DAMAGE_SCALE_UP_TIME"] = 0.2;
	self.timings["MEGA_DAMAGE_SCALE_DOWN_TIME"] = 0.4;

    -- register slash command
    SLASH_MEGADAMAGE1 = "/mdmg";
    SlashCmdList["MEGADAMAGE"] = MegaDamage_CommandHandler;
end

--========================================
-- Event triggers
--========================================
function MegaDamage_OnEvent(self, event, ...)

    if (event == "ADDON_LOADED") then
        local arg1 = ...;
        if (arg1 == "MegaDamage") then
            MegaDamage_Setup();
        end
    elseif (event == "COMBAT_LOG_EVENT_UNFILTERED") then
        local timestamp, type, hideCaster, sourceGUID, sourceName, sourceFlags, sourceFlags2, destGUID, destName, destFlags, destFlags2, parg1, parg2, parg3, sarg1, sarg2, sarg3, sarg4, sarg5, sarg6, sarg7, sarg8, sarg9 = ...;

        if (sourceGUID == UnitGUID("player")) then

            if (type == "SWING_DAMAGE") then
                MegaDamage_ProcessDamage("Auto Attack", sarg1, sarg7);
            elseif (type == "RANGE_DAMAGE") then
                MegaDamage_ProcessDamage(parg2, sarg1, sarg7);
            elseif (type == "SPELL_DAMAGE") then
                MegaDamage_ProcessDamage(parg2, sarg1, sarg7);
            elseif (type == "SPELL_PERIODIC_DAMAGE") then
                MegaDamage_ProcessDamage(parg2, sarg1, sarg7, true);
            elseif (type == "SPELL_HEAL") then
                MegaDamage_ProcessDamage(parg2, sarg1, sarg4, false, true);
            elseif (type == "SPELL_PERIODIC_HEAL") then
                MegaDamage_ProcessDamage(parg2, sarg1, sarg4, true, true);
            end
        end
    end

end

--========================================
-- Main setup routine
--========================================
function MegaDamage_Setup()

    MEGA_DAMAGE_PROFILE = GetRealmName() .. "." .. UnitName("player");

    MegaDamage_LoadSettings();

    if (MEGA_DAMAGE['config']['show_load_message']) then
        MegaDamage_Message(string.format(L["LOADED_MESSAGE"], MEGA_DAMAGE_VERSION));
    end

    MegaDamage_LockFrame(true);

end

--========================================
-- Check/initialize config and profile
--========================================
function MegaDamage_LoadSettings()

    local version = MegaDamage_CheckSetting("version", MEGA_DAMAGE_VERSION_ID);

    MEGA_DAMAGE['config']['version'] = MEGA_DAMAGE_VERSION_ID;

    MegaDamage_CheckSetting("enablesound", true);
    MegaDamage_CheckSetting("term_correction", false);
    MegaDamage_CheckSetting("lifetime_threshold", true);
    MegaDamage_CheckSetting("show_load_message", false);

    MegaDamage_CheckSetting("scale", 10);
    MEGA_DAMAGE['config']['scale'] = math.max(5, math.min(15, MEGA_DAMAGE['config']['scale']));
    MegaDamageFrame:SetScale(MEGA_DAMAGE['config']['scale'] * 0.1);

    MegaDamage_CheckSetting("opacity", 10);
    MEGA_DAMAGE['config']['opacity'] = math.max(0, math.min(10, MEGA_DAMAGE['config']['opacity']));

    MegaDamage_CheckProfileSetting("min_damage", 30);
    MegaDamage_CheckProfileSetting("min_heal", 30);

    MegaDamage_CheckProfileSetting("lifetime_damage", 0);
    MegaDamage_CheckProfileSetting("lifetime_healing", 0);
    MegaDamage_CheckProfileSetting("selected_sound_damage", 2);
    MegaDamage_CheckProfileSetting("selected_sound_healing", 2);

end

--========================================
-- Check configuration setting, and
-- initialize with default value if not
-- present
--========================================
function MegaDamage_CheckSetting(field, defaultValue)

    -- make sure the config array is present
    if (MEGA_DAMAGE['config'] == nil) then
	    MEGA_DAMAGE['config'] = {};
	end

	if (not MEGA_DAMAGE['config'][field]) then
	    MEGA_DAMAGE['config'][field] = defaultValue;
	end
    return MEGA_DAMAGE['config'][field];
end

--========================================
-- Check character-specific setting, and
-- initialize with default value if not
-- present
--========================================
function MegaDamage_CheckProfileSetting(field, defaultValue)

    if (MEGA_DAMAGE[MEGA_DAMAGE_PROFILE] == nil) then
        MEGA_DAMAGE[MEGA_DAMAGE_PROFILE] = {};
    end

    if (not MEGA_DAMAGE[MEGA_DAMAGE_PROFILE][field]) then
        MEGA_DAMAGE[MEGA_DAMAGE_PROFILE][field] = defaultValue;
    end
    return MEGA_DAMAGE[MEGA_DAMAGE_PROFILE][field];
end

--========================================
-- Main update routine
--========================================
function MegaDamage_OnUpdate(noticeFrame, elapsedTime)
	if ((not noticeFrame) or (not noticeFrame.slot1) or (not noticeFrame.slot2) or (MEGA_DAMAGE_UNLOCKED)) then
		return;
	end

	MEGA_DAMAGE_UNUSED = true;
	if (noticeFrame.slot1:IsShown()) then
		MegaDamage_UpdateSlot(noticeFrame.slot1, noticeFrame.timings, elapsedTime);
		MEGA_DAMAGE_UNUSED = false;
	end

	if (noticeFrame.slot2:IsShown()) then
		MegaDamage_UpdateSlot(noticeFrame.slot2, noticeFrame.timings, elapsedTime);
		MEGA_DAMAGE_UNUSED = false;
	end
	
	if (MEGA_DAMAGE_UNUSED) then
		noticeFrame:Hide();
	end

    MegaDamageFrameBackground:SetAlpha(math.min(1, math.max(0, ((MEGA_DAMAGE_BACKGROUND_FADETIMER + 8.2) - GetTime()) / 3)) * (MegaDamageConfig_GetOpacity() * 0.01));
    MegaDamageFrameFlash:SetAlpha(math.min(1, math.max(0, (MEGA_DAMAGE_BACKGROUND_FADETIMER + 1) - GetTime())));

end

--========================================
-- Text fade setup
--========================================
function MegaDamage_FadeInit(slotFrame)
	FadingFrame_OnLoad(slotFrame);
	FadingFrame_SetFadeInTime(slotFrame, 0.2);
	FadingFrame_SetHoldTime(slotFrame, 5.0);
	FadingFrame_SetFadeOutTime(slotFrame, 3.0);
end

--========================================
-- Setting text line
--========================================
function MegaDamage_SetSlot(slotFrame, textString, colorInfo, minHeight)
	slotFrame:SetText(textString);
	slotFrame:SetTextColor(colorInfo.r, colorInfo.g, colorInfo.b, 1.0);
	slotFrame:SetTextHeight(minHeight);
	FadingFrame_Show(slotFrame);
end

--========================================
-- Activate display with specified
-- spell/ability name and calculated
-- mega damage
--========================================
function MegaDamage_AddMessage(spellName, damage, isCrit, isPeriodic, isHeal, noSound, noFlash)
    local noticeFrame = MegaDamageFrame;

	if ((not noticeFrame) or (not noticeFrame.slot1) or (not noticeFrame.slot2) or (MEGA_DAMAGE_UNLOCKED)) then
		return;
	end

	noticeFrame:Show();

    -- set text color
    local lineColor1 = {r = 1, g = 0.95, b = 0.59};
    if (isHeal) then
        lineColor1 = {r = 0, g = 1, b = 0};
    end
    local lineColor2 = {r = 0.375, g = 0.125, b = 0.06};

    -- default MEGA DAMAGE / MEGA HEALING text
    if (isHeal) then
        noticeFrame.slot2_text = L["MEGA_HEALING"];
    else
        noticeFrame.slot2_text = L["MEGA_DAMAGE"];
    end

    -- make adjustments for prefix correction
    local dispValue = damage;
    if (damage >= 1000) then
        if (MEGA_DAMAGE['config']['term_correction']) then
            dispValue = math.floor(dispValue / 1000);
        end
    else
        if (MEGA_DAMAGE['config']['term_correction']) then
            if (isHeal) then
                noticeFrame.slot2_text = L["KILO_HEALING"];
            else
                noticeFrame.slot2_text = L["KILO_DAMAGE"];
            end
        end
    end

    -- set spell text (e.g. "Your ___ crit for _")
    if (isCrit) then
        noticeFrame.slot1_text = string.format(L["SPELL_CRIT"], spellName, dispValue);
    else
        if (isPeriodic) then
            noticeFrame.slot1_text = string.format(L["SPELL_TICK"], spellName, dispValue);
        else
            if (isHeal) then
                noticeFrame.slot1_text = string.format(L["SPELL_HEAL"], spellName, dispValue);
            else
                noticeFrame.slot1_text = string.format(L["SPELL_HIT"], spellName, dispValue);
            end
        end
    end

    -- add text to display
	MegaDamage_SetSlot( noticeFrame.slot1, noticeFrame.slot1_text, lineColor1, noticeFrame.timings["MEGA_DAMAGE_MIN_HEIGHT"] );
	noticeFrame.slot1.scrollTime = 0;
	MegaDamage_SetSlot( noticeFrame.slot2, noticeFrame.slot2_text, lineColor2, noticeFrame.timings["MEGA_DAMAGE_MIN_HEIGHT"] );
	noticeFrame.slot2.scrollTime = 0;

    -- play sound if enabled and not too soon after the last playback
    if (not noSound) then
        if (MEGA_DAMAGE['config']['enablesound']) then
            if (isHeal) then
                MegaDamage_PlaySound(MEGA_DAMAGE[MEGA_DAMAGE_PROFILE]['selected_sound_healing']);
            else
                MegaDamage_PlaySound(MEGA_DAMAGE[MEGA_DAMAGE_PROFILE]['selected_sound_damage']);
            end
        end
    end

    -- set flash
    MEGA_DAMAGE_BACKGROUND_FADETIMER = GetTime();
    if (noFlash) then
        MegaDamageFrameFlash:Hide();
    else
        MegaDamageFrameFlash:Show();
    end
end

--========================================
-- Update individual text line
--========================================
function MegaDamage_UpdateSlot(slotFrame, timings, elapsedTime)
	if (slotFrame.scrollTime) then
		slotFrame.scrollTime = slotFrame.scrollTime + elapsedTime;
		if (slotFrame.scrollTime <= timings["MEGA_DAMAGE_SCALE_UP_TIME"]) then
			slotFrame:SetTextHeight(floor(timings["MEGA_DAMAGE_MIN_HEIGHT"]+((timings["MEGA_DAMAGE_MAX_HEIGHT"]-timings["MEGA_DAMAGE_MIN_HEIGHT"])*slotFrame.scrollTime/timings["MEGA_DAMAGE_SCALE_UP_TIME"])));
		elseif ( slotFrame.scrollTime <= timings["MEGA_DAMAGE_SCALE_DOWN_TIME"] ) then
			slotFrame:SetTextHeight(floor(timings["MEGA_DAMAGE_MAX_HEIGHT"] - ((timings["MEGA_DAMAGE_MAX_HEIGHT"]-timings["MEGA_DAMAGE_MIN_HEIGHT"])*(slotFrame.scrollTime - timings["MEGA_DAMAGE_SCALE_UP_TIME"])/(timings["MEGA_DAMAGE_SCALE_DOWN_TIME"] - timings["MEGA_DAMAGE_SCALE_UP_TIME"]))));
		else
			slotFrame:SetTextHeight(timings["MEGA_DAMAGE_MIN_HEIGHT"]);
			slotFrame.scrollTime = nil;
		end
	end	
	FadingFrame_OnUpdate(slotFrame);
end

--========================================
-- Process spell damage/healing and
-- calculate mega damage value
--========================================
function MegaDamage_ProcessDamage(spellName, damage, isCrit, isPeriodic, isHeal)
    damage = tonumber(damage) or 0;
    if (damage) then
        local minValue = 0;
        local totalField = "lifetime_damage";
        local minRecord = 0;
        -- pick threshold and lifetime record field to use (damage/healing)
        if (isHeal) then
            minValue = MEGA_DAMAGE[MEGA_DAMAGE_PROFILE]['min_heal'];
            totalField = "lifetime_healing";
        else
            minValue = MEGA_DAMAGE[MEGA_DAMAGE_PROFILE]['min_damage'];
        end
        -- check if the lifetime totals are supposed to obey the main thresholds
        if (MEGA_DAMAGE['config']['lifetime_threshold']) then
            minRecord = minValue;
        end
        -- add to lifetime total
        if (damage >= (minRecord * 1000)) then
            MEGA_DAMAGE[MEGA_DAMAGE_PROFILE][totalField] = MEGA_DAMAGE[MEGA_DAMAGE_PROFILE][totalField] + math.floor(damage / 1000);
            MegaDamageConfig_UpdateTotalsDisplay();
        end
        -- check damage amount against selected threshold
        if (damage >= (minValue * 1000)) then
            local mDmg = math.floor(damage / 1000);
            MegaDamage_AddMessage(spellName, mDmg, isCrit, isPeriodic, isHeal);
        end
    end
end

--========================================
-- Slash command (/mdmg) handler
--========================================
function MegaDamage_CommandHandler(cmd)

    InterfaceOptionsFrame_OpenToCategory(MegaDamageConfigContainer);

end

--========================================
-- Outputs status message to chat window
--========================================
function MegaDamage_Message(text)
    DEFAULT_CHAT_FRAME:AddMessage("|cffa080ff<" .. L["ADDON_TITLE"] .. ">|cffffffff " .. text, 1, 1, 1);
end

--========================================
-- Unlocks the frame's position
--========================================
function MegaDamage_UnlockFrame()
    if (not MEGA_DAMAGE_UNLOCKED) then
        MegaDamageFrame:Show();
        MegaDamageFrame:EnableMouse(1);
        MegaDamageFrameUnlockBG:Show();
        MegaDamageFrameBackground:SetAlpha(1);
        MegaDamageFrameFlash:Hide();
        MegaDamageFrameSlot1:SetText(L["FRAME_UNLOCKED1"]);
        MegaDamageFrameSlot1:SetTextHeight(MegaDamageFrame.timings["MEGA_DAMAGE_MIN_HEIGHT"]);
        MegaDamageFrameSlot1:SetTextColor(1, 0.95, 0.59);
        MegaDamageFrameSlot1:Show();
        MegaDamageFrameSlot2:SetText(L["FRAME_UNLOCKED2"]);
        MegaDamageFrameSlot2:SetTextHeight(MegaDamageFrame.timings["MEGA_DAMAGE_MIN_HEIGHT"]);
        MegaDamageFrameSlot2:SetTextColor(0.375, 0.125, 0.06);
        MegaDamageFrameSlot2:Show();
        MegaDamageFrame:RegisterForDrag("LeftButton");
        MEGA_DAMAGE_UNLOCKED = true;
    end
end

--========================================
-- Locks the frame's position
--========================================
function MegaDamage_LockFrame(force)
    if ((MEGA_DAMAGE_UNLOCKED) or (force)) then
        MegaDamageFrame:EnableMouse(0);
        MegaDamageFrame:Hide();
        MegaDamageFrameUnlockBG:Hide();
        MegaDamageFrame:RegisterForDrag();
        MEGA_DAMAGE_UNLOCKED = false;
    end
end

--========================================
-- Resets the frame's position on the
-- screen
--========================================
function MegaDamage_ResetPosition(silent)
    local oldIsMovable = MegaDamageFrame:IsMovable();
    MegaDamageFrame:SetUserPlaced(0);
    MegaDamageFrame:ClearAllPoints();
    MegaDamageFrame:SetPoint("TOP", "RaidWarningFrame", "BOTTOM", 0, 0);
    MegaDamageFrame:SetWidth(750);
    MegaDamageFrame:SetHeight(250);
	if (not silent) then
        MegaDamage_Message(L["POSITION_RESET_MESSAGE"]);
	end
end

--========================================
-- Play a sound
--========================================
function MegaDamage_PlaySound(index, ignoreCooldown)

    if (MEGA_DAMAGE_SOUNDS[index].cooldown) then
        if (not ignoreCooldown) then
            if (MEGA_DAMAGE_SOUND_LASTPLAYED[index]) then
                if (GetTime() - MEGA_DAMAGE_SOUND_LASTPLAYED[index] < MEGA_DAMAGE_SOUNDS[index].cooldown) then
                    return;
                end
            end
        end
        MEGA_DAMAGE_SOUND_LASTPLAYED[index] = GetTime();
    end

    PlaySoundFile(MEGA_DAMAGE_SOUNDS[index].file);

end
