DL_VARIABLES_LOADED = nil;
DL_ENTERING_WORLD = nil;

DL_INIT_FUNCTIONS = {};
DL_FRAME_FUNCTIONS = {};

DL_Old_UseAction = UseAction;

DL_Old_PickupAction = PickupAction;
DL_Old_PlaceAction = PlaceAction;

function DL_PickupAction(action)
	DL_Old_PickupAction(action);
	DL_Update_ActionList();
end

function DL_PlaceAction(action)
	DL_Old_PlaceAction(action);
	DL_Update_ActionList();
end

PickupAction = DL_PickupAction;
PlaceAction = DL_PlaceAction;

function DiscordLib_Main_OnEvent(event)
	if (event == "CHAT_MSG_COMBAT_SELF_MISSES" or event == "CHAT_MSG_SPELL_SELF_DAMAGE") then
		if (string.find(arg1, DL_TEXT.Dodge)) then
			DL_TARGET_DODGED = 6;
		elseif (string.find(arg1, DL_TEXT.Parry)) then
			DL_TARGET_PARRIED = 6;
		elseif (string.find(arg1, DL_TEXT.Block)) then
			DL_TARGET_BLOCKED = 6;
		end
	elseif (event == "PARTY_MEMBERS_CHANGED") then
		for i=1,4 do
			DL_Update_Auras("party"..i);
		end
	elseif (event == "PET_ATTACK_START") then
		DL_PETATTACKING = true;
	elseif (event == "PET_ATTACK_STOP") then
		DL_PETATTACKING = nil;
	elseif (event == "PLAYER_AURAS_CHANGED") then
		DL_Update_Auras("player");
	elseif (event == "PLAYER_ENTER_COMBAT") then
		DL_ATTACKING = true;
		DL_INCOMBAT = true;
	elseif (event == "PLAYER_ENTERING_WORLD") then
		DL_ENTERING_WORLD = true;
		DL_ATTACKING = nil;
		DL_INCOMBAT = nil;
		DL_REGEN = true;
		if ( DL_VARIABLES_LOADED ) then
			DL_START_CHECKING_FRAME = true;
		end
	elseif (event == "PLAYER_LEAVE_COMBAT") then
		DL_ATTACKING = nil;
		if (DL_REGEN) then
			DL_INCOMBAT = nil;
		end
	elseif (event == "PLAYER_REGEN_DISABLED") then
		DL_REGEN = nil;
		DL_INCOMBAT = true;
	elseif (event == "PLAYER_REGEN_ENABLED") then
		DL_REGEN = true;
		if (not DL_ATTACKING) then
			DL_INCOMBAT = nil;
		end
	elseif (event == "PLAYER_TARGET_CHANGED") then
		DL_Update_Auras("target");
	elseif (event == "RAID_ROSTER_UPDATE") then
		for i=1,40 do
			DL_Update_Auras("raid"..i);
		end
	elseif (event == "UNIT_AURA") then
		if (arg1 ~= "mouseover" and arg1 ~= "player") then
			DL_Update_Auras(arg1);
		end
	elseif (event == "UNIT_PET") then
		DL_Update_Auras("pet");
		for i=1,4 do
			DL_Update_Auras("partypet"..i);
		end
	elseif (event == "UPDATE_SHAPESHIFT_FORMS") then
		DL_Update_Forms();
	elseif (event == "VARIABLES_LOADED") then
		DL_VARIABLES_LOADED = true;
		if ( DL_ENTERING_WORLD ) then
			DL_START_CHECKING_FRAME = true;
		end
	end
end

function DiscordLib_Main_OnLoad()
	this:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES");
	this:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE");
	this:RegisterEvent("PARTY_MEMBERS_CHANGED");
	this:RegisterEvent("PET_ATTACK_START");
	this:RegisterEvent("PET_ATTACK_STOP");
	this:RegisterEvent("PLAYER_AURAS_CHANGED");
	this:RegisterEvent("PLAYER_ENTER_COMBAT");
	this:RegisterEvent("PLAYER_ENTERING_WORLD");
	this:RegisterEvent("PLAYER_LEAVE_COMBAT");
	this:RegisterEvent("PLAYER_REGEN_DISABLED");
	this:RegisterEvent("PLAYER_REGEN_ENABLED");
	this:RegisterEvent("PLAYER_TARGET_CHANGED");
	this:RegisterEvent("RAID_ROSTER_UPDATE");
	this:RegisterEvent("UNIT_AURA");
	this:RegisterEvent("UNIT_PET");
	this:RegisterEvent("UPDATE_SHAPESHIFT_FORMS");
	this:RegisterEvent("VARIABLES_LOADED");

	SlashCmdList["DISCLIB"] = DL_Slash_Handler;
	SLASH_DISCLIB1 = "/disclib";
end

function DL_Slash_Handler(msg)
	if (msg == "nobuffchecking") then
		if (DiscordLibrary_Settings.disableBuffChecking) then
			DiscordLibrary_Settings.disableBuffChecking = nil;
			DL_Feedback("Discord Library's buff/debuff checking has been turned on.");
			DiscordLib_MainFrame:RegisterEvent("UNIT_AURA");
			DiscordLib_MainFrame:RegisterEvent("PLAYER_AURAS_CHANGED");
			DL_Update_Auras("player");
			DL_Update_Auras("pet");
			DL_Update_Auras("target");
			for i=1,4 do
				DL_Update_Auras("party"..i);
				DL_Update_Auras("partypet"..i);
			end
			for i=1,40 do
				DL_Update_Auras("raid"..i);
			end
		else
			DiscordLibrary_Settings.disableBuffChecking = 1;
			DL_Feedback("Discord Library's buff/debuff checking has been turned off.");
			DL_BUFFS = {};
			DL_DEBUFFS = {};
			DL_STATUS = {};
			DiscordLib_MainFrame:UnregisterEvent("UNIT_AURA");
			DiscordLib_MainFrame:UnregisterEvent("PLAYER_AURAS_CHANGED");
		end
	end
end

function DiscordLib_Main_OnUpdate(elapsed)
	if (DL_TARGET_DODGED) then
		DL_TARGET_DODGED = DL_TARGET_DODGED - elapsed;
		if (DL_TARGET_DODGED < 0) then DL_TARGET_DODGED = nil; end
	end
	if (DL_TARGET_BLOCKED) then
		DL_TARGET_BLOCKED = DL_TARGET_BLOCKED - elapsed;
		if (DL_TARGET_BLOCKED < 0) then DL_TARGET_BLOCKED = nil; end
	end
	if (DL_TARGET_PARRIED) then
		DL_TARGET_PARRIED = DL_TARGET_PARRIED - elapsed;
		if (DL_TARGET_PARRIED < 0) then DL_TARGET_PARRIED = nil; end
	end
	if (this.inittimer) then
		this.inittimer = this.inittimer - elapsed;
		if (this.inittimer < 0) then
			this.inittimer = nil;
			DiscordLib_Initialize();
		end
	else
		if (DL_START_CHECKING_FRAME and (not DISCORDLIB_INITIALIZED)) then
			if (ZoneTextFrame:GetCenter()) then
				this.inittimer = 1;
			end
		end
	end
end

function DL_UnitName(unit)
	local name = UnitName(unit);
	if (name == UNKNOWNOBJECT or name == UKNOWNBEING) then
		return nil;
	else
		return name;
	end
end

DL_Old_MoneyFrame_Update = MoneyFrame_Update
function MoneyFrame_Update(frameName, money)
	if (not money) then return end
	DL_Old_MoneyFrame_Update(frameName, money)
end

--[[DL_Old_ChatEdit_OnEnterPressed = ChatEdit_OnEnterPressed
function ChatEdit_OnEnterPressed()
	local f = this:GetName()
	DL_Debug(f)
	if (string.find(f, "DAB_")) then
		return
	end
	DL_Old_ChatEdit_OnEnterPressed()
end

DL_Old_ChatEdit_OnEscapePressed = ChatEdit_OnEscapePressed
function ChatEdit_OnEscapePressed(editBox)
	if (not editBox) then return end
	if (not editBox.SetText) then return end
	DL_Old_ChatEdit_OnEscapePressed(editBox)
end
--]]
function DiscordLib_Initialize()
	if (DISCORDLIB_INITIALIZED) then return; end

	if (not DiscordLibrary_Settings) then
		DiscordLibrary_Settings = {}
	end

	DL_Old_ColorPickerFrame_OnColorSelect = ColorPickerFrame:GetScript("OnColorSelect");
	ColorPickerFrame:SetScript("OnColorSelect", function() DL_Old_ColorPickerFrame_OnColorSelect(); DL_ColorBox_UpdateDisplay(); end);

	for i=2,30 do
		local left = getglobal("DiscordLib_AuraTooltipTextLeft"..i)
		for i,v in left do
			if (type(v) == "function") then
				left[i] = function() end
			end
		end
		local left = getglobal("DiscordLib_AuraTooltipTextRight"..i)
		for i,v in left do
			if (type(v) == "function") then
				left[i] = function() end
			end
		end
	end
	for i,v in DiscordLib_AuraTooltipTextLeft1 do
		if (type(v) == "function" and i ~= "GetText" and i ~= "SetText" and i ~= "Hide" and i ~= "Show") then
			DiscordLib_AuraTooltipTextLeft1[i] = function() end
		end
	end
	for i,v in DiscordLib_AuraTooltipTextRight1 do
		if (type(v) == "function" and i ~= "GetText" and i ~= "SetText" and i ~= "Hide" and i ~= "Show") then
			DiscordLib_AuraTooltipTextRight1[i] = function() end
		end
	end
	
	DiscordLib_Tooltip:SetOwner(WorldFrame, "ANCHOR_NONE");
	DiscordLib_AuraTooltip:SetOwner(WorldFrame, "ANCHOR_NONE");

	DL_Update_ActionList();
	DL_Init_AnchorFramesList();
	DL_Init_UnitsList();
	DL_TEXTURES_LIST = { {text="", value=""} };
	DL_Init_FontsList();
	DL_Update_Forms();
		DL_Update_Auras("player");
	DL_Update_Auras("pet");
	for i=1,4 do
		DL_Update_Auras("party"..i);
		DL_Update_Auras("partypet"..i);
	end
	for i=1,40 do
		DL_Update_Auras("raid"..i);
	end
	for index, initFunction in DL_FRAME_FUNCTIONS do
		initFunction();
	end
	for index, initFunction in DL_INIT_FUNCTIONS do
		initFunction();
	end

	DISCORDLIB_INITIALIZED = true;
	DL_INIT_FUNCTIONS = nil;
end

function DiscordLib_RegisterInitFunc(func, delay)
	DL_INIT_FUNCTIONS_INDEX = DL_INIT_FUNCTIONS_INDEX + 1;
	DL_INIT_FUNCTIONS[DL_INIT_FUNCTIONS_INDEX] = func;
end

function DiscordLib_RegisterFrameCreationFunc(func)
	DL_FRAME_FUNCTIONS_INDEX = DL_FRAME_FUNCTIONS_INDEX + 1
	DL_FRAME_FUNCTIONS[DL_FRAME_FUNCTIONS_INDEX] = func
end

function DL_ActionUsable(action)
	local cd = DL_CooldownLeft(action);
	if (cd > 0) then return; end
	local isUsable, notEnoughMana = IsUsableAction(action);
	if (not isUsable) then return; end
	if (notEnoughMana) then return; end
	local range = IsActionInRange(action);
	if (range ~= 0) then return true; end
end

function DL_AddToMenu(menu, text, value, desc)
	if (not text) then return; end
	if (not value) then value = text; end
	if (desc == 1) then
		desc = value;
		if (desc and string.len(desc) > 25) then
			desc = string.sub(desc, 1, 25).."\n"..string.sub(desc, 26);
		end
	end
	local found;
	for _, val in menu do
		if (val.value == value) then
			found = 1;
			break;
		end
	end
	if (not found) then
		menu[table.getn(menu) + 1] = { text=text, value=value, desc=desc };
	end
end

function DL_Alphabetic_Insert(table, value, subindex, start)
	local insertIndex;
	for i,v in table do
		if ( strlower(value[subindex])<strlower(v[subindex]) or strlower(value[subindex])==strlower(v[subindex]) ) then
			if ((start and i >= start) or (not start)) then
				insertIndex = i;
				break;
			end
		end
	end
	tinsert(table, insertIndex, value);
end

function DL_AttackTarget()
	if (UnitHealth("target") and UnitHealth("target") <= 0) then return; end
	local _, class = UnitClass("player");
	if (class == "ROGUE" and DL_Get_ShapeshiftForm() == 1) then return; end
	if (class == "DRUID" and DL_Get_ShapeshiftForm() == 3) then return; end
	if (UnitCanAttack("player","target")) then
		if (not DL_ATTACKING) then
			AttackTarget(); 
		end
	end
end

function DL_CheckBuff(unit, buff)
	if (not DL_BUFFS[unit]) then return; end
	for i=1,16 do
		if (DL_BUFFS[unit][i]) then
			if (string.find(DL_BUFFS[unit][i], buff, 1, true)) then
				return true;
			end
		else
			break;
		end
	end
end

function DL_CheckDebuff(unit, buff)
	if (not DL_DEBUFFS[unit]) then return; end
	for i=1,16 do
		if (DL_DEBUFFS[unit][i]) then
			if (string.find(DL_DEBUFFS[unit][i], buff, 1, true)) then
				return i;
			end
		else
			break;
		end
	end
end

function DL_CheckStatus(unit, status)
	if (not DL_STATUS[unit]) then return 0; end
	local count = 0;
	for i=1,16 do
		if (DL_STATUS[unit][i] == status) then
			count = count + 1;
		end
	end
	return count;
end

function DL_CleanUp_TempOptions(menu)
	if (menu.addedoptions) then
		for i in menu.addedoptions do
			getglobal(menu.table)[i] = nil;
		end
	end
end

function DL_ColorBox_UpdateColor()
	if (not this:GetText() or this:GetText() == "") then return; end
	local r, g, b = ColorPickerFrame:GetColorRGB();
	local id = this:GetID();
	if (id == 1) then
		r = this:GetNumber();
		if (not r) then r = 0; end
	elseif (id == 2) then
		g = this:GetNumber();
		if (not g) then g=0; end
	else
		b = this:GetNumber();
		if (not b) then b=0; end
	end
	ColorPickerFrame:SetColorRGB(r, g, b);
end

function DL_ColorBox_UpdateDisplay()
	if (DL_COLORBOX_FOCUS) then return; end
	local r, g, b = ColorPickerFrame:GetColorRGB();
	DL_RedBox:SetNumber(DL_round(r, 4));
	DL_GreenBox:SetNumber(DL_round(g, 4));
	DL_BlueBox:SetNumber(DL_round(b, 4));
end

function DL_Compare(val1, val2, method)
	if (method == 1) then
		if (val1 > val2) then return 1; else return; end
	elseif (method == 2) then
		if (val1 >= val2) then return 1; else return; end
	elseif (method == 3) then
		if (val1 == val2) then return 1; else return; end
	elseif (method == 4) then
		if (val1 <= val2) then return 1; else return; end
	elseif (method == 5) then
		if (val1 < val2) then return 1; else return; end
	end
end

function DL_CooldownLeft(action)
	local start, duration, enable = GetActionCooldown(action);
	if (start and duration and enable) then
		local remaining = duration - (GetTime() - start);
		if (remaining > 0) then
			return remaining;
		else
			return 0;
		end
	else
		return 0;
	end
end

function DL_Copy_Table(src, dest)
	for index, value in src do
		if (type(value) == "table") then
			dest[index] = {};
			DL_Copy_Table(value, dest[index]);
		else
			dest[index] = value;
		end
	end
end

function DL_Debug(msg)
	DEFAULT_CHAT_FRAME:AddMessage( msg, 1.0, 0.0, 0.0 );
end

function DL_Error(msg)
	if (not DL_ScriptErrors:IsVisible()) then
		DL_ScriptErrors_Message:SetText(msg);
		DL_ScriptErrors:Show();
	else
		DL_ERRORS_QUEUE[table.getn(DL_ERRORS_QUEUE) + 1] = msg;
	end
end

function DL_Feedback(msg)
	DEFAULT_CHAT_FRAME:AddMessage( msg, 1.0, 1.0, 0.0 );
end

function DL_Get_ActionName(id, ranktoggle)
	if (not id) then return ""; end
	if (not HasAction(id)) then return ""; end
	DiscordLib_Tooltip:SetOwner(WorldFrame, "ANCHOR_NONE", 100, 100);
	DiscordLib_Tooltip:SetAction(id);
	local name, rank = "";
	if (DiscordLib_TooltipTextLeft1:IsShown()) then
		name = DiscordLib_TooltipTextLeft1:GetText();
	end
	if (DiscordLib_TooltipTextRight1:IsShown()) then
		rank = DiscordLib_TooltipTextRight1:GetText();
	end
	if (ranktoggle) then
		return name, rank;		
	else
		if (not rank) then
			return name;
		else
			return name.." ("..rank..")";
		end
	end
end

function DL_Get_Backdrop(index)
	-- returns bgtexture, bordertexture, tile, tileSize, edgeSize, left, right, top, bottom
	if (index == 1) then
		return "Interface\\AddOns\\DiscordLibrary\\PlainBackdrop", "Interface\\AddOns\\DiscordLibrary\\PlainBackdrop",  true, 8, 1, 1, 1, 1, 1;
	elseif (index == 2) then
		return "Interface\\AddOns\\DiscordLibrary\\PlainBackdrop", "Interface\\Tooltips\\UI-Tooltip-Border", true, 16, 16, 5, 5, 5, 5;
	elseif (index == 3) then
		return "Interface\\AddOns\\DiscordLibrary\\PlainBackdrop", "Interface\\DialogFrame\\UI-DialogBox-Border", true, 32, 32, 11, 12, 12, 11;
	elseif (index == 4) then
		return "Interface\\AddOns\\DiscordLibrary\\PlainBackdrop", "Interface\\Buttons\\UI-SliderBar-Border", true, 8, 8, 3, 3, 6, 6;
	elseif (index == 5) then
		return "Interface\\AddOns\\DiscordLibrary\\PlainBackdrop", "Interface\\Glues\\Common\\Glue-Tooltip-Border", true, 16, 16, 10, 5, 4, 9;
	end
end

function DL_Get_Health(unit)
	local health;
	if (unit == "target" and MobHealth_GetTargetCurHP) then
		health = MobHealth_GetTargetCurHP();
		if ((not health) or health == 0) then health = UnitHealth(unit); end
	elseif (string.find(unit, "target") and MobHealth_PPP) then
		if (UnitName(unit) and UnitLevel(unit)) then
			local mhindex = UnitName(unit)..":"..UnitLevel(unit);
			local ppp = MobHealth_PPP(mhindex);
			health = math.floor(UnitHealth(unit) * ppp + 0.5);
		end
		if (health == 0 or (not health)) then health = UnitHealth(unit); end
	else
		health = UnitHealth(unit);
	end
	return health;
end

function DL_Get_HealthMax(unit)
	local healthmax;
	if (unit == "target" and MobHealth_GetTargetMaxHP and UnitHealth(unit) > 0) then
		healthmax = MobHealth_GetTargetMaxHP();
		if ((not healthmax) or healthmax == 0) then healthmax = UnitHealthMax(unit); end
	elseif (string.find(unit, "target") and MobHealth_PPP) then
		if (UnitName(unit) and UnitLevel(unit)) then
			local mhindex = UnitName(unit)..":"..UnitLevel(unit);
			local ppp = MobHealth_PPP(mhindex);
			healthmax = math.floor(100 * ppp + 0.5);
		end
		if (healthmax == 0 or (not healthmax)) then healthmax = UnitHealthMax(unit); end
	else
		healthmax = UnitHealthMax(unit);
	end
	if (not healthmax) then healthmax = 0; end
	return healthmax;
end

function DL_Get_HexColor(r, g, b)
	return "|cFF"..string.format("%02X%02X%02X", r * 255.0, g * 255.0, b * 255.0);
end

function DL_Get_ItemName(bag, slot)
	local itemname, itemlink = "";
	if (slot) then
		itemlink = GetContainerItemLink(bag,slot);
	else
		itemlink = GetInventoryItemLink("player", bag);
	end
	if (itemlink) then
		itemname = string.sub(itemlink, string.find(itemlink, "[", 1, true) + 1, string.find(itemlink, "]", 1, true) - 1);
	end
	return itemname;
end

function DL_Get_MenuText(menu, value)
	for _, option in menu do
		if (option.value == value) then
			return option.text;
		end
	end
	return "";
end

function DL_Get_LocalKBText(name, prefix, shorten)
	if (not prefix) then
		prefix = "KEY_";
	end
	local tempName = name;
	local i = strfind(name, "-");
	local dashIndex = nil;
	while ( i ) do
		if ( not dashIndex ) then
			dashIndex = i;
		else
			dashIndex = dashIndex + i;
		end
		tempName = strsub(tempName, i + 1);
		i = strfind(tempName, "-");
	end

	local modKeys = '';
	if ( not dashIndex ) then
		dashIndex = 0;
	else
		modKeys = strsub(name, 1, dashIndex);
	end

	local variablePrefix = prefix;
	if ( not variablePrefix ) then
		variablePrefix = "";
	end
	local localizedName = nil;
	if ( IsMacClient() ) then
		-- see if there is a mac specific name for the key
		localizedName = getglobal(variablePrefix..tempName.."_MAC");
	end
	if ( not localizedName ) then
		localizedName = getglobal(variablePrefix..tempName);
	end
	if ( not localizedName ) then
		localizedName = tempName;
	end

	local kbtext = modKeys..localizedName;
	if (shorten) then
		kbtext = string.upper(kbtext);
		kbtext = string.gsub(kbtext, "SHIFT", "S");
		kbtext = string.gsub(kbtext, "CTRL", "C");
		kbtext = string.gsub(kbtext, "ALT", "A");
		kbtext = string.gsub(kbtext, "MOUSE BUTTON", "MB");
		kbtext = string.gsub(kbtext, "BUTTON1", "MB1");
		kbtext = string.gsub(kbtext, "BUTTON2", "MB2");
		kbtext = string.gsub(kbtext, "BUTTON3", "MB3");
		kbtext = string.gsub(kbtext, "BUTTON4", "MB4");
		kbtext = string.gsub(kbtext, "BUTTON5", "MB5");
		kbtext = string.gsub(kbtext, "MIDDLE MOUSE", "MM");
		kbtext = string.gsub(kbtext, "NUM PAD", "NP");
	end

	return kbtext;
end

function DL_Get_KeybindingText(name, prefix, shorten)
	local kbtext, kbtext2 = GetBindingKey(name);
	if ( not kbtext ) then
		kbtext = "";
	else
		kbtext = DL_Get_LocalKBText(kbtext, prefix, shorten);
	end
	if ( not kbtext2 ) then
		kbtext2 = "";
	else
		kbtext2 = DL_Get_LocalKBText(kbtext2, prefix, shorten);
	end
	return kbtext, kbtext2;
end

function DL_Get_Offsets(frame, baseframe, attachpoint, attachto)
	local x1, y1 = DL_Get_Position(frame, attachpoint);
	local x2, y2 = DL_Get_Position(baseframe, attachto);
	local xoffset, yoffset;
	if (x1 and x2 and y1 and y2) then
		local es1, es2 = 1, 1;
		if (baseframe.GetEffectiveScale) then
			es1 = baseframe:GetEffectiveScale();
		end
		if (frame.GetEffectiveScale) then
			es2 = frame:GetEffectiveScale();
		end
		xoffset = DL_round(x1 - x2 * es1 / es2, 2);
		yoffset = DL_round(y1 - y2 * es1 / es2, 2);
	end
	return xoffset, yoffset;
end

function DL_Get_Position(frame, attach)
	if (frame.GetCenter and (not frame:GetCenter())) then
		return;
	end
	local x, y;
	if (attach == "TOPLEFT") then
		x = frame:GetLeft();
		y = frame:GetTop();
	elseif (attach == "TOP") then
		x = frame:GetCenter();
		y = frame:GetTop();
	elseif (attach == "TOPRIGHT") then
		x = frame:GetRight();
		y = frame:GetTop();
	elseif (attach == "LEFT") then
		x = frame:GetLeft();
		_, y = frame:GetCenter();
	elseif (attach == "CENTER") then
		x, y = frame:GetCenter();
	elseif (attach == "RIGHT") then
		x = frame:GetRight();
		_, y = frame:GetCenter();
	elseif (attach == "BOTTOMLEFT") then
		x = frame:GetLeft();
		y = frame:GetBottom();
	elseif (attach == "BOTTOM") then
		x = frame:GetCenter();
		y = frame:GetBottom();
	elseif (attach == "BOTTOMRIGHT") then
		x = frame:GetRight();
		y = frame:GetBottom();
	end
	x = DL_round(x, 2);
	y = DL_round(y, 2);
	return x, y;
end

function DL_Get_ShapeshiftForm()
	for i=1,GetNumShapeshiftForms() do
		local _, _, isActive = GetShapeshiftFormInfo(i);
		if isActive == 1 then return i; end
	end
	return 0;
end

function DL_Hook(func, prefix)
	setglobal(prefix.."_Old_"..func, getglobal(func));
	setglobal(func, getglobal(prefix.."_"..func));
end

function DL_Init_AnchorFramesList()
	DL_ANCHOR_FRAMES = {};
	DL_ANCHOR_FRAMES[1] = { text = "UIParent", value="UIParent" };
end

function DL_Init_CheckBox(frame, setting)
	if (setting) then
		frame:SetChecked(1);
	else
		frame:SetChecked(0);
	end
end

function DL_Init_ColorPicker(frame, color)
	if (not color) then
		color = {r=1, g=1, b=1};
	end
	frame:SetBackdropColor(color.r, color.g, color.b);
end

function DL_Init_EditBox(frame, text)
	if (not text) then
		text = "";
	end
	frame.prevvalue = text;
	frame:SetText(text);
end

function DL_Init_FontsList()
	DL_FONTS_LIST = {};
	DL_FONTS_LIST[1] = { text="Fonts\\FRIZQT__.TTF", value="Fonts\\FRIZQT__.TTF", desc="Fonts\\FRIZQT__.TTF" };
	DL_FONTS_LIST[2] = { text="Fonts\\ARIALN.TTF", value="Fonts\\ARIALN.TTF", desc="Fonts\\ARIALN.TTF" };
	DL_FONTS_LIST[3] = { text="Fonts\\skurri.ttf", value="Fonts\\skurri.ttf", desc="Fonts\\skurri.ttf" };
	DL_FONTS_LIST[4] = { text="Fonts\\MORPHEUS.ttf", value="Fonts\\MORPHEUS.ttf", desc="Fonts\\MORPHEUS.ttf" };
end

function DL_Init_MenuControl(frame, setting, override)
	local text;
	for _, value in getglobal(frame.table) do
		if (value.value == setting) then
			text = value.text;
			break;
		end
	end
	if ((not text) and frame.addoptions) then
		for _, value in frame.addoptions do
			if (value.value == setting) then
				text = value.text;
				break;
			end
		end
	end
	if (not text) then
		if (override) then
			text = setting;
			if (not text) then
				text = "";
			end
		else
			text = "";
		end
	end
	getglobal(frame:GetName().."_Setting"):SetText(text);
end

function DL_Init_Slider(frame, setting)
	if (string.find(frame:GetName(), "DFM") and (not setting)) then
		getglobal(frame:GetName().."_Display"):SetText("");
		return;
	end
	if (frame.scale) then
		setting = setting * frame.scale;
	end
	local min, max = frame:GetMinMaxValues();
	getglobal(frame:GetName().."_Display"):SetText(setting);
	if (setting < min or setting > max) then
		return;
	end
	frame:SetValue(setting);
end

function DL_Init_TabButton(text, selected, func, checked)
	getglobal(this:GetName().."_Border"):SetVertexColor(.4, 0, 0);
	getglobal(this:GetName().."_Text"):SetText(text);
	this.selected = selected;
	this.clickFunc = func;
	if (checked) then
		getglobal(this:GetName().."_Text"):SetTextColor(1, 0, 0);
		getglobal(this:GetName().."_Background"):SetVertexColor(1, 1, 0);
		if (selected) then
			setglobal(selected, this:GetName());
		end
	else
		getglobal(this:GetName().."_Background"):SetVertexColor(0, 0, 0);
	end
end

function DL_Init_UnitsList()
	DL_UNITS_LIST = {};
	DL_UNITS_LIST[1] = {text=""};
	DL_UNITS_LIST[2] = {text="player (you)", value="player"};
	DL_UNITS_LIST[3] = {text="pet", value="pet"};
	DL_UNITS_LIST[4] = {text="target", value="target"};
	for i=1,4 do
		DL_UNITS_LIST[i + 4] = {text="party"..i, value="party"..i};
		DL_UNITS_LIST[i + 8] = {text="partypet"..i, value="partypet"..i};
	end
	for i=1,40 do
		DL_UNITS_LIST[i + 12] = {text="raid"..i, value="raid"..i};
	end
end

function DL_IsMouseOver(frame)
	local lowx = frame:GetLeft();
	local highx = frame:GetRight();
	local lowy = frame:GetBottom();
	local highy = frame:GetTop();
	if (not lowx or not highx or not lowy or not highy) then return; end
	local x, y = GetCursorPosition();
	x = x  / UIParent:GetScale();
	y = y / UIParent:GetScale();
	if (x < lowx or x > highx) then
		return nil;
	end
	if (y < lowy or y > highy) then
		return nil;
	end
	return true;
end

function DL_Layout_VerticalFrames(base, num, onClick)
	local button, prev;
	if (onClick) then
		getglobal(base..1):SetScript("OnClick", onClick);
	end
	for i=2, num do
		button = getglobal(base..i);
		prev = getglobal(base..(i - 1));
		button:ClearAllPoints();
		button:SetPoint("TOP", prev, "BOTTOM", 0, 0);
		if (onClick) then
			button:SetScript("OnClick", onClick);
		end
	end
end

function DL_Layout_Menu(menu, clickFunc)
	local button, button2;
	if (clickFunc) then
		getglobal(menu.."_Option1"):SetScript("OnClick", clickFunc);
	end
	for i=2, getglobal(menu).count do
		button = getglobal(menu.."_Option"..i);
		button2 = menu.."_Option"..(i - 1);
		button:ClearAllPoints();
		button:SetPoint("TOPLEFT", button2, "BOTTOMLEFT", 0, 0);
		if (clickFunc) then
			button:SetScript("OnClick", clickFunc);
		end
	end
end

function DL_Layout_ScrollButtons(base, num, onClick)
	local button, prev;
	if (onClick) then
		getglobal(base..1):SetScript("OnClick", onClick);
		getglobal(base.."1Highlight"):SetVertexColor(1, 0, 1, 1);
		getglobal(base.."1Checked"):SetVertexColor(.7, .7, 0, 1);
	end
	for i=2, num do
		button = getglobal(base..i);
		prev = getglobal(base..(i - 1));
		button:ClearAllPoints();
		button:SetPoint("TOP", prev, "BOTTOM", 0, 0);
		if (onClick) then
			button:SetScript("OnClick", onClick);
			getglobal(base..i.."Highlight"):SetVertexColor(1, 0, 1, 1);
			getglobal(base..i.."Checked"):SetVertexColor(.7, .7, 0, 1);
		end
	end
end

function DL_Menu_Timeout(elapsed)
	if (this.timer) then
		this.timer = this.timer - elapsed;
		if (this.timer < 0) then
			this.timer = nil;
			this:Hide();
		end
	end
end

function DL_NudgeButton_OnLoad()
	if (this:GetID() == 0) then
		this:SetText(0);
	elseif (this:GetID() == 1) then
		this:SetText("^");
	elseif (this:GetID() == 2) then
		this:SetText("v");
	elseif (this:GetID() == 3) then
		this:SetText("<");
	elseif (this:GetID() == 4) then
		this:SetText(">");
	elseif (this:GetID() == 5) then
		this:SetText("-");
	elseif (this:GetID() == 6) then
		this:SetText("+");
	elseif (this:GetID() == 7) then
		this:SetText("R");
	elseif (this:GetID() == 8) then
		this:SetText("L");
	end
	this:RegisterForClicks("LeftButtonUp", "RightButtonUp", "MiddleButtonUp");
end

function DL_PetAttack()
	if (DL_PETATTACKING) then return; end
	if (not UnitName("target")) then return; end
	if (not UnitCanAttack("pet", "target")) then return; end
	PetAttack();
end

function DL_round(number, precision)
	if (precision > 0) then
		number = number * 10 ^ precision;
	end
	local remainder = number  - math.floor(number);
	if (remainder >= .5) then
		number = math.floor(number) + 1;
	else
		number = math.floor(number);
	end
	if (precision > 0) then
		number = number / 10 ^ precision;
	end
	return number;
end

function DL_ScriptErrors_OK_OnClick()
	if (DL_ERRORS_QUEUE[1]) then
		DL_ScriptErrors_Message:SetText(DL_ERRORS_QUEUE[1]);
		table.remove(DL_ERRORS_QUEUE, 1);
	else
		DL_ScriptErrors:Hide();
	end
end

function DL_Set_Anchor(subframe, xoffset, yoffset, attachpoint, attachto, center, frame)
	if (not frame) then
		frame = this;
	end
	if (not attachpoint) then
		attachpoint = "TOPLEFT";
	end
	if (not attachto) then
		attachto = "BOTTOMLEFT";
	end
	xoffset = getglobal(frame:GetName().."_Label"):GetWidth() + xoffset + 5;
	if (center) then
		xoffset = xoffset / 2;
	end
	frame:ClearAllPoints();
	frame:SetPoint(attachpoint, frame:GetParent():GetName()..subframe, attachto, xoffset, yoffset);
end

function DL_Set_Label(text)
	getglobal(this:GetName().."_Label"):SetText(text);
end

function DL_Set_OptionsTitle(frame, texture, version)
	getglobal(frame.."_TitleFrame_Title"):SetTexture("Interface\\AddOns\\"..texture);
	getglobal(frame.."_TitleFrame_Title"):SetAlpha(1);
	getglobal(frame.."_TitleFrame_Version"):SetText("v"..version);
end

function DL_Set_Timer(id, seconds)
	if (not DL_TIMERS) then DL_TIMERS = {}; end
	DL_TIMERS[id] = seconds;
end

function DL_Show_Menu(toggle)
	if (toggle) then
		this = this:GetParent();
	end
	local menu = getglobal(this.menu);
	if (menu:IsVisible()) then
		menu:Hide();
		DL_CleanUp_TempOptions(menu);
		return;
	end
	if (this.addoptions) then
		menu.addedoptions = {};
		for i,v in this.addoptions do
			tinsert(getglobal(this.table), v);
			menu.addedoptions[table.getn(getglobal(this.table))] = 1;
		end
	else
		menu.addedoptions = nil;
	end
	if (not menu.scrollMenu) then
		local count = 0;
		local widest = 0;
		for _, value in getglobal(this.table) do
			count = count + 1;
			getglobal(this.menu.."_Option"..count):Show();
			getglobal(this.menu.."_Option"..count.."_Text"):SetText(value.text);
			getglobal(this.menu.."_Option"..count).value = value.value;
			getglobal(this.menu.."_Option"..count).desc = value.desc;
			local width = getglobal(this.menu.."_Option"..count.."_Text"):GetWidth();
			if (width > widest) then
				widest = width;
			end
		end
		for i=1, menu.count do
			if (i <= count) then
				getglobal(this.menu.."_Option"..i):SetWidth(widest);
			else
				getglobal(this.menu.."_Option"..i):Hide();			
			end
		end
		menu:SetWidth(widest + 20);
		menu:SetHeight(count * 15 + 20);
	end
	menu:ClearAllPoints();
	local frame = this:GetName();
	menu:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, 0);
	if (menu:GetBottom() < UIParent:GetBottom()) then
		local yoffset = UIParent:GetBottom() - menu:GetBottom();
		menu:ClearAllPoints();
		menu:SetPoint("TOPLEFT", this:GetName().."_Setting", "BOTTOMLEFT", 0, yoffset);
	end
	menu:Show();
	menu.table = this.table;
	menu.index = this.index;
	menu.subindex = this.subindex;
	menu.subindex2 = this.subindex2;
	menu.miscindex = this.miscindex;
	menu.initFunc = this.initFunc;
	menu.settingType = this.settingType;
	menu.controlbox = this:GetName().."_Setting";
	menu.cboxbase = this:GetName();
	if (menu.scrollMenu) then
		for i=1, 10 do
			getglobal(menu:GetName().."_Button"..i):SetChecked(0);
		end
		this = getglobal(menu:GetName().."_ScrollFrame");
		getglobal(menu:GetName().."_ScrollFrameScrollBar"):SetValue(0);
		getglobal(menu:GetName().."_ScrollFrame").offset = 0;
		menu.updateFunc();
	end
end

function DL_Sort(origTable)
	local function sortf(a, b)
		if ( strlower(a)<strlower(b) ) then
			return true;
		elseif ( strlower(a)==strlower(b) and a<b ) then
			return true;
		end
	end
	table.sort(origTable, sortf);
end

function DL_SortKeys(origTable)
	local a = {};
	for n in pairs(origTable) do
		table.insert(a, n);
	end
	DL_Sort(a, sortf);
	return a;
end

function DL_TabButton_OnClick()
	local selected = getglobal(this.selected);
	if (selected) then
		getglobal(selected):SetChecked(0);
		getglobal(selected.."_Background"):SetVertexColor(0, 0, 0);
		getglobal(selected.."_Text"):SetTextColor(1, 1, 0);
	end
	this:SetChecked(1);
	getglobal(this:GetName().."_Background"):SetVertexColor(1, 1, 0);
	getglobal(this:GetName().."_Text"):SetTextColor(1, 0, 0);
	if (this.selected) then
		setglobal(this.selected, this:GetName());
	end
	if (this.clickFunc) then
		this.clickFunc(this:GetID());
	end
end

function DL_Update_Auras(unit)
	if (DiscordLibrary_Settings and DiscordLibrary_Settings.disableBuffChecking) then
		return;
	end
	if (not DL_BUFFS[unit]) then
		DL_BUFFS[unit] = {};
	end
	if (not DL_DEBUFFS[unit]) then
		DL_DEBUFFS[unit] = {};
	end	
	if (not DL_STATUS[unit]) then
		DL_STATUS[unit] = {};
	end
	if ((not UnitExists(unit)) or (not UnitName(unit))) then return; end
	for i=1,16 do
		if (UnitBuff(unit, i)) then
			DiscordLib_AuraTooltip:SetUnitBuff(unit, i);
			if (DiscordLib_AuraTooltipTextLeft1:IsShown()) then
				DL_BUFFS[unit][i] = DiscordLib_AuraTooltipTextLeft1:GetText();
			else
				DL_BUFFS[unit][i] = "";
			end
		else
			DL_BUFFS[unit][i] = "";
		end
		if (UnitDebuff(unit, i)) then
			DiscordLib_AuraTooltip:SetUnitDebuff(unit, i);
			if (DiscordLib_AuraTooltipTextLeft1:IsShown()) then
				DL_DEBUFFS[unit][i] = DiscordLib_AuraTooltipTextLeft1:GetText();
			else
				DL_DEBUFFS[unit][i] = "";
			end
			if (DiscordLib_AuraTooltipTextRight1:IsShown()) then
				DL_STATUS[unit][i] = DiscordLib_AuraTooltipTextRight1:GetText();
			else
				DL_STATUS[unit][i] = "";
			end
		else
			DL_DEBUFFS[unit][i] = "";
			DL_STATUS[unit][i] = "";
		end
	end
end

function DL_Update_Forms()
	DL_FORMS = {};
	local _, class = UnitClass("player");
	if (class ~= "WARRIOR") then
		DL_FORMS[0] = { text=DL_TEXT.Humanoid, value=0 };
	end
	for i=1,GetNumShapeshiftForms() do
		local _, name = GetShapeshiftFormInfo(i);
		DL_FORMS[i] = { text=name, value=i };
	end
end

function DL_Update_Timers(elapsed)
	if (not DL_TIMERS) then return; end
	for k,v in DL_TIMERS do
		DL_TIMERS[k] = v - elapsed;
	end
end

function UseAction(action, checkcursor, selfcast)
	DL_LAST_ACTION = action;
	DL_Old_UseAction(action, checkcursor, selfcast);
end

function DL_Update_ActionList()
	DL_ACTIONS = {};
	for i=1, 120 do
		DL_ACTIONS[i] = { text="["..i.."] "..DL_Get_ActionName(i), value=i };
	end
end