--------------------------------------------------------------------------
-- IsMounted.lua 
--------------------------------------------------------------------------
--[[
IsMounted

author: AnduinLothar KarlKFI@cosmosui.org

-Mini-Library that maintains a list of who's mounted.

Note: Information gathered from this addon is only correct for friendly units or units that you can see the buffs of.

Change Log:
v1.7
-Now Embeddable
v1.62
-Updated tooltip anchoring for 1.10
v1.61
-Fixed nil find string error
v1.6
-Player mounted check now uses the PlayerBuff functions rather than UnitBuff.
-Player Buff ID returned now coresponds to the buff index returned from GetPlayerBuff(i, "HELPFUL|PASSIVE") and can be used directly by CancelPlayerBuff.
-Dismount now works correctly with debuffs.
-Localed a few internal functions for quicker indexing
v1.5
- Mounted status now accurate when called from PLAYER_AURAS_CHANGED and not cleared on UNIT_AURA
(UNIT_AURA w/ arg1=="player" is always called after PLAYER_AURAS_CHANGED.) 
v1.4
- Changed tooltip scan to use IsShown to work with 1.9
v1.3
- Added French and German Localizations
v1.2
- Added Function: Dismount()
- Added Function: UnitMountSpeed(unit)
- Added Function: UnitMountBuffID( string unit )
- Changed all refrences of 'buff slot' to 'id' 
Note: id is the id of the buff in terms of Tooltip/Texture index that starts at 1 while the Buff/Frame index starts at 0. So you might have to subtract 1 till blizz fixes it next patch, depending on your usage.
v1.1
- Fixed but that would throw error when UNIT_AURA triggered for 'mouseover' unit. (I Didn't know it caught mouseover O.o)
v1.0
- Initial Release
]]--

local ISMOUNTED_NAME 			= "IsMounted"
local ISMOUNTED_VERSION 		= 1.7
local ISMOUNTED_LAST_UPDATED	= "August 6, 2006"
local ISMOUNTED_AUTHOR 			= "AnduinLothar"
local ISMOUNTED_EMAIL			= "karlkfi@cosmosui.org"
local ISMOUNTED_WEBSITE			= "http://www.wowwiki.com/Telepathy"

------------------------------------------------------------------------------
--[[ Embedded Sub-Library Load Algorithm ]]--
------------------------------------------------------------------------------

if (not IsMounted) then
	IsMounted = {};
end
local isBetterInstanceLoaded = (IsMounted.version and IsMounted.version >= ISMOUNTED_VERSION);

if (not isBetterInstanceLoaded) then
	
	IsMounted.version = ISMOUNTED_VERSION;

	
	--------------------------------------------------
	-- Predictibly consistant units
	--------------------------------------------------
	
	IsMounted.UnitTable = {
		player = {};
		target = {};
	}
	for i=1, 4 do IsMounted.UnitTable["party"..i] = {} end
	for i=1, 40 do IsMounted.UnitTable["raid"..i] = {} end
	
	
	--------------------------------------------------
	-- Internal Functions
	--------------------------------------------------
	
	function IsMounted.OnLoad()
		IsMountedFrame:RegisterEvent("PLAYER_AURAS_CHANGED");
		IsMountedFrame:RegisterEvent("UNIT_AURA");
		IsMountedFrame:RegisterEvent("PLAYER_TARGET_CHANGED");
		IsMountedFrame:RegisterEvent("PARTY_MEMBERS_CHANGED");
		IsMountedFrame:RegisterEvent("RAID_ROSTER_UPDATE");
		IsMountedFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
	end
	
	-- Nils predictibly consistant stored values when the unit or its buffs change
	function IsMounted.OnEvent()
		if (event == "PLAYER_ENTERING_WORLD") then
			IsMounted.UnitTable["player"].speed = nil;
			IsMounted.UnitTable["player"].id = nil;
		elseif (event == "PLAYER_AURAS_CHANGED") then
			--Sea.io.print("player IsMounted mount state nilled.");
			IsMounted.UnitTable["player"].speed = nil;
			IsMounted.UnitTable["player"].id = nil;
		elseif (event == "UNIT_AURA") then
			if (not IsMounted.UnitTable[arg1]) then
				--Triggered by 'mouseover' buff change
				--Sea.io.print("UNIT_AURA: ", arg1);
			elseif (arg1 ~= "player") then
				--Sea.io.print(arg1, " IsMounted mount state nilled.");
				IsMounted.UnitTable[arg1].speed = nil;
				IsMounted.UnitTable[arg1].id = nil;
			end
		elseif (event == "PLAYER_TARGET_CHANGED") then
			IsMounted.UnitTable["target"].speed = nil;
			IsMounted.UnitTable["target"].id = nil;
		elseif (event == "PARTY_MEMBERS_CHANGED") then
			for i=1, 4 do
				IsMounted.UnitTable["party"..i].speed = nil;
				IsMounted.UnitTable["party"..i].id = nil;
			end
		elseif (event == "RAID_ROSTER_UPDATE") then
			for i=1, 40 do
				IsMounted.UnitTable["raid"..i].speed = nil;
				IsMounted.UnitTable["raid"..i].id = nil;
			end
		end
	end
	
	-- Sets stored values of predictibly consistant units and returns the speed and id of the mount buff.
	local GetUpdatedUnitTable = function(unit)
		local speed, id = IsMounted.GetUpdatedMountBuffInfo(unit);
		if (IsMounted.UnitTable[unit]) then
			--Sea.io.print("Updating UnitTable for ".. unit..": ", speed, ", ", id); -- debug msg
			IsMounted.UnitTable[unit].speed = speed;
			IsMounted.UnitTable[unit].id = id;
		end
		return speed, id;
	end
	
	
	-- Gets speed and id of player mount buff according to GetPlayerBuff
	local GetPlayerMountBuffInfo = function()
		-- Check the tooltips of the player's active buffs for the key string.
		local text, buffIndex, untilCancelled, _, speed;
		IsMountedTooltip:SetOwner(UIParent, "ANCHOR_NONE"); 
		for i = 0, 23 do
			buffIndex, untilCancelled = GetPlayerBuff(i, "HELPFUL|PASSIVE");
			if ( buffIndex < 0 ) then
				break;
			elseif ( untilCancelled ) then
				IsMountedTooltip:SetPlayerBuff(buffIndex);
				if (IsMountedTooltipTextLeft2:IsShown()) then
					text = IsMountedTooltipTextLeft2:GetText();
					if (text) then
						_, _, speed = string.find(text, ISMOUNTED_SPEED_INCREASED_BY);
						if (speed) then
							IsMountedTooltip:Hide();
							return tonumber(speed), buffIndex;
						end
					end
				end
			end
		end
		IsMountedTooltip:Hide();
		return false;
	end
	
	-- Gets speed and id of non-player mount buff according to UnitBuff
	local GetNonPlayerMountBuffInfo = function(unit)
		-- Check the tooltips of the unit's active buffs for the key string.
		local text, _, speed;
		IsMountedTooltip:SetOwner(UIParent, "ANCHOR_NONE"); 
		for i = 1, 24 do
			if (not UnitBuff(unit, i)) then
				break;
			end
			IsMountedTooltip:SetUnitBuff(unit, i);
			if (IsMountedTooltipTextLeft2:IsShown()) then
				text = IsMountedTooltipTextLeft2:GetText();
				if (text) then
					--Sea.io.print("Checking text: ".. text); -- debug msg
					_, _, speed = string.find(text, ISMOUNTED_SPEED_INCREASED_BY);
					if (speed) then
						IsMountedTooltip:Hide();
						return tonumber(speed), i;
					end
				end
			end
		end
		IsMountedTooltip:Hide();
		return false;
	end
	
	
	--------------------------------------------------
	-- User Functions
	--------------------------------------------------
	
	-- 
	-- UnitIsMounted( string unit )
	--
	--	EX: if (UnitIsMounted("player")) then doSomething(); end
	--
	-- Returns: 
	-- 	(Boolean isMounted)
	--	isMounted - 1 if mounted, else nil
	-- 
	function UnitIsMounted(unit)
		if (IsMounted.GetMountBuffInfo(unit)) then
			return 1;
		end
	end
	
	-- 
	-- Dismount()
	--
	--	EX: if (Dismount()) then print("I'm Dismounting"); end
	--	Macro EX: /dismount
	--
	-- Returns: 
	-- 	(Boolean wasMounted)
	--	wasMounted - 1 if was mounted, else nil
	-- 
	function Dismount()
		local speed, id = IsMounted.GetMountBuffInfo("player");
		if (speed) then
			CancelPlayerBuff(id);
			return 1;
		end
	end
	
	SlashCmdList["DISMOUNT"] = Dismount;
	SLASH_DISMOUNT1 = "/dismount";
	
	-- 
	-- UnitMountSpeed( string unit )
	--
	--	EX: local speed = UnitMountSpeed("target")
	--
	-- Returns: 
	-- 	(Number speed)
	-- 	speed - percent speed increase of the mount (60 or 100) if unit is mounted,
	--			false if mount is not found
	-- 
	function UnitMountSpeed(unit)
		local speed, id = IsMounted.GetMountBuffInfo(unit);
		return speed;
	end
	
	-- 
	-- UnitMountBuffID( string unit )
	--
	--	EX: local id = UnitMountBuffID("target")
	--
	-- Returns: 
	-- 	(Number id)
	-- 	id - the buff id id of the mount, nil if mount is not found or unit doesn't exist
	-- 
	function UnitMountBuffID(unit)
		local speed, id = IsMounted.GetMountBuffInfo(unit);
		return id;
	end
	
	-- 
	-- IsMounted.GetMountBuffInfo( string unit )
	-- 
	--	EX: local speed, id = IsMounted.GetMountBuffInfo("player")
	--
	-- Returns: 
	-- 	(Number speed, Number id)
	-- 	speed - percent speed increase of the mount (60 or 100) if unit is mounted,
	--			false if mount is not found
	--	id - the buff id id of the mount, nil if mount is not found or unit doesn't exist
	--
	function IsMounted.GetMountBuffInfo(unit)
		if (IsMounted.UnitTable[unit]) and (IsMounted.UnitTable[unit].speed ~= nil) then
			return IsMounted.UnitTable[unit].speed, IsMounted.UnitTable[unit].id;
		end
		return GetUpdatedUnitTable(unit);
	end
	
	-- 
	-- IsMounted.GetUpdatedMountBuffInfo( string unit )
	-- 
	--	Gets Updated Mount Buff Info (duh!)
	--
	-- Returns: 
	-- 	(Number speed, Number id)
	-- 	speed - percent speed increase of the mount (60 or 100) if unit is mounted,
	--			false if mount is not found (so that we know it's been checked since last changed)
	--	id - the buff id id of the mount, nil if mount is not found or unit doesn't exist
	--
	function IsMounted.GetUpdatedMountBuffInfo(unit)
		if (UnitIsPlayer(unit)) then
			if ( (not IsMounted.UnitTable[unit]) or (unit == "target") ) and (UnitIsFriend("player", unit)) then
				-- This unit check will always check 46 stored tables for saved speed, but it should hopefully still be faster than setting and checking the tooltips of the unit's active buffs.
				for u, i in IsMounted.UnitTable do
					if (i.speed ~= nil) and (UnitIsUnit(unit, u)) then
						--Sea.io.print(unit, " is ", u);	-- debug msg
						if (unit == "target") then
							--only store the info for the unit if it is predictibly consistant
							IsMounted.UnitTable.target.speed = i.speed;
							IsMounted.UnitTable.target.id = i.id;
						end
						return i.speed, i.id;
					end
				end
			end
			
			if (unit == "player") then
				return GetPlayerMountBuffInfo();
			else
				return GetNonPlayerMountBuffInfo(unit);
			end
		end
		return false;
	end
	
	
	------------------------------------------------------------------------------
	--[[ Frame Script Assignment ]]--
	------------------------------------------------------------------------------
	
	--Event Driver
	if (not IsMountedFrame) then
		CreateFrame("Frame", "IsMountedFrame");
	end
	IsMountedFrame:Hide();
	--Frame Scripts
	IsMountedFrame:SetScript("OnEvent", IsMounted.OnEvent);
	--OnLoad Call (this masked)
	local tempThis = this;
	this = IsMountedFrame;
	IsMounted.OnLoad();
	this = tempThis;
	
	--Mining Tooltip
	if (not IsMountedTooltip) then
		CreateFrame("GameTooltip", "IsMountedTooltip", nil, "GameTooltipTemplate");
	end
	IsMountedTooltip:SetOwner(IsMountedTooltip, "ANCHOR_NONE");
	
end
