--[[

	Name:		MobHealth2
	Version:	2.6
	Author:		Wyv (wyv@wp.pl)
	Description:	Displays health value for mobs.
			Original version by Telo.
	URL:		http://www.curse-gaming.com/mod.php?addid=1087
			http://ui.worldofwar.net/ui.php?id=681
			http://www.wowinterface.com/downloads/fileinfo.php?id=3938
			
]]


-- Current target data
local lTargetData;

-- table of event handlers
local lEventHandler = {};

local COLOR_BLUE	= "|c009090FF";
local COLOR_WHITE	= "|c00FFFFFF";
local TITLE		= "|c003060FFMobHealth2"..COLOR_WHITE;

--------------------------------------------------------------------------------------------------
-- external functions for macros / scripts
--   - return value:
--     * (current hp or max hp) if a mob is selected and it's health is known
--     * 0 otherwise
--------------------------------------------------------------------------------------------------

function MobHealth_GetTargetCurHP()
	if ( lTargetData ) then
		return math.floor(lTargetData.currentHealthPercent * lTargetData.PPP + 0.5);
	end	
end

function MobHealth_GetTargetMaxHP()
	if ( lTargetData ) then
		return math.floor(100 * lTargetData.PPP + 0.5);
	end		
end

--------------------------------------------------------------------------------------------------
-- external functions for macros / scripts
--   - return value:
--     curHP, maxHP = MobHealth_GetUnitHP(unit)
--     curHP and maxHP equal 0 if unknown
--     (for unit = "target" or unit = "mouseover" etc.)
--     (please use MobHealth_GetTargetCurHP / MaxHP if you are always intrested in unit="target"
--     (they are faster)
--------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------
-- Internal functions
--   - all MobHealthDB access functions are left intact for compatiblity
--   - MobHealth is no longer local (but it's not a recomended way to access mob health - even tho
--     it's better than using MobHealthDB directly)
--------------------------------------------------------------------------------------------------

function MobHealth_PPP(index)
	if ( index and MobHealthDB[index] ) then
		local s, e;
		local pts;
		local pct;
		
		s, e, pts, pct = string.find(MobHealthDB[index], "^(%d+)/(%d+)$");
		if ( pts and pct ) then
			pts = pts + 0;
			pct = pct + 0;
			if ( pct ~= 0 ) then
				return pts / pct;
			end
		end
	end
	return 0;
end

local function MobHealth_Pts(index)
	if ( index and MobHealthDB[index] ) then
		local s, e;
		local val;
		
		s, e, val = string.find(MobHealthDB[index], "^(%d+)/");
		
		if ( val ) then
			return val + 0;
		end
	end
	return 0;
end

local function MobHealth_Pct(index)
	if ( index and MobHealthDB[index] ) then
		local s, e;
		local val;
		
		s, e, val = string.find(MobHealthDB[index], "/(%d+)$");
		
		if ( val ) then
			return val + 0;
		end
	end
	return 0;
end

local function MobHealth_Set(index, pts, pct)
	if ( index ) then
		if ( pts ) then
			pts = pts + 0;
		else
			pts = 0;
		end
		if ( pct ) then
			pct = pct + 0;
		else
			pct = 0;
		end
		if ( pts == 0 and pct == 0 ) then
			MobHealthDB[index] = nil;
		else
			MobHealthDB[index] = pts.."/"..pct;
		end
	end
end



local function MobHealthPlayer_Pts(index)
	if ( index and MobHealthPlayerDB[index] ) then
		local s, e;
		local val;
		
		s, e, val = string.find(MobHealthPlayerDB[index], "^(%d+)/");
		
		if ( val ) then
			return val + 0;
		end
	end
	return 0;
end

local function MobHealthPlayer_Pct(index)
	if ( index and MobHealthPlayerDB[index] ) then
		local s, e;
		local val;
		
		s, e, val = string.find(MobHealthPlayerDB[index], "/(%d+)$");
		
		if ( val ) then
			return val + 0;
		end
	end
	return 0;
end

local function MobHealthPlayer_Set(index, pts, pct)
	if ( index ) then
		if ( pts ) then
			pts = pts + 0;
		else
			pts = 0;
		end
		if ( pct ) then
			pct = pct + 0;
		else
			pct = 0;
		end
		if ( pts == 0 and pct == 0 ) then
			MobHealthPlayerDB[index] = nil;
		else
			MobHealthPlayerDB[index] = pts.."/"..pct;
		end
	end
end

local function MobHealth_Display(currentPct, index)
	local field = getglobal("MobHealthText");
	local text = "";
	
	if ( field ) then
		if ( lTargetData and lTargetData.PPP > 0 ) then
			local maxhp;
			if (not MobHealthConfig["unstablemax"] and lTargetData.maxHP) then
				maxhp = lTargetData.maxHP;
			else
				maxhp = (100 * lTargetData.PPP) + 0.5;
			end
			if ( currentPct ) then
				text = string.format("%d / %d", (currentPct * lTargetData.PPP) + 0.5, maxhp);
			else
				text = string.format("??? / %d", maxhp);
			end

		end
		field:SetText(text);
	end
end

local function MobHealth_SetPos(pos)
	MobHealthFrame:SetPoint("TOP", "TargetFrameHealthBar", "BOTTOM", -2, tonumber(pos) );
	DEFAULT_CHAT_FRAME:AddMessage(TITLE.." position set to "..pos);
end

local function MobHealth_VariablesLoaded()
	if (not MobHealthDB ) then
		MobHealthDB = { };
	else
		local index, value;
		
		for index, value in MobHealthDB do
			-- Convert the old-style data into the newer, more compact form
			if( type(value) == "table" ) then
				MobHealth_Set(index, value.damPts, value.damPct);
			end
		end
	end
	
	if (not MobHealthConfig ) then
		MobHealthConfig = {};
	elseif (MobHealthConfig["position"]) then
		MobHealth_SetPos(MobHealthConfig["position"]);
	end

	if (not MobHealthPlayerDB) then
		MobHealthPlayerDB = {};
	end

end

local function MobHealth_Reset()
	DEFAULT_CHAT_FRAME:AddMessage(TITLE.." reseting database");
	MobHealthDB = {};
	MobHealthConfig = {};
	MobHealthPlayerDB = {}; -- this is not saved variable
end

local function MobHealth_ClearTargetData()
	if (lTargetData) then
		DEFAULT_CHAT_FRAME:AddMessage(TITLE.." reseting data for "..lTargetData.index);
		lTargetData.PPP = 0;
		lTargetData.firstBattle = true;
		lTargetData.totalDamagePoints = 0;
		lTargetData.totalChangeHP = 0;
		lTargetData.maxHP = nil;

		MobHealth_Display(nil, nil);

		if (lTargetData.mob) then
			MobHealthDB[lTargetData.index] = nil;
		else
			MobHealthPlayerDB[lTargetData.index] = nil;
		end
	else
		DEFAULT_CHAT_FRAME:AddMessage(TITLE.." no target with hp information in DB found.");
	end

end


function MobHealth_CMD(msg)
	local _, _, cmd, arg1 = string.find(msg, "(%w+)[ ]?([-%w]*)"); 

	if (cmd == "pos") then
		if (arg1 ~= "") then
			MobHealthConfig["position"] = arg1;
			MobHealth_SetPos(arg1);
		else
			DEFAULT_CHAT_FRAME:AddMessage("You need to specify position: /mobhealth2 pos [position]");
		end
	elseif (cmd == "stablemax") then
		MobHealthConfig["unstablemax"] = nil;
		DEFAULT_CHAT_FRAME:AddMessage(TITLE.." using stable max HP display");
	elseif (cmd == "unstablemax") then
		MobHealthConfig["unstablemax"] = true;
		DEFAULT_CHAT_FRAME:AddMessage(TITLE.." using unstable max HP display");
	elseif (cmd == "reset" and arg1 == "all") then
		MobHealth_Reset();
	elseif (cmd == "reset" or cmd == "del" or cmd == "delete" or cmd == "rem" or cmd == "remove" or cmd == "clear") then
		MobHealth_ClearTargetData();
	else
		DEFAULT_CHAT_FRAME:AddMessage(TITLE.." commands:");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_BLUE.."  /mobhealth2 pos [position]");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_WHITE.."    set position to [position]");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_WHITE.."    (relative to target frame, default 22, negatives also work)");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_BLUE.."  /mobhealth2 stablemax");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_WHITE.."    updates Max mob HP less often (only in first battle with mob");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_WHITE.."    and between battles)");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_BLUE.."  /mobhealth2 unstablemax");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_WHITE.."    always updates Max mob HP");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_BLUE.."  /mobhealth2 reset all");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_WHITE.."    clears whole database!");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_BLUE.."  /mobhealth2 reset/del/delete/rem/remove/clear");
		DEFAULT_CHAT_FRAME:AddMessage(COLOR_WHITE.."    clear data for current target");
	end
end

function MobHealth_SaveTargetData()
	if (lTargetData) then
		if( lTargetData.PPP > 0 and lTargetData.totalChangeHP < 10000 ) then
			if (lTargetData.mob) then
				MobHealth_Set(lTargetData.index, lTargetData.totalDamagePoints, lTargetData.totalChangeHP);
			else
				MobHealthPlayer_Set(lTargetData.index, lTargetData.totalDamagePoints, lTargetData.totalChangeHP);
			end
		end
	end	
end

--------------------------------------------------------------------------------------------------
-- OnFoo functions
--------------------------------------------------------------------------------------------------

function MobHealth_OnLoad()
	for key,value in lEventHandler do
		-- DEFAULT_CHAT_FRAME:AddMessage("Registering "..key);
		this:RegisterEvent(key);
	end

	SlashCmdList["MOBHEALTH2"] = MobHealth_CMD;

	SLASH_MOBHEALTH21 = "/mobhealth2";

end

function MobHealth_OnEvent(event)
	-- DEFAULT_CHAT_FRAME:AddMessage("Event: "..event)
	if (lEventHandler[event]) then
		lEventHandler[event](arg1, arg2, arg3, arg4);
	end
end

--------------------------------------------------------------------------------------------------
-- Event handlers
--------------------------------------------------------------------------------------------------

lEventHandler["VARIABLES_LOADED"] = function ()
	MobHealth_VariablesLoaded();	
end

lEventHandler["UNIT_HEALTH"] = function ()
	if (arg1 == "target") then
		MobHealth_Display(nil, nil);
		if( lTargetData ) then
			local health = UnitHealth("target");
			if( health and health ~= 0 ) then
				local change = lTargetData.currentHealthPercent - health;
				local damage = lTargetData.tempDamagePoints;
				if( change > 0 and damage > 0 ) then
					lTargetData.totalDamagePoints = lTargetData.totalDamagePoints + damage;
					lTargetData.totalChangeHP = lTargetData.totalChangeHP + change;

					if( lTargetData.totalChangeHP ~= 0 and lTargetData.totalDamagePoints ~= 0 ) then
						lTargetData.PPP = lTargetData.totalDamagePoints / lTargetData.totalChangeHP;
					end

					if (lTargetData.firstBattle) then
						-- this is needed for other addons reading directly from DB
						MobHealth_SaveTargetData()
					end

					lTargetData.tempDamagePoints = 0;

				elseif ( change ~= 0 ) then -- could be negative so let's be safe
					lTargetData.tempDamagePoints = 0;
				end

				lTargetData.currentHealthPercent = health;

				MobHealth_Display(health, lTargetData.index);
			end
		end
	end
end

lEventHandler["UNIT_COMBAT"] = function ()
	if( lTargetData and arg1 == "target" ) then
		lTargetData.tempDamagePoints = lTargetData.tempDamagePoints + arg4;
	end
end

lEventHandler["PLAYER_TARGET_CHANGED"] = function ()
	if (lTargetData) then
		-- we have old target, so we should update it's HP in database
		MobHealth_SaveTargetData();
	end

	local name = UnitName("target");
	if( name ) then
		if( UnitCanAttack("player", "target") and not UnitIsDead("target") and not UnitIsFriend("player", "target") ) then
			-- Attackable, alive, non-player target
			lTargetData = { };
			lTargetData.name = name;
			lTargetData.level = UnitLevel("target");
			lTargetData.currentHealthPercent = UnitHealth("target");
			lTargetData.tempDamagePoints = 0;

			if (UnitIsPlayer("target") ) then
				lTargetData.player = true;
				lTargetData.index = lTargetData.name;
				lTargetData.totalDamagePoints = MobHealthPlayer_Pts(lTargetData.index);
				lTargetData.totalChangeHP = MobHealthPlayer_Pct(lTargetData.index);
			else
				lTargetData.mob = true;
				lTargetData.index = lTargetData.name..":"..lTargetData.level;
				lTargetData.totalDamagePoints = MobHealth_Pts(lTargetData.index);
				lTargetData.totalChangeHP = MobHealth_Pct(lTargetData.index);
			end

			
			if( lTargetData.totalChangeHP ~= 0 and lTargetData.totalDamagePoints ~= 0 ) then
				lTargetData.PPP = lTargetData.totalDamagePoints / lTargetData.totalChangeHP;
			else
				lTargetData.PPP = 0;
			end

			if (lTargetData.totalChangeHP == 0) then
				-- first battle with mob/player
				lTargetData.firstBattle = true;
			else
				lTargetData.maxHP = math.floor(lTargetData.PPP * 100 + 0.5);
			end

			MobHealth_Display(lTargetData.currentHealthPercent, lTargetData.index);
			return;
		end
	end

	-- Unusable target
	lTargetData = nil;

	MobHealth_Display(nil, nil);
end

