-------------------------------------------------------------------------------
-- Quu Spell Alert
-- A mod that alerts about spell effects. Written by Quu
-- Original "Spell Alert" written by Awen
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Contants
-- The things that make this tick
-------------------------------------------------------------------------------
BINDING_HEADER_QSATITLE = "Quu Spell Alert";
QSA_VERSION             = "2.5.1";
local QSA_SLASH_COMMAND = "/qsa";
-- how long alerts should be displayed
local QSA_NON_CRIT_TIME = 2;
local QSA_CRIT_TIME     = 5;

-------------------------------------------------------------------------------
-- controll variables
-- these are the variables that are saved between sessions, and variables
-- that enable the "controll" logic
-------------------------------------------------------------------------------

-- the basics (there are stored in the savedvariables.lua
QSA_SavedVars_DEFAULT = { 
	Enabled = true;
	
	CombatOnly = true;
	TargetOnly = false;
	
	ShortDisplay = true;
	
	EmoteMode = true;
	GainMode = true;
	FadeMode = false;
	Afflictions = true;
	
	QSA_Hide = false;
};

QSA_SavedVars = { 
};
QSA_RED   = 1.0;
QSA_GREEN = 0.2;
QSA_BLUE  = 0.2;
QSA_ALPHA = 1.0;

function getVar( varName)
	local playerName = UnitName( "player");
	if (not QSA_SavedVars[playerName]) then
		QSA_SavedVars[playerName] = QSA_SavedVars_DEFAULT;
	end
	
	return QSA_SavedVars[playerName][varName];
end

function setVar( varName, value)
	local playerName = UnitName( "player");
	if (not QSA_SavedVars[playerName]) then
		QSA_SavedVars[playerName] = QSA_SavedVars_DEFAULT;
	end
	
	QSA_SavedVars[playerName][varName] = value;
end

function toggleVar( varName) 
	local playerName = UnitName( "player");
	if (not QSA_SavedVars[playerName]) then
		QSA_SavedVars[playerName] = QSA_SavedVars_DEFAULT;
	end
	
	QSA_SavedVars[playerName][varName] = not QSA_SavedVars[playerName][varName];
end


-- this is the last caster that was captured
-- used for the key binding
local QSA_LastTarget;

-------------------------------------------------------------------------------
-- Alert functions
-------------------------------------------------------------------------------
-- print simple messages
function QSA_println( Message)
	DEFAULT_CHAT_FRAME:AddMessage(Message, 1.0, 1.0, 1.0);
end

-- normal alerts
function QSA_Banner(msg)
	QuuSpellAlertNormalFrame:AddMessage(msg, QSA_RED, QSA_GREEN, QSA_BLUE, QSA_ALPHA, QSA_NON_CRIT_TIME);
end

-- critical alerts
function QSA_Critical(msg)
	QuuSpellAlertCriticalFrame:AddMessage(msg, QSA_RED, QSA_GREEN, QSA_BLUE, QSA_ALPHA, QSA_CRIT_TIME);
end

-------------------------------------------------------------------------------
-- the colour picking stuff (and alpha)
-------------------------------------------------------------------------------
-- initial loading of the color chooser
function QSA_ChooseColor()
	ColorPickerFrame.func = QSA_SetColor;
	ColorPickerFrame.hasOpacity = true;
	ColorPickerFrame.opacityFunc = QSA_SetOpacity;
	ColorPickerFrame.opacity = 1.0 - QSA_ALPHA;
	ColorPickerFrame:SetColorRGB(QSA_RED, QSA_GREEN, QSA_BLUE);
	ColorPickerFrame.previousValues = {r = QSA_RED, g = QSA_GREEN, b = QSA_BLUE, opacity = (1.0 - QSA_ALPHA)};
	ColorPickerFrame.cancelFunc = QSA_CancelColor;
	ShowUIPanel(ColorPickerFrame);
end

-- this is the values as they are set
function QSA_SetColor()
	QSA_RED, QSA_GREEN, QSA_BLUE = ColorPickerFrame:GetColorRGB();
end

-- this si the opacity as they are set
function QSA_SetOpacity()
	QSA_ALPHA = 1.0 - OpacitySliderFrame:GetValue();
end

-- they changed thier minds
function QSA_CancelColor()
	QSA_RED   = ColorPickerFrame.previousValues.r;
	QSA_GREEN = ColorPickerFrame.previousValues.g;
	QSA_BLUE  = ColorPickerFrame.previousValues.b;
	QSA_ALPHA = 1.0 - ColorPickerFrame.previousValues.opacity;
end

-------------------------------------------------------------------------------
-- Options box... the functions for the options dialog box
-------------------------------------------------------------------------------
function QSA_OptionsOpen()
	if ( not QuuSpellAlertOptions:IsVisible() ) then
		QuuSpellAlertOptionsEnable:SetChecked(getVar("Enabled"));
		QuuSpellAlertOptionsCombat:SetChecked(getVar("CombatOnly"));
		QuuSpellAlertOptionsTarget:SetChecked(getVar("TargetOnly"));
		QuuSpellAlertOptionsShort:SetChecked(getVar("ShortDisplay"));
		QuuSpellAlertOptionsEmote:SetChecked(getVar("EmoteMode"));
		QuuSpellAlertOptionsGain:SetChecked(getVar("GainMode"));
		QuuSpellAlertOptionsFade:SetChecked(getVar("FadeMode"));
		QuuSpellAlertOptionsAffliction:SetChecked(getVar("Afflictions"));

		QuuSpellAlertOptions:Show();
	else
		QSA_OptionsClose();
	end
end

function QSA_OptionsClose()

	setVar( "Enabled", QuuSpellAlertOptionsEnable:GetChecked());
	setVar( "CombatOnly", QuuSpellAlertOptionsCombat:GetChecked());
	setVar( "TargetOnly", QuuSpellAlertOptionsTarget:GetChecked());
	setVar( "ShortDisplay", QuuSpellAlertOptionsShort:GetChecked());
	setVar( "EmoteMode", QuuSpellAlertOptionsEmote:GetChecked());
	setVar( "GainMode", QuuSpellAlertOptionsGain:GetChecked());
	setVar( "FadeMode", QuuSpellAlertOptionsFade:GetChecked());
	setVar( "Afflictions", QuuSpellAlertOptionsAffliction:GetChecked());

	QuuSpellAlertOptions:Hide();
end

-------------------------------------------------------------------------------
-- the init and configuration
-------------------------------------------------------------------------------
function QSA_Init()
	QSA_Status();

	-- Register our slash command
	SLASH_QSA1 = QSA_SLASH_COMMAND;
	SlashCmdList["QSA"] = function(msg)
		QSA_Command(msg);
	end

	if (getVar( "QSA_Hide")) then
		QuuSpellAlertAnchor:Hide();
	end

	QSA_LastTarget = nil;
end

function QSA_Command(msg)
	if( msg ) then
		local command = string.lower(msg);
		if( command == "" ) then
			QSA_println(QSA_SLASH_COMMAND.." ["..QSA_MACRO_STATUS.."|"..QSA_MACRO_TOGGLE.."|...]");
			QSA_println("  "..QSA_MACRO_STATUS.."  "..QSA_MACRO_STATUS_DESC);
			QSA_println("  "..QSA_MACRO_TOGGLE.."  "..QSA_MACRO_TOGGLE_DESC);
			QSA_println("  "..QSA_MACRO_REPORT.."  "..QSA_MACRO_REPORT_DESC);
			QSA_println("  "..QSA_MACRO_TARGET.."  "..QSA_MACRO_TARGET_DESC);
			QSA_println("  "..QSA_MACRO_SHOW.."  "..QSA_MACRO_SHOW_DESC);
			QSA_println("  "..QSA_MACRO_EMOTE.."  "..QSA_MACRO_EMOTE_DESC);
			QSA_println("  "..QSA_MACRO_GAIN.."  "..QSA_MACRO_GAIN_DESC);
			QSA_println("  "..QSA_MACRO_FADE.."  "..QSA_MACRO_FADE_DESC);
			QSA_println("  "..QSA_MACRO_COMBAT.."  "..QSA_MACRO_COMBAT_DESC);
			QSA_println("  "..QSA_MACRO_TRGNLY.."  "..QSA_MACRO_TRGNLY_DESC);
			QSA_println("  "..QSA_MACRO_SHORT.."  "..QSA_MACRO_SHORT_DESC);
			QSA_println("  "..QSA_MACRO_AFFL.."  "..QSA_MACRO_AFFL_DESC);
			QSA_println("  "..QSA_MACRO_TEST.."  "..QSA_MACRO_TEST_DESC);
		elseif (command == QSA_MACRO_STATUS ) then
			QSA_Status();
		elseif (command == QSA_MACRO_TOGGLE ) then
			toggleVar( "Enabled");
			QSA_Status();
		elseif (command == QSA_MACRO_REPORT ) then
			toggleVar( "QSA_Report");
			QSA_Status();
		elseif (command == QSA_MACRO_TARGET ) then
			QSA_TargetLast();
		elseif (command == QSA_MACRO_SHOW ) then
			setVar( "QSA_Hide", false)
			QuuSpellAlertAnchor:Show();
			QuuSpellAlertTimerFrame:Show();
		elseif (command == QSA_MACRO_EMOTE ) then
			toggleVar( "EmoteMode");
			QSA_Status();
		elseif (command == QSA_MACRO_GAIN ) then
			toggleVar( "GainMode");
			QSA_Status();
		elseif (command == QSA_MACRO_FADE ) then
			toggleVar( "FadeMode");
			QSA_Status();
		elseif (command == QSA_MACRO_COMBAT ) then
			toggleVar( "CombatOnly");
			QSA_Status();
		elseif (command == QSA_MACRO_TRGNLY ) then
			toggleVar( "TargetOnly");
			QSA_Status();
		elseif (command == QSA_MACRO_SHORT ) then
			toggleVar( "ShortDisplay");
			QSA_Status();
		elseif (command == QSA_MACRO_AFFL ) then
			toggleVar( "Afflictions");
			QSA_Status();
		elseif (command == QSA_MACRO_TEST ) then
			QSA_Test();
		else
			QSA_println( QSA_MACRO_ERROR);
		end
	end
end

function QSA_Test()
	local enabled_temp = getVar("Enabled");
	local combat_temp = getVar("CombatOnly");
	local target_only = getVar("TargetOnly");

	setVar( "Enabled",true);
	setVar( "CombatOnly",false);
	setVar( "TargetOnly",false);

	-- I am not going to localize the test strings

	
	--Running through the events
	local creature = "Beta Death Shaman";

	local emote = " is dacing to the beat!";
	QSA_Event( "CHAT_MSG_MONSTER_EMOTE", emote, creature);
	
	local casting = "Uber Flame Strike";
	local buffer = string.format(SPELLCASTOTHERSTART, creature, casting);
	QSA_Event( "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE", buffer, nil);
	
	local performing = "Death Coil Onion";
	buffer = string.format(SPELLPERFORMOTHERSTART, creature, performing);
	QSA_Event( "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE", buffer, nil);
	
	local totem = "Uber "..QSA_TOTEM;
	buffer = string.format(SPELLCASTGOOTHER, creature, totem);
	QSA_Event( "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE", buffer, nil);
	

	local aura = "Uberness";
	buffer = string.format(AURAADDEDOTHERHELPFUL, creature, aura);
	QSA_Event( "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE", buffer, nil);

	-- and displaying some information
	QSA_Critical("This is the Critical Alert area");
	QSA_Critical("More important stuff is here");

	setVar( "TargetOnly",target_only);
	setVar( "CombatOnly",combat_temp);
	setVar( "Enabled",enabled_temp);
end

function QSA_TargetLast()
	if (QSA_LastTarget ~= nil) then
		TargetByName(QSA_LastTarget);
	end
end

function QSA_Status()
	--[[
	QSA_println( "Quu Spell Alert "..QSA_VERSION);
	if (getVar("Enabled")) then
		 QSA_println( QSA_STATUS_ENABLED);
		if (QSA_Report) then
			QSA_println( QSA_REPORT_ENABLED);
		end
		if (getVar("EmoteMode")) then
			QSA_println( QSA_EMOTE_ENABLED);
		end
		if (getVar("GainMode")) then
			QSA_println( QSA_GAIN_ENABLED);
		end
		if (getVar("FadeMode")) then
			QSA_println( QSA_FADE_ENABLED);
		end
		if (getVar("CombatOnly")) then
			QSA_println( QSA_COMBAT_ENABLED);
		end
		if (getVar("TargetOnly")) then
			QSA_println( QSA_TRGONLY_ENABLED)
		end
		if (getVar("ShortDisplay")) then
			QSA_println( QSA_SHORT_ENABLED)
		end
		if (getVar("Afflictions")) then
			QSA_println( QSA_AFFL_ENABLED)
		end
	else
		QSA_println( QSA_STATUS_DISBALED);
	end
	]]--
end


-------------------------------------------------------------------------------
-- the display logic
-- this takes the spell and caster... and determains if we alert on it
-------------------------------------------------------------------------------

function QSA_ReplaceTokens( originalString, caster, effect)
	return string.gsub( string.gsub( originalString, "$c", caster), "$e", effect);
end

-- name check function
function QSA_IsInParty( Name)
	if (Name == UnitName("player")) then
		return true;
	elseif (Name == UnitName("pet")) then
		return true;
	elseif (Name == UnitName("party1")) then
		return true;
	elseif (Name == UnitName("party2")) then
		return true;
	elseif (Name == UnitName("party3")) then
		return true;
	elseif (Name == UnitName("party4")) then
		return true;
	elseif (Name == UnitName("partypet1")) then
		return true;
	elseif (Name == UnitName("partypet2")) then
		return true;
	elseif (Name == UnitName("partypet3")) then
		return true;
	elseif (Name == UnitName("partypet4")) then
		return true;
	else
		local numRaidMembers = GetNumRaidMembers();
		if (numRaidMembers > 0) then
			-- we are in a raid
			for i=1, numRaidMembers do
				RaidName = GetRaidRosterInfo(i);
				if ( Name == RaidName) then
					return true;
				end
				if ( Name == UnitName("raidpet"..i)) then
					return true;
				end
			end
		end
	end
	return false;
end

-- this displays the information
function QSA_Display( Creature, Effect, Msg, type, bannner)
	if (not getVar(type)) then
		return;
	end;
	
	-- first... do we ignore it?
	if (QSA_IgnoreSpells[Effect]) then
		return;
	end

	-- then if they are in our party... ignore it
	if (QSA_IsInParty(Creature)) then
		return;
	end

	if (getVar("TargetOnly") and (UnitExists("target"))) then
		if (UnitName("target") ~= Creature) then
			return;
		end
	end

	-- save the name for a targetbyname possability
	QSA_LastTarget = Creature;

	if (QSA_ImportantSpells[Effect]) then
		-- its important... flag it
		if (getVar("ShortDisplay")) then
			QSA_Critical( QSA_ReplaceTokens(bannner, Creature, Effect));
		else
			QSA_Critical( Msg);
		end

	else
		-- its not important... put a normal display
		if (getVar("ShortDisplay")) then
			QSA_Banner( QSA_ReplaceTokens(bannner, Creature, Effect));
		else
			QSA_Banner( Msg);
		end
	end

end

-------------------------------------------------------------------------------
-- event block... this listens and deals with all those pesky events
-------------------------------------------------------------------------------

-- this is all the events that might trip up ones we care about
-- all these doe is remove potential "pitfall" alerts... spam
QSA_PreConsumerStrings = {
	AURAADDEDSELFHELPFUL,
	AURADISPELSELF,
	AURAREMOVEDSELF,
	GENERICPOWERGAIN_OTHER,
	GENERICPOWERGAIN_SELF,
	PERIODICAURAHEALOTHEROTHER,
	PERIODICAURAHEALOTHERSELF,
	PERIODICAURAHEALSELFOTHER,
	PERIODICAURAHEALSELFSELF,
	POWERGAIN_OTHER,
	POWERGAIN_SELF,
	SPELLCASTGOSELF,
	SPELLCASTSELFSTART,
	SPELLEXTRAATTACKSOTHER,
	SPELLEXTRAATTACKSOTHER_SINGULAR,
	SPELLEXTRAATTACKSSELF,
	SPELLEXTRAATTACKSSELF_SINGULAR,
	SPELLPERFORMSELFSTART,
	SPELLPOWERDRAINOTHEROTHER,
	SPELLPOWERDRAINOTHERSELF,
	SPELLPOWERLEECHOTHEROTHER,
	SPELLPOWERLEECHOTHERSELF,
	SPELLPOWERLEECHSELFOTHER,
}

-- this function converts one of the globals into a search string
-- this is important so we don't have to localize them
function QSA_FormatGlobalStrings( globalString)
	local res = globalString;

	-- first reformat the "global" so it can be gsubed
	local i;
	for i = 1, string.len(res) do
		if (string.sub(res, i, i) == "%") then
			if (i == 1) then
				res = "$"..string.sub(res, 2);
			else
				res = string.sub(res, 1, i -1).."$"..string.sub(res, i + 1);
			end
		end
	end
	res = string.gsub(res, "$s", "(.+)");
	res = string.gsub(res, "$d", "(%d)");
	return res;
end

-- this is the "big" function
function QSA_Event(event, arg1, arg2)
	-- are we enabled
	if (not getVar("Enabled")) then
		return;
	end

	-- are we in combat
	if ((not UnitAffectingCombat("player")) and getVar("CombatOnly")) then
		return;
	end

	-- first lets check for emotes
	if (
		(event == "CHAT_MSG_EMOTE") or
		-- (event == "CHAT_MSG_TEXT_EMOTE") or
		(event == "CHAT_MSG_MONSTER_EMOTE")
	) then
		-- got one... process it for display
		QSA_Display( arg2, arg1, arg2.." "..arg1, "EmoteMode", QSA_EMOTE_BANNER);
		return;
	elseif (
		(event == "CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE") or
		(event == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE") or
		(event == "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
	) then
		-- looks like we have somethign effecting the party

		if (getVar("Afflictions")) then
			for effect in string.gfind(arg1,QSA_FormatGlobalStrings(AURAADDEDSELFHARMFUL)) do
				if (getVar("Afflictions")) then
					if (QSA_Alert_Afflictions[effect]) then
						QSA_Critical(arg1);
					end
				end
	
				return;
			end

			-- now we look to see if a team member is afflicted with somethign we care about
			for player, effect in string.gfind(arg1,QSA_FormatGlobalStrings(AURAADDEDOTHERHARMFUL)) do
				--AURAADDEDOTHERHARMFUL = "%s is afflicted by %s."; -- Combat log text for aura events
				if (QSA_Alert_Afflictions[effect]) then
					QSA_Critical(arg1);
				end
				return;
			end
		end
	else

		-- first remove any troublesome strings that might trip up the real ones
		for key, value in QSA_PreConsumerStrings do
			for _  in string.gfind(arg1,QSA_FormatGlobalStrings(value)) do
				if (QSA_Debug) then
					if ( not QSA_DebugArray.preConsumed) then
						QSA_DebugArray.preConsumed = { };
					end
					QSA_DebugArray.preConsumed[arg1] = event;
				end
				return;
			end
		end


		-- now parse the different spells we listen to
		local mob = "<ERROR>";
		local effect = "<ERROR>";


		for mob, effect in string.gfind(arg1,QSA_FormatGlobalStrings(SPELLCASTOTHERSTART)) do
			QSA_Display( mob, effect, arg1, "Enabled", QSA_CASTING_BANNER);
			return;
		end

		for mob, effect in string.gfind(arg1,QSA_FormatGlobalStrings(SPELLPERFORMOTHERSTART)) do
			QSA_Display( mob, effect, arg1, "Enabled", QSA_CASTING_BANNER);
			return;
		end

		for mob, effect in string.gfind(arg1,QSA_FormatGlobalStrings(SPELLCASTGOOTHER)) do
			if (string.find(effect, QSA_TOTEM)) then
				QSA_Display( mob, effect, arg1, "Enabled", QSA_CASTING_BANNER);
			end


			return;
		end

		for effect, mob in string.gfind(arg1,QSA_FormatGlobalStrings(AURAREMOVEDOTHER)) do
			QSA_Display( mob, effect, arg1, "FadeMode", QSA_FADE_BANNER);
			return;
		end

		for mob, effect in string.gfind(arg1,QSA_FormatGlobalStrings(AURADISPELOTHER)) do
			QSA_Display( mob, effect, arg1, "FadeMode", QSA_FADE_BANNER);
			return;
		end

		for mob, effect in string.gfind(arg1,QSA_FormatGlobalStrings(AURAADDEDOTHERHELPFUL)) do
			QSA_Display( mob, effect, arg1, "GainMode", QSA_GAIN_BANNER);
			return;
		end

		-- GAIN2 is for when a mob gets a bad status ailment...
		-- we don't normally care about that... so only do it
		-- if it is an important event.
		for mob, effect in string.gfind(arg1,QSA_FormatGlobalStrings(AURAADDEDOTHERHARMFUL)) do
			if (QSA_ImportantSpells[effect]) then
				QSA_Display( mob, effect, arg1, "GainMode", QSA_GAIN_BANNER);
			end
			return;
		end

	end
end

