--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
--|	GypsyMod Library - Gypsy_HotLib.lua
--|	HotBar management functions
--|	Respect to Dhargo, creator of Flexbar, for the meta table usage
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
--|	Summary & Explanation
--|	The HotLib is a library of convenience functions specifically designed to work
--|	with the HotBar's many component's buttons and bars, and to provide a central
--|	location for many common executions. It's use to any other add-on is questionable,
--|	and it is not intended to be.
--|
--|	The library is comprised largely of functions which enact upon "buttons", and in
--|	most cases there exist functions to do one thing to both a single button, or to
--|	a group of buttons. There is a vast variety of functions like this, used in
--|	different situations to avoid redundant for loops and to provide a clean way of
--|	dealing with specific things.
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

-- Isolated file version
local version = 1.10;

--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
--|	Version History
--|	1.00 - Original library with full complement of functions
--| 1.01 - Added label filtering functions
--| 1.02 - Added display functions
--| 1.03 - Added GetScale function
--| 1.04 - Expanded ShowButton to do dynamic anchoring
--|	1.05 - Improved label stripping intelligence, added comments
--|	1.06 - Added drop down functions
--| 1.07 - Added GetOppositePoint, revised drop-down placement
--| 1.08 - Added special action classes list - is not the same as bonus classes
--| 1.09 - Added loading of localized class names
--|	1.10 - Added scale record saving on objects being scaled, for change tracking
--|	1.11 - Added PlaceButton & PlaceGroup methods.
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

-- Load only if the library has not been loaded previously, or if previously loaded versions are older
if (not Gypsy_HotLib or not Gypsy_HotLib.version or Gypsy_HotLib.version < version) then

	-- Other libraries required for this to run
	if (not Gypsy_Utility) then
		return;
	end
	
	--= Libraries =--
	
	local utility = Gypsy_Utility:New();
	
	Gypsy_HotLib = {};
	Gypsy_HotLib.version = version;
	
	-- Data
	Gypsy_HotLib.defaultButtonSize = 36;
	Gypsy_HotLib.petClasses = {"Warlock", "Hunter", "Priest"};
	Gypsy_HotLib.bonusClasses = {"Warrior", "Druid", "Rogue"};
	Gypsy_HotLib.specialActionClasses = {"Warrior", "Druid", "Rogue", "Paladin"};
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_HotLib:New
	--|	Used to make this library available for use locally from script files. 
	--|		Usage: local hotlib = Gypsy_HotLib:New();
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:New ()			
		local object = {};
		setmetatable(object, self);
		self.__index = self;
		return object;
	end

	--= Generic Button Functions =--
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_HotLib:ShowButton
	--| Shows and can dynamically positions any passed button object. Positioning is done
	--|	using the GetDisplayProperties method, and requires orientation information, gap
	--|	size (distance between button and what it's anchored to), and a frame name to 
	--|	anchor it to. Used primarily to show and anchor sequential buttons in a bar.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:ShowButton (button, orient, gap, rel)
		if (not button) then
			return;
		end
		
		button.shown = 1;
		button:Show();
		
		if (orient and gap) then
			button:ClearAllPoints();
			button:SetPoint(self:GetDisplayProperties(orient, gap, rel));
		end
	end
	
	function Gypsy_HotLib:ShowGroup (prefix, start, finish, orient, gap, rel)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			self:ShowButton(getglobal(prefix..i), orient, gap, rel);
		end
	end
	
	function Gypsy_HotLib:PlaceButton (button, orient, gap, rel)
		if (not button) then
			return;
		end
		
		if (orient and gap) then
			button:ClearAllPoints();
			button:SetPoint(self:GetDisplayProperties(orient, gap, rel));
		end
	end
	
	function Gypsy_HotLib:PlaceGroup (prefix, start, finish, orient, gap, rel)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			self:PlaceButton(getglobal(prefix..i), orient, gap, rel);
		end
	end
	
	function Gypsy_HotLib:HideButton (button)
		if (not button) then
			return;
		end
		
		button.shown = 0;
		button:Hide();
	end
	
	function Gypsy_HotLib:HideGroup (prefix, start, finish)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			getglobal(prefix..i).shown = 0;
			getglobal(prefix..i):Hide();
		end
	end

	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_HotLib:GetScale
	--|	The HotBar utilizes a button sizing option which in essence actually uses scaling.
	--|	This function is here to convert the button size in pixels to a decimal scale
	--|	figure. It does this by comparing the normal size of the button to the desired
	--|	size, and multiplying by the UIParent's actual scale, to get the new independent
	--|	scale factor.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:GetScale (size)
		if (not size) then
			size = self.defaultButtonSize;
		end
		
		return size / self.defaultButtonSize;
	end
	
	function Gypsy_HotLib:ScaleButton (button, size)
		if (not button) then
			return;
		end

		button:SetScale(self:GetScale(size));
		button.scale = button:GetScale();
	end
	
	function Gypsy_HotLib:ScaleGroup (prefix, start, finish, size)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			self:ScaleButton(getglobal(prefix..i), size);
		end
	end

	--= Action Button Functions =--
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Here are a number of action button specific functions which in most cases provide
	--|	merely a convenient way to manually update various button aspects, in single and
	--|	groups.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:UpdateGroup (prefix, start, finish)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			Gypsy_ActionButton_Update(getglobal(prefix..i));
		end
	end
	
	function Gypsy_HotLib:UpdateGroupCount (prefix, start, finish)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			Gypsy_ActionButton_UpdateCount(getglobal(prefix..i));
		end
	end
	
	function Gypsy_HotLib:UpdateButtonHotkey (button)
		if (not button) then
			return;
		end
		
		Gypsy_ActionButton_UpdateHotkeys(button);
	end
	
	function Gypsy_HotLib:UpdateGroupHotkey (prefix, start, finish)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			self:UpdateButtonHotkey(getglobal(prefix..i));
		end
	end
	
	function Gypsy_HotLib:UpdateButtonGrid (button, showgrid, update)
		if (not button or not showgrid) then
			return;
		end
		
		button.showgrid = showgrid;
		
		if (update) then
			Gypsy_ActionButton_Update(button);
		end
	end
	
	function Gypsy_HotLib:UpdateGroupGrid (prefix, start, finish, showgrid, update)
		if (not start or not finish) then
			return;
		end
		
		for i=start, finish do
			self:UpdateButtonGrid(getglobal(prefix..i), showgrid, update);
		end
	end

	--= Label Functions =--
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Label filters are a series of strings which act as filters to shorten or strip
	--|	out undesired portions of key binding labels. The string filters must be provided
	--|	to the library by an external source due to localization. This is done with the
	--|	LoadLabelFilters function. The function accepts a simple table where each index
	--|	is the string that is to be replaced, and it's value is the replacement.
	--|		Example:
	--|			labelFilters = {
	--|				["Shift"] = "S";
	--|			};
	--|	If this was loaded, any string supplied to the CondenseLabel function would have
	--|	occurances of "Shift" replaced with "S". Any string supplied to StripLabel would
	--|	have occurances of "Shift" replaced with "". StripCondensedLabel may also be used
	--|	to strip an already condensed label.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:LoadLabelFilters (args)
		if (type(args) == "table") then
			self.labelFilters = args;
		end
	end		
	
	function Gypsy_HotLib:CondenseLabel (label)
		if (self.labelFilters) then
			for i,v in self.labelFilters do
				label = gsub(label, i, v);
			end
		end
		return label;
	end
			
	function Gypsy_HotLib:StripLabel (label)
		if (self.labelFilters) then
			for i,v in self.labelFilters do
				if (strsub(label, strlen(label)-strlen(i), strlen(label)) == i) then
					label = gsub(label, i, "");
					label = label..i;
				else
					label = gsub(label, i, "");
				end
			end
			if (strsub(label, strlen(label), strlen(label)) == "-") then
				label = "-";
			else
				label = gsub(label, "-", "");
			end
		end
		return label;
	end
	
	function Gypsy_HotLib:StripCondensedLabel (label)
		if (self.labelFilters) then
			for i,v in self.labelFilters do
				if (strsub(label, strlen(label)-strlen(v), strlen(label)) == v) then
					label = gsub(label, v, "");
					label = label..v;
				else
					label = gsub(label, v, "");
				end
			end
			if (strsub(label, strlen(label), strlen(label)) == "-") then
				label = "-";
			else
				label = gsub(label, "-", "");
			end
		end
		return label;
	end
	
	--= Display Functions =--
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_HotLib:GetDisplayProperties
	--|	A simple function which relates only to button positioning, will return all
	--|	anchoring information depending on orientation, with proper adjustments for
	--|	orientation.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:GetDisplayProperties (orient, gap, rel)
		local point, relPoint;
		local x = 0;
		local y = 0;
		
		if (orient == "HORI") then
			point = "BOTTOMLEFT";
			relPoint = "BOTTOMRIGHT";
			x = gap;
		else
			point = "TOPLEFT";
			relPoint="BOTTOMLEFT";
			y = -gap;
		end
		
		return point, rel, relPoint, x, y;
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	The GetGroupWidth and Height functions are simple calculations based upon orient
	--|	and a number of supplied lengths which have different meanings depending upon the
	--|	function used, and the orientation itself.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:GetGroupWidth (orient, numButtons, sizeX, sizeY, gap)
		if (orient == "HORI") then
			return (sizeX * numButtons) + (gap * numButtons) - gap;
		else
			return sizeX;
		end
	end
	
	function Gypsy_HotLib:GetGroupHeight (orient, numButtons, sizeX, sizeY, gap)
		if (orient == "HORI") then
			return sizeY;
		else
			return (sizeY * numButtons) + (gap * numButtons) - gap;
		end
	end
	
	--= Drop-Down Menu Functions =--
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_HotLib:ToggleDropDown
	--|	Toggles a drop-down menu and positions it optimally, depending on where the
	--|	target capsule is on-screen.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:ToggleDropDown (target)
		if (not target) then
			return;
		end
		
		local frame = getglobal(target.."_Capsule");
				
		if (not frame.orient) then
			return;
		end
		
		local parent;
		
		if (frame.orient == "HORI") then
			parent = getglobal(target.."_CapsuleHoriTab");
		else
			parent = getglobal(target.."_CapsuleVertTab");
		end
		
		ToggleDropDownMenu(1, nil, getglobal(target.."_DropDown"), parent:GetName(), 0, 0);
		
		utility:PositionDropDown(parent);
		
		--[[local tabX, tabY = parent:GetCenter();
		local screenX, screenY = UIParent:GetCenter();
		local quadrant = "";
		
		if (tabX > screenX) then
			quadrant = "RIGHT";
		else
			quadrant = "LEFT";
		end
		
		if (tabY > screenY) then
			quadrant = "TOP"..quadrant;
		else
			quadrant = "BOTTOM"..quadrant;
		end
		
		DropDownList1:ClearAllPoints();
		DropDownList1:SetPoint(quadrant, parent:GetName(), utility:GetOppositePoint(quadrant));]]
	end
	
	--= Utility Functions =--
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	These functions are quick checks as to whether a unit qualifies as a pet or bonus
	--|	-bar having class. Defaults to player, uses class lists supplied at the start of
	--|	this library.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_HotLib:LoadClassNames (petList, bonusList, specialActionList)
		self.petClasses = petList;
		self.bonusClasses = bonusList;
		self.specialActionClasses = specialActionList;
	end
	
	function Gypsy_HotLib:IsPetClass (unit)
		if (not unit) then
			unit = "player";
		end
		
		for i,v in self.petClasses do
			if (UnitClass(unit) == v) then
				return 1;
			end
		end
		
		return nil;
	end
	
	function Gypsy_HotLib:IsBonusClass (unit)
		if (not unit) then
			unit = "player";
		end
		
		for i,v in self.bonusClasses do
			if (UnitClass(unit) == v) then
				return 1;
			end
		end
		
		return nil;
	end
	
	function Gypsy_HotLib:IsSpecialActionClass (unit)
		if (not unit) then
			unit = "player";
		end
		
		for i,v in self.specialActionClasses do
			if (UnitClass(unit) == v) then
				return 1;
			end
		end
		
		return nil;
	end
end