-------------------------------------------------------------------------------
-- Constants
-------------------------------------------------------------------------------

NUM_SPELL_SLOTS = 10;
VERSION = "3.0.0.12"

-------------------------------------------------------------------------------
-- Variables
-------------------------------------------------------------------------------

SHOW_WELCOME = true;
FLOTOTEMBAR_OPTIONS_DEFAULT = { scale = 1, borders = true, barLayout = "1row", barSettings = {} };
FLOTOTEMBAR_OPTIONS = FLOTOTEMBAR_OPTIONS_DEFAULT;
FLOTOTEMBAR_BARSETTINGS_DEFAULT = {
	["EARTH"] = { buttonsOrder = {}, position = "auto", color = { 0, 0.49, 0, 0.7 }, hiddenSpells = {} },
	["FIRE"] = { buttonsOrder = {}, position = "auto", color = { 0.49, 0, 0, 0.7 }, hiddenSpells = {} },
	["WATER"] = { buttonsOrder = {}, position = "auto", color = { 0, 0.49, 0.49, 0.7 }, hiddenSpells = {} },
	["AIR"] = { buttonsOrder = {}, position = "auto", color = { 0, 0, 0.99, 0.7 }, hiddenSpells = {} },
};
FLO_CLASS_NAME = nil;

-------------------------------------------------------------------------------
-- Functions
-------------------------------------------------------------------------------

-- Executed on load, calls general set-up functions
function FloTotemBar_OnLoad(self)

	-- Re-anchor the first button, link it to the timer
	local thisName = self:GetName();
	local button = getglobal(thisName.."Button1");

	button:SetPoint("LEFT", thisName.."Countdown", "RIGHT", 5, 0);

	-- Class-based setup, abort if not supported
	_, FLO_CLASS_NAME = UnitClass("player");
	FLO_CLASS_NAME = strupper(FLO_CLASS_NAME);

	local classSpells = FLO_TOTEM_SPELLS[FLO_CLASS_NAME];

	if classSpells == nil then
		return;
	end

	self.totemtype = string.sub(thisName, 7);

	-- Store the spell list for later
	self.availableSpells = classSpells[self.totemtype];
	if self.availableSpells == nil then
		return;
	end

	-- Init the settings variable
	FLOTOTEMBAR_OPTIONS.barSettings[self.totemtype] = FLOTOTEMBAR_BARSETTINGS_DEFAULT[self.totemtype];

	self.spells = {};
	self.SetupSpell = FloTotemBar_SetupSpell;
	self.OnSetup = FloTotemBar_OnSetup;
	self.menuHooks = { SetPosition = FloTotemBar_SetPosition, SetBorders = FloTotemBar_SetBorders };
	if FLO_CLASS_NAME == "SHAMAN" then
		self.slot = getglobal(self.totemtype.."_TOTEM_SLOT");
		self.menuHooks.SetLayoutMenu = FloTotemBar_SetLayoutMenu;
	end
	self:EnableMouse(1);

	if SHOW_WELCOME then
		DEFAULT_CHAT_FRAME:AddMessage( "FloTotemBar "..VERSION.." loaded." );
		SHOW_WELCOME = nil;

		SLASH_FLOTOTEMBAR1 = "/flototembar";
		SLASH_FLOTOTEMBAR2 = "/ftb";
		SlashCmdList["FLOTOTEMBAR"] = FloTotemBar_ReadCmd;

		self:RegisterEvent("VARIABLES_LOADED");
	end
	self:RegisterEvent("PLAYER_ENTERING_WORLD");
	self:RegisterEvent("LEARNED_SPELL_IN_TAB");
	self:RegisterEvent("CHARACTER_POINTS_CHANGED");
	self:RegisterEvent("PLAYER_ALIVE");
	self:RegisterEvent("PLAYER_LEVEL_UP");
	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED");
	self:RegisterEvent("SPELL_UPDATE_COOLDOWN");
	self:RegisterEvent("SPELL_UPDATE_USABLE");
	self:RegisterEvent("PLAYER_DEAD");
	self:RegisterEvent("UPDATE_BINDINGS");

	-- Destruction detection
	if ( FLO_CLASS_NAME == "HUNTER" ) then
		self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED");
	else
		self:RegisterEvent("PLAYER_TOTEM_UPDATE");
	end
end

function FloTotemBar_OnEvent(self, event, arg1, ...)

	if event == "PLAYER_ENTERING_WORLD" or event == "LEARNED_SPELL_IN_TAB" or event == "PLAYER_ALIVE" or event == "PLAYER_LEVEL_UP" or event == "CHARACTER_POINTS_CHANGED" then
		FloLib_Setup(self);
	elseif event == "UNIT_SPELLCAST_SUCCEEDED"  then
		if arg1 == "player" then
			FloTotemBar_StartTimer(self, ...);
		end
	elseif event == "SPELL_UPDATE_COOLDOWN" or event == "SPELL_UPDATE_USABLE" then
		FloLib_UpdateState(self);
	elseif event == "PLAYER_DEAD" then
		FloTotemBar_ResetTimer(self);
	elseif event == "VARIABLES_LOADED" then

		-- Copy new variables
		FloLib_CopyPreserve(FLOTOTEMBAR_OPTIONS_DEFAULT, FLOTOTEMBAR_OPTIONS);

		-- Import old variables
		if FLOTOTEMBAR_LAYOUT then
			for k, v in pairs(FLOTOTEMBAR_OPTIONS.barSettings) do
				v.position = FLOTOTEMBAR_LAYOUT;
			end
		elseif FLOTOTEMBAR_OPTIONS.layout then
			for k, v in pairs(FLOTOTEMBAR_OPTIONS.buttonsOrder) do
				if k ~= "TRAP" then
					FLOTOTEMBAR_OPTIONS.barSettings[k] = FLOTOTEMBAR_BARSETTINGS_DEFAULT[k];
					FLOTOTEMBAR_OPTIONS.barSettings[k].position = FLOTOTEMBAR_OPTIONS.layout;
				end
			end
			FLOTOTEMBAR_OPTIONS.layout = nil;
		end
		if FLOTOTEMBAR_SCALE then
			FLOTOTEMBAR_OPTIONS.scale = FLOTOTEMBAR_SCALE;
		end
		if FLOTOTEMBAR_BUTTONS_ORDER then
			for k, v in pairs(FLOTOTEMBAR_BUTTONS_ORDER) do
				if k ~= "TRAP" then
					FLOTOTEMBAR_OPTIONS.barSettings[k].buttonsOrder = v;
				end
			end
		elseif FLOTOTEMBAR_OPTIONS.buttonsOrder then
			for k, v in pairs(FLOTOTEMBAR_OPTIONS.buttonsOrder) do
				if k ~= "TRAP" then
					FLOTOTEMBAR_OPTIONS.barSettings[k].buttonsOrder = v;
				end
			end
			FLOTOTEMBAR_OPTIONS.buttonsOrder = nil;
		end

		for k, v in pairs(FLOTOTEMBAR_OPTIONS.barSettings) do

			FloLib_CopyPreserve(FLOTOTEMBAR_BARSETTINGS_DEFAULT[k], v);
			local bar = getglobal("FloBar"..k);
			bar.globalSettings = FLOTOTEMBAR_OPTIONS;
			bar.settings = v;
			FloTotemBar_SetPosition(nil, bar, v.position);
		end
		FloTotemBar_SetScale(FLOTOTEMBAR_OPTIONS.scale);
		FloTotemBar_SetBorders(nil, FLOTOTEMBAR_OPTIONS.borders);

		-- Hook the UIParent_ManageFramePositions function
		hooksecurefunc("UIParent_ManageFramePositions", FloTotemBar_UpdatePositions);

	elseif event == "UPDATE_BINDINGS" then
		FloLib_UpdateBindings(self, "FLOTOTEM"..self.totemtype);
	else
		-- Events used for totem destruction detection
		if self.activeSpell then
			self.spells[self.activeSpell].algo(self, arg1, ...);
		end
	end
end

function FloTotemBar_ReadCmd(line)

	local cmd, var = strsplit(' ', line or "");

	if cmd == "scale" and tonumber(var) then
		FloTotemBar_SetScale(var);
	elseif cmd == "lock" or cmd == "unlock" or cmd == "auto" then
		for i, v in ipairs({FloBarEARTH, FloBarFIRE, FloBarWATER, FloBarAIR}) do
			FloTotemBar_SetPosition(nil, v, cmd);
		end
	elseif cmd == "borders" then
		FloTotemBar_SetBorders(nil, true);
	elseif cmd == "noborders" then
		FloTotemBar_SetBorders(nil, false);
	elseif cmd == "panic" or cmd == "reset" then
		FloLib_ResetAddon("FloTotemBar");
	else
		DEFAULT_CHAT_FRAME:AddMessage( "FloTotemBar usage :" );
		DEFAULT_CHAT_FRAME:AddMessage( "/ftb lock|unlock : lock/unlock position" );
		DEFAULT_CHAT_FRAME:AddMessage( "/ftb borders|noborders : show/hide borders" );
		DEFAULT_CHAT_FRAME:AddMessage( "/ftb auto : Automatic positioning" );
		DEFAULT_CHAT_FRAME:AddMessage( "/ftb scale <num> : Set scale" );
		DEFAULT_CHAT_FRAME:AddMessage( "/ftb panic|reset : Reset FloTotemBar" );
		return;
	end
end

function FloTotemBar_UpdateTotem(self, slot)

	if self.slot == slot then

		local duration = GetTotemTimeLeft(slot);
		if duration == 0 then
			FloTotemBar_ResetTimer(self);
		end
	end
end

function FloTotemBar_CheckTrapLife(self, timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, spellId, spellName, spellSchool, ...)
	-- [environmentalDamageType]
	-- [spellName, spellRank, spellSchool]
	-- [damage, school, [resisted, blocked, absorbed, crit, glancing, crushing]]

	local name = string.upper(self.spells[self.activeSpell].name);

	if strsub(event, 1, 5) == "SPELL" and event ~= "SPELL_CAST_SUCCESS" and event ~= "SPELL_CREATE" and string.find(string.upper(spellName), name, 1, true) then
		if CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_MINE) then
			FloTotemBar_ResetTimer(self);
		else
			FloTotemBar_TimerRed(self);
		end
	end
end

function FloTotemBar_CheckTrap2Life(self, timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...)
	-- [environmentalDamageType]
	-- [spellName, spellRank, spellSchool]
	-- [damage, school, [resisted, blocked, absorbed, crit, glancing, crushing]]

	local name = string.upper(self.spells[self.activeSpell].name);
	local COMBATLOG_FILTER_MY_GUARDIAN = bit.bor(
		COMBATLOG_OBJECT_AFFILIATION_MINE,
		COMBATLOG_OBJECT_REACTION_FRIENDLY,
		COMBATLOG_OBJECT_CONTROL_PLAYER,
		COMBATLOG_OBJECT_TYPE_GUARDIAN
		);
 

	if strsub(event, 1, 5) == "SWING" and CombatLog_Object_IsA(sourceFlags, COMBATLOG_FILTER_MY_GUARDIAN) then
		FloTotemBar_ResetTimer(self);
	end
end

function FloTotemBar_SetupSpell(self, spell, spellID, pos)

	local duration = 60;
	local algo;

	-- Avoid tainting
	if not InCombatLockdown() then
		local button, icon, texture;
		button = getglobal(self:GetName().."Button"..pos);
		icon = getglobal(self:GetName().."Button"..pos.."Icon");

		button:SetAttribute("type1", "spell");
		button:SetAttribute("spell", spell.name);

		texture = GetSpellTexture(spellID, BOOKTYPE_SPELL);
		icon:SetTexture(texture);
	end

	if FLO_CLASS_NAME ~= "HUNTER" then
		duration = nil;
		algo = FloTotemBar_UpdateTotem;
	elseif spell.x then
		algo = FloTotemBar_CheckTrap2Life;
	else
		algo = FloTotemBar_CheckTrapLife;
	end

	self.spells[pos] = { name = spell.name, id = spellID, duration = duration, algo = algo };

end

function FloTotemBar_OnSetup(self)

	FloTotemBar_ResetTimer(self);
end

function FloTotemBar_UpdatePosition(self)

	-- Avoid tainting when in combat
	if InCombatLockdown() then
		return;
	end

	-- non auto positionning
	if not self.settings or self.settings.position ~= "auto" then
		return;
	end

	local layout = FLO_TOTEM_LAYOUTS[FLOTOTEMBAR_OPTIONS.barLayout];

	self:ClearAllPoints();
	if self == FloBarEARTH then
		local yOffset = -3;
		local yOffset1 = 0;
		local yOffset2 = 0;
		local anchorFrame;

		if not MainMenuBar:IsShown() then
			anchorFrame = UIParent;
			yOffset = 110-UIParent:GetHeight();
		else
			anchorFrame = MainMenuBar;
			if ReputationWatchBar:IsShown() and MainMenuExpBar:IsShown() then
				yOffset = yOffset + 9;
			end

			if MainMenuBarMaxLevelBar:IsShown() then
				yOffset = yOffset - 5;
			end

			if SHOW_MULTI_ACTIONBAR_2 then
				yOffset2 = yOffset2 + 45;
			end

			if SHOW_MULTI_ACTIONBAR_1 then
				yOffset1 = yOffset1 + 45;
			end
		end

		if FLO_CLASS_NAME == "HUNTER" then
			if FloAspectBar then
				self:SetPoint("LEFT", FloAspectBar, "RIGHT", 12, 0);
			else
				self:SetPoint("BOTTOMLEFT", anchorFrame, "TOPLEFT", 512/FLOTOTEMBAR_OPTIONS.scale, (yOffset + yOffset2)/FLOTOTEMBAR_OPTIONS.scale);
			end
		else
			local finalOffset = layout.offset * self:GetHeight();
			self:SetPoint("BOTTOMLEFT", anchorFrame, "TOPLEFT", 0, (yOffset + yOffset1)/FLOTOTEMBAR_OPTIONS.scale + finalOffset);
		end

	elseif FLO_CLASS_NAME == "SHAMAN" then

		self:SetPoint(unpack(layout[self:GetName()]));
	end
end

function FloTotemBar_UpdatePositions()

	-- Avoid tainting when in combat
	if InCombatLockdown() then
		return;
	end

	for k, v in pairs(FLOTOTEMBAR_OPTIONS.barSettings) do
		if v.position == "auto" then
			FloTotemBar_UpdatePosition(getglobal("FloBar"..k))
		end
	end
end

function FloTotemBar_SetBarDrag(frame, enable)

	local countdown = getglobal(frame:GetName().."Countdown");
	if enable then
		FloLib_ShowBorders(frame);
		frame:RegisterForDrag("LeftButton");
		countdown:RegisterForDrag("LeftButton");
	else
		if FLOTOTEMBAR_OPTIONS.borders then
			FloLib_ShowBorders(frame);
		else
			FloLib_HideBorders(frame);
		end
	end
end

function FloTotemBar_SetBorders(self, visible)

	FLOTOTEMBAR_OPTIONS.borders = visible;
	for k, v in pairs(FLOTOTEMBAR_OPTIONS.barSettings) do
		local bar = getglobal("FloBar"..k);
		if visible or v.position == "unlock" then
			FloLib_ShowBorders(bar);
		else
			FloLib_HideBorders(bar);
		end
	end

end

function FloTotemBar_SetPosition(self, bar, mode)

	local unlocked = (mode == "unlock");

	-- Close all dropdowns
	CloseDropDownMenus();

	if bar.settings then
		bar.settings.position = mode;
		DEFAULT_CHAT_FRAME:AddMessage(bar:GetName().." position "..mode);

		FloTotemBar_SetBarDrag(bar, unlocked);

		if mode == "auto" then
			-- Force the auto positionning
			FloTotemBar_UpdatePosition(bar);
		end
	end
end

function FloTotemBar_SetLayoutMenu()

	-- Add the possible values to the menu
	for i = 1, #FLO_TOTEM_LAYOUTS_ORDER do
		local value = FLO_TOTEM_LAYOUTS_ORDER[i];
		local info = UIDropDownMenu_CreateInfo();
		info.text = FLO_TOTEM_LAYOUTS[value].label;
		info.value = value;
		info.func = FloTotemBar_SetLayout;
		info.arg1 = value;

		if value == FLOTOTEMBAR_OPTIONS.barLayout then
			info.checked = 1;
		end
		UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
	end

end

function FloTotemBar_SetLayout(self, layout)

	-- Close all dropdowns
	CloseDropDownMenus();

	FLOTOTEMBAR_OPTIONS.barLayout = layout;
	FloTotemBar_UpdatePositions();
end

function FloTotemBar_SetScale(scale)

	scale = tonumber(scale);
	if not scale or scale <= 0 then
		DEFAULT_CHAT_FRAME:AddMessage( "FloTotemBar : scale must be >0 ("..scale..")" );
		return;
	end

	FLOTOTEMBAR_OPTIONS.scale= scale;

	for i, v in ipairs({FloBarEARTH, FloBarFIRE, FloBarWATER, FloBarAIR}) do
		local p, a, rp, ox, oy = v:GetPoint();
		local os = v:GetScale();
		v:SetScale(scale);
		if a == nil or a == UIParent or a == MainMenuBar then
			v:SetPoint(p, a, rp, ox*os/scale, oy*os/scale);
		end
	end
	FloTotemBar_UpdatePositions();

end

function FloTotemBar_ResetTimer(self)

	self.startTime = 0;
	FloTotemBar_OnUpdate(self);
end

function FloTotemBar_TimerRed(self)

	local countdown = getglobal(self:GetName().."Countdown");
	countdown:SetStatusBarColor(1.0, 0.0, 0.0);

end

function FloTotemBar_StartTimer(self, spellName, rank)

	local founded = false;
	local haveTotem, name, startTime, duration, icon;
	local countdown;

	-- Special case for Totemic Call
	if spellName == FLO_TOTEMIC_CALL_SPELL then
		FloTotemBar_ResetTimer(self);
		return;
	end

	-- Find spell
	for i = 1, #self.spells do
		if string.lower(self.spells[i].name) == string.lower(spellName) then
			founded = i;

			if FLO_CLASS_NAME == "SHAMAN" then
				haveTotem, name, startTime, duration, icon = GetTotemInfo(self.slot);
			else
				duration = self.spells[i].duration;
				startTime = GetTime();
			end
			break;
		end
	end

	if founded then

		self.activeSpell = founded;
		self.startTime = startTime;

		countdown = getglobal(self:GetName().."Countdown");
		countdown:SetMinMaxValues(0, duration);
		countdown:SetStatusBarColor(1.0, 0.7, 0.0);
		FloTotemBar_OnUpdate(self);

	end

end

function FloTotemBar_OnUpdate(self)

	local isActive;
	local button;
	local countdown;
	local timeleft;
	local duration;

	for i=1, #self.spells do

		button = getglobal(self:GetName().."Button"..i);

		spell = self.spells[i];

		if self.activeSpell == i then

			countdown = getglobal(self:GetName().."Countdown");
			_, duration = countdown:GetMinMaxValues();

			timeleft = self.startTime + duration - GetTime();
			isActive = timeleft > 0;

			if (isActive) then
				countdown:SetValue(timeleft);
			else
				self.activeSpell = nil;
				countdown:SetValue(0);
			end;
		else
			isActive = false;
		end

		if isActive then
			button:SetChecked(1);
		else
			button:SetChecked(0);
		end
	end
end

