--
-- Simple Feign Health - by Brodrick ( aka Kirov )
-- Version 1.6
--
-- Continues to show the player's health and mana values after they've feigned death.
-- Also attempts to show party / raid hunters' health and mana after they FD using
-- last known valid numbers.
--
-- Attempts to keep track of a player's health and mana regen, as well as and damage
-- or heals recieved after using feign death.  Currently does not keep track of any
-- kind of mana draining effects.
--

SFH_ValidUnits = {};
SimpleFeignHealth_Saved = {};

local healthRegen, healthRegenLast, healthRegenTime, healthRegenWaste, manaRegen, manaRegenStart, manaRegenWaste, player;

local function print( msg )
	DEFAULT_CHAT_FRAME:AddMessage( msg );
end


-- hook functions --
--------------------
local oldUnitHealth = UnitHealth;
function UnitHealth( unit )
	local health = oldUnitHealth( unit );
	local name = UnitName(unit);
	if ( health == 0 and SFH_ValidUnits[name] and (SFH_ValidUnits[name][3] or SimpleFeignHealth_CheckFeign(unit)) ) then
		if ( player and name == player ) then
			return min( max( floor( SFH_ValidUnits[name][1] ), 0 ), UnitHealthMax(unit) );
		elseif (  SFH_ValidUnits[name][3] ) then
			return SFH_ValidUnits[name][1];
		end
	end
	return health;
end

local oldUnitMana = UnitMana;
function UnitMana( unit )
	local mana = oldUnitMana( unit );
	local name = UnitName(unit);
	if ( mana == 0 and SFH_ValidUnits[name] and (SFH_ValidUnits[name][3] or SimpleFeignHealth_CheckFeign(unit)) ) then
		if ( player and name == player ) then
			return min( max( floor( SFH_ValidUnits[name][2] ), 0 ), UnitManaMax(unit) );
		elseif (  SFH_ValidUnits[name][3] ) then
			return SFH_ValidUnits[name][2];
		end
	end
	return mana;
end

local oldUnitIsDead = UnitIsDead;
function UnitIsDead( unit )
	local dead = oldUnitIsDead( unit );
	local name = UnitName(unit);
	if ( dead and SFH_ValidUnits[name] and (SFH_ValidUnits[name][3] or SimpleFeignHealth_CheckFeign(unit)) ) then
		return;
	end
	return dead;
end


-- initialization --
--------------------
function SimpleFeignHealth_OnLoad()
	this:RegisterEvent("VARIABLES_LOADED");
end

function SimpleFeignHealth_Init()
	print( "SimpleFeignHealh : loaded - /sfh" );

	SLASH_SFEIGNHEALTH1 = "/sfh";
	SLASH_SFEIGNHEALTH2 = "/simplefeignhealth";
	SLASH_SFEIGNHEALTH3 = "/feignhealth";
	SlashCmdList["SFEIGNHEALTH"] = SimpleFeignHealth_Console;
	
	if ( not SimpleFeignHealth_Saved["disable"] ) then
		SimpleFeignHealth_Enable();
	end
end

function SimpleFeignHealth_Enable()
	local _,class = UnitClass("player");
	if ( class == "HUNTER" ) then
		this:RegisterEvent("MIRROR_TIMER_START");
		this:RegisterEvent("MIRROR_TIMER_STOP");
		player = UnitName("player");
		SFH_ValidUnits[player] = {};
	end
	SimpleFeignHealth_CheckForHunters();
	this:RegisterEvent("UNIT_HEALTH");
	this:RegisterEvent("UNIT_MANA");
	this:RegisterEvent("UNIT_AURA");
	this:RegisterEvent("UNIT_MAXHEALTH");
	this:RegisterEvent("UNIT_MAXMANA");
	this:RegisterEvent("PARTY_MEMBERS_CHANGED");
end

function SimpleFeignHealth_Disable()
	SFH_ValidUnits = {};
	this:UnregisterEvent("MIRROR_TIMER_START");
	this:UnregisterEvent("MIRROR_TIMER_STOP");
	this:UnregisterEvent("UNIT_HEALTH");
	this:UnregisterEvent("UNIT_MANA");
	this:UnregisterEvent("UNIT_AURA");
	this:UnregisterEvent("PARTY_MEMBERS_CHANGED");
end


-- event catching --
--------------------
function SimpleFeignHealth_OnEvent()
	if ( event == "VARIABLES_LOADED" ) then
		SimpleFeignHealth_Init();
	elseif ( event == "MIRROR_TIMER_START" ) then
		-- client event (happens before buff) health and mana still valid
		if ( arg1 == "FEIGNDEATH" ) then
			SFH_ValidUnits[player][1] = UnitHealth("player")
			SFH_ValidUnits[player][2] = UnitMana("player")
			this:RegisterEvent("UNIT_COMBAT");
			this:RegisterEvent("PLAYER_REGEN_DISABLED");
			this:RegisterEvent("PLAYER_REGEN_ENABLED");
			manaRegenStart = GetTime();
			if ( not PlayerFrame.onHateList ) then
				healthRegenLast = manaRegenStart;
			end
			healthRegenTime = 0;
			healthRegen = 0;
			manaRegenTime = 0;
			manaRegen = 0;
		end
	elseif ( event == "MIRROR_TIMER_STOP" ) then
		if ( arg1 == "FEIGNDEATH" ) then
			this:UnregisterEvent("UNIT_COMBAT");
			this:UnregisterEvent("PLAYER_REGEN_DISABLED");
			this:UnregisterEvent("PLAYER_REGEN_ENABLED");
		end
	elseif ( event == "PLAYER_REGEN_ENABLED" ) then
		-- re-enable player's health regen
		healthRegenLast = GetTime();
	elseif ( event == "PLAYER_REGEN_DISABLED" ) then
		-- disable player's health regen and calculate tota regen time
		healthRegenTime = healthRegenTime + GetTime() - healthRegenLast;
		healthRegenLast = nil;
	elseif ( event == "PARTY_MEMBERS_CHANGED" ) then
		-- Find hunters in party / raid
		SimpleFeignHealth_CheckForHunters();
	else
		local name = UnitName(arg1);
		local validUnit = SFH_ValidUnits[name];
		if ( event == "UNIT_HEALTH" ) then
			if ( validUnit ) then
				local health = oldUnitHealth( arg1 );
				if ( health > 0 ) then
					-- Keep track of all valid units' health before FD
					validUnit[1] = health;
				elseif ( arg1 == "player" and validUnit[3] ) then
					-- do regen if it's the player and he's FDed;
					validUnit[1] = SimpleFeignHealth_HealthRegen();
				end
			end
		elseif ( event == "UNIT_MANA" ) then
			local validUnit = SFH_ValidUnits[name];
			if ( validUnit ) then
				local mana = oldUnitMana( arg1 );
				if ( mana > 0 ) then
					-- Keep track of all valid units' mana before FD
					validUnit[2] = mana;
				elseif ( arg1 == "player" and SFH_ValidUnits[name][3] ) then
					-- do regen if it's the player and he's FDed;
					validUnit[2] = SimpleFeignHealth_ManaRegen();
				end
			end
		elseif ( event == "UNIT_COMBAT" ) then
			if ( not SFH_ValidUnits[player][3] ) then return; end
			if ( arg1 == "player" and arg4 ) then
				if ( arg2 == "HEAL" ) then
					SFH_ValidUnits[player][1] = SFH_ValidUnits[player][1] + arg4;
				else
					SFH_ValidUnits[player][1] = SFH_ValidUnits[player][1] - arg4;
				end
			end
		elseif ( event == "UNIT_AURA" ) then
			if ( SFH_ValidUnits[name] ) then
				SimpleFeignHealth_CheckFeign(arg1);
			end
		end
	end
end


-- add hunters to valid list --
-------------------------------
function SimpleFeignHealth_CheckForHunters()
	if ( SimpleFeignHealth_Saved["noparty"] ) then return; end
	local raidNum = GetNumRaidMembers();
	local partyNum = GetNumPartyMembers();
	if ( raidNum > 0 ) then
		for i=1, raidNum do
			local unit = "raid"..i;
			local _,class = UnitClass(unit);
			if ( class == "HUNTER" ) then
				local name = UnitName( unit );
				if ( not SFH_ValidUnits[name] ) then
					SFH_ValidUnits[name] = {};
					SFH_ValidUnits[name][1] = UnitHealth(unit);
					SFH_ValidUnits[name][2] = UnitMana(unit);
				end
			end
		end
	elseif ( partyNum > 0 ) then
		for i=1, partyNum do
			local unit = "party"..i;
			local _,class = UnitClass(unit);
			if ( class == "HUNTER" ) then
				local name = UnitName(unit);
				if ( not SFH_ValidUnits[name] ) then
					SFH_ValidUnits[name] = {};
					SFH_ValidUnits[name][1] = UnitHealth(unit);
					SFH_ValidUnits[name][2] = UnitMana(unit);
				end
			end
		end
	else -- cull all but player from list
		for k, v in SFH_ValidUnits do
			if ( k ~= player ) then
				SFH_ValidUnits[k] = nil;
			end
		end
	end
end


-- check for fd --
------------------
function SimpleFeignHealth_CheckFeign( unit )
	local i, texture = 1, oldtexture;
	if(unit == "player") then
		unitFrame = PlayerPortrait;
	elseif (unit == "party1") then
		unitFrame = PartyMemberFrame1Portrait;
	elseif (unit == "party2") then
		unitFrame = PartyMemberFrame2Portrait;
	elseif (unit == "party3") then
		unitFrame = PartyMemberFrame3Portrait;
	elseif (unit == "party4") then
		unitFrame = PartyMemberFrame4Portrait;
	elseif (unit == "target") then
		unitFrame = TargetPortrait;
	else
		unitFrame = nil;
	end
	repeat
		texture = UnitBuff( unit, i );
		if ( texture == "Interface\\Icons\\Ability_Rogue_FeignDeath" ) then
			SFH_ValidUnits[UnitName(unit)][3] = 1
			if (unitFrame ~= nil) then
			unitFrame:SetTexture("Interface\\Icons\\Ability_Rogue_FeignDeath");
			end
			return 1;
		end
		i = i + 1;
	until not texture;
	if (unitFrame ~= nil) then
	SetPortraitTexture(unitFrame, unit);
	end
	SFH_ValidUnits[UnitName(unit)][3] = nil;
end


-- console commands --
----------------------
function SimpleFeignHealth_Console( msg )
	if ( string.sub( strlower( msg ), 1, 5 ) == "party" ) then
		if ( SimpleFeignHealth_Saved["noparty"] ) then
			SimpleFeignHealth_Saved["noparty"] = nil;
			SimpleFeignHealth_CheckForHunters();
			print( "SimpleFeignHealh : enabled for party hunters" );
		else
			SimpleFeignHealth_Saved["noparty"] = 1;
			for k, v in SFH_ValidUnits do
				if ( k ~= player ) then
					SFH_ValidUnits[k] = nil;
				end
			end
			print( "SimpleFeignHealh : disabled for party hunters" );
		end
	elseif ( string.sub( strlower( msg ), 1, 2 ) == "on" ) then
		SimpleFeignHealth_Enable();
		SimpleFeignHealth_Saved["disable"] = nil;
		print( "SimpleFeignHealh : enabled" );
	elseif ( string.sub( strlower( msg ), 1, 3 ) == "off" ) then
		SimpleFeignHealth_Disable();
		SimpleFeignHealth_Saved["disable"] = 1;
		print( "SimpleFeignHealh : disabled" );
	elseif ( string.sub( strlower( msg ), 1, 6 ) == "toggle" ) then
		if ( SimpleFeignHealth_Saved["disable"] ) then
			SimpleFeignHealth_Saved["disable"] = nil;
			SimpleFeignHealth_Enable();
			print( "SimpleFeignHealh : enabled" );
		else
			SimpleFeignHealth_Saved["disable"] = 1;
			SimpleFeignHealth_Disable();
			print( "SimpleFeignHealh : disabled" );
		end
	elseif ( string.sub( strlower( msg ), 1, 4 ) == "dump" ) then
		print( healthRegen )
		print( GetTime()-healthRegenLast )
		print( healthRegenTime )
		print( manaRegen )
		print( GetTime()-manaRegenStart )
	else
		if ( SimpleFeignHealth_Saved["disable"] ) then
			print( "SimpleFeignHealh : /sfh : currently disabled" );
		else
			print( "SimpleFeignHealh : /sfh : currently enabled" );
		end
		print( "on / off / toggle - enable and disable this mod" );
		print( "party - toggle showing heath / mana values for partied hunters" );
		if ( SimpleFeignHealth_Saved["noparty"] ) then
			print( "party hunters disabled" );
		else
			print( "party hunters enabled" );
		end
	end
end


-- player's regen --
--------------------
function SimpleFeignHealth_HealthRegen()
	if ( healthRegenTime ) then
		if ( healthRegenLast ) then
			healthRegenTime = healthRegenTime + GetTime() - healthRegenLast;
			healthRegenLast = GetTime();
		end
		local validUnit = SFH_ValidUnits[player];
		local base, spirit = UnitStat("player", 5);
		local time = (healthRegenTime - mod(healthRegenTime, 2)) / 2;
		local regen = floor(spirit * time * 0.23);
		local health = validUnit[1] + regen - healthRegen;
		--ChatFrame5:AddMessage( "old hp = "..validUnit[1].." : new regen = "..regen-healthRegen.." : ("..regen.."-"..healthRegen..") : new hp = "..health);
		healthRegen = regen;
		return max( min( health, UnitHealthMax("player") ), 0);
	end
end

function SimpleFeignHealth_ManaRegen()
	if ( manaRegenStart ) then
		local validUnit = SFH_ValidUnits[player];
		local base, spirit = UnitStat("player", 5);
		local time = GetTime() - manaRegenStart - 4;
		time = (time - mod(time, 2)) / 2;
		local regen = floor((spirit * time * 0.2) + ( time * 15 ));
		local mana = validUnit[2] + max(regen - manaRegen, 0);
		--ChatFrame5:AddMessage( "old mp = "..validUnit[2].." : new regen = "..regen-manaRegen.." : ("..regen.."-"..manaRegen..") : new mp = "..mana);
		manaRegen = regen;
		return max( min( mana, UnitManaMax("player") ), 0);
	end
end
