--Cache global variables
local _G = _G
local date = date
local error = error
local pairs = pairs
local select = select
local tonumber = tonumber
local type = type
local unpack = unpack
local find, format, gmatch, gsub, len, lower, match, upper, sub = string.find, string.format, string.gmatch, string.gsub, string.len, string.lower, string.match, string.upper, string.sub
local getn = table.getn
--WoW API
local debugstack = debugstack
local GetContainerItemInfo = GetContainerItemInfo
local GetContainerItemLink = GetContainerItemLink
local GetContainerNumSlots = GetContainerNumSlots
local GetInventoryItemTexture = GetInventoryItemTexture
local GetItemInfo = GetItemInfo
local GetShapeshiftFormInfo = GetShapeshiftFormInfo
local GetQuestGreenRange = GetQuestGreenRange
local GetRealZoneText = GetRealZoneText
local IsInInstance = IsInInstance
--local UnitBuff = UnitBuff
--local UnitDebuff = UnitDebuff
local UnitLevel = UnitLevel
--WoW Variables
local DUNGEON_DIFFICULTY1 = DUNGEON_DIFFICULTY1
local DURABILITY_TEMPLATE = gsub(DURABILITY_TEMPLATE, "%%d / %%d", "(%%d+) / (%%d+)")
local NUM_BAG_FRAMES = NUM_BAG_FRAMES
local TIMEMANAGER_AM = gsub(TIME_TWELVEHOURAM, "^.-(%w+)$", "%1")
local TIMEMANAGER_PM = gsub(TIME_TWELVEHOURPM, "^.-(%w+)$", "%1")
--Libs
local LBC = LibStub("LibBabble-Class-3.0"):GetLookupTable()
local LBZ = LibStub("LibBabble-Zone-3.0"):GetLookupTable()
local IFDB = LibStub("ItemFamilyDB")
local QIS = LibStub("QuestItemStarterDB")

CLASS_SORT_ORDER = {
	"WARRIOR",
	"PALADIN",
	"PRIEST",
	"SHAMAN",
	"DRUID",
	"ROGUE",
	"MAGE",
	"WARLOCK",
	"HUNTER"
}
MAX_CLASSES = getn(CLASS_SORT_ORDER)

LOCALIZED_CLASS_NAMES_MALE = {}
LOCALIZED_CLASS_NAMES_FEMALE = {}

CLASS_ICON_TCOORDS = {
	["WARRIOR"] = {0, 0.25, 0, 0.25},
	["MAGE"] = {0.25, 0.49609375, 0, 0.25},
	["ROGUE"] = {0.49609375, 0.7421875, 0, 0.25},
	["DRUID"] = {0.7421875, 0.98828125, 0, 0.25},
	["HUNTER"] = {0, 0.25, 0.25, 0.5},
	["SHAMAN"] = {0.25, 0.49609375, 0.25, 0.5},
	["PRIEST"] = {0.49609375, 0.7421875, 0.25, 0.5},
	["WARLOCK"] = {0.7421875, 0.98828125, 0.25, 0.5},
	["PALADIN"] = {0, 0.25, 0.5, 0.75}
}

RAID_CLASS_COLORS = {
	["HUNTER"] = { r = 0.67, g = 0.83, b = 0.45, colorStr = "ffabd473" },
	["WARLOCK"] = { r = 0.53, g = 0.53, b = 0.93, colorStr = "ff8788ee" },
	["PRIEST"] = { r = 1.0, g = 1.0, b = 1.0, colorStr = "ffffffff" },
	["PALADIN"] = { r = 0.96, g = 0.55, b = 0.73, colorStr = "fff58cba" },
	["MAGE"] = { r = 0.25, g = 0.78, b = 0.92, colorStr = "ff3fc7eb" },
	["ROGUE"] = { r = 1.0, g = 0.96, b = 0.41, colorStr = "fffff569" },
	["DRUID"] = { r = 1.0, g = 0.49, b = 0.04, colorStr = "ffff7d0a" },
	["SHAMAN"] = { r = 0.0, g = 0.44, b = 0.87, colorStr = "ff0070de" },
	["WARRIOR"] = { r = 0.78, g = 0.61, b = 0.43, colorStr = "ffc79c6e" },
}

QuestDifficultyColors = {
	["impossible"] = {r = 1.00, g = 0.10, b = 0.10},
	["verydifficult"] = {r = 1.00, g = 0.50, b = 0.25},
	["difficult"] = {r = 1.00, g = 1.00, b = 0.00},
	["standard"] = {r = 0.25, g = 0.75, b = 0.25},
	["trivial"] = {r = 0.50, g = 0.50, b = 0.50},
	["header"] = {r = 0.70, g = 0.70, b = 0.70}
}

function HookScript(frame, scriptName, handler)
	if not (type(frame) == "table" and frame.GetScript and type(scriptName) == "string" and type(handler) == "function") then
		error("Usage: HookScript(frame, \"type\", function)", 2)
	end

	local original_scipt = frame:GetScript(scriptName)
	if original_scipt then
		frame:SetScript(scriptName, function()
			original_scipt(this)
			handler(this)
		end)
	else
		frame:SetScript(scriptName, handler)
	end
end

function hooksecurefunc(a1, a2, a3)
	local isMethod = type(a1) == "table" and type(a2) == "string" and type(a1[a2]) == "function" and type(a3) == "function"
	if not (isMethod or (type(a1) == "string" and type(_G[a1]) == "function" and type(a2) == "function")) then
		error("Usage: hooksecurefunc([table,] \"functionName\", hookfunc)", 2)
	end

	if not isMethod then
		a1, a2, a3 = _G, a1, a2
	end

	local original_func = a1[a2]

	a1[a2] = function(...)
		local original_return = {original_func(unpack(arg))}
		a3(unpack(arg))

		return unpack(original_return)
	end
end

local secureFunctions = {
	["CameraOrSelectOrMoveStop"] = true,
	["MoveBackwardStop"] = true,
	["MoveForwardStop"] = true,
	["PitchDownStop"] = true,
	["PitchUpStop"] = true,
	["StrafeLeftStart"] = true,
	["StrafeRightStart"] = true,
	["ToggleMouseMove"] = true,
	["TurnLeftStart"] = true,
	["TurnOrActionStop"] = true,
	["TurnRightStart"] = true,
	["CameraOrSelectOrMoveStart"] = true,
	["Jump"] = true,
	["MoveBackwardStart"] = true,
	["MoveForwardStart"] = true,
	["PitchDownStart"] = true,
	["PitchUpStart"] = true,
	["StrafeLeftStop"] = true,
	["StrafeRightStop"] = true,
	["ToggleRun"] = true,
	["TurnLeftStop"] = true,
	["TurnOrActionStart"] = true,
	["TurnRightStop"] = true
}
local secureScripts = {}
function issecurevariable(a1, a2)
--	local isMethod = type(a1) == "table" and type(a2) == "string" and type(a1[a2]) == "function"
--	if not (isMethod or (type(a1) == "string" and type(_G[a1]) == "function") then
--		error("Usage: issecurevariable([table,] \"variable\")", 2)
--	end
--
--	local isSecure
--	if isMethod then
--		isSecure = secureScripts[a2] and 1
--	else
--		isSecure = secureFunctions[a1] and 1
--	end
--
--	return isSecure

	if type(a1) == "table" then return end

	if type(a1) ~= "string" then
		error("Usage: issecurevariable([table,] \"variable\")", 2)
	end

	return secureFunctions[a1] and 1
end

function issecure()
	return
end

function securecall(f, ...)
	if arg.n > 0 then
		_G[f](unpack(arg))
	else
		_G[f]()
	end
end

function tContains(table, item)
	local index = 1

	while table[index] do
		if item == table[index] then
			return 1
		end
		index = index + 1
	end

	return
end

function UnitAura(unit, i, filter)
	if not ((type(unit) == "string" or type(unit) == "number") and (type(i) == "string" or type(i) == "number")) then
		error("Usage: UnitAura(\"unit\", index [, filter])", 2)
	end

	if not filter or match(filter, "(HELPFUL)") then
		local texture, count = UnitBuff(unit, i, filter)
		return texture, count
	else
		local texture, count, dType = UnitDebuff(unit, i, filter)
		return texture, count, dType
	end
end

function difftime(time2, time1)
	if type(time2) ~= "number" then
		error(format("bad argument #1 to 'difftime' (number expected, got %s)", time2 ~= nil and type(time2) or "no value"), 2)
	elseif time1 and type(time1) ~= "number" then
		error(format("bad argument #2 to 'difftime' (number expected, got %s)", time1 ~= nil and type(time1) or "no value"), 2)
	end

	return time1 and time2 - time1 or time2
end

function BetterDate(formatString, timeVal)
	local dateTable = date("*t", timeVal)
	local amString = (dateTable.hour >= 12) and "PM" or "AM"

	--First, we'll replace %p with the appropriate AM or PM.
	formatString = gsub(formatString, "^%%p", amString)	--Replaces %p at the beginning of the string with the am/pm token
	formatString = gsub(formatString, "([^%%])%%p", "%1"..amString) -- Replaces %p anywhere else in the string, but doesn't replace %%p (since the first % escapes the second)

	return date(formatString, timeVal)
end

function GetQuestDifficultyColor(level)
	local levelDiff = level - UnitLevel("player")
	if levelDiff >= 5 then
		return QuestDifficultyColors["impossible"]
	elseif levelDiff >= 3 then
		return QuestDifficultyColors["verydifficult"]
	elseif levelDiff >= -2 then
		return QuestDifficultyColors["difficult"]
	elseif -levelDiff <= GetQuestGreenRange() then
		return QuestDifficultyColors["standard"]
	else
		return QuestDifficultyColors["trivial"]
	end
end

function FillLocalizedClassList(tab, female)
	if type(tab) ~= "table" then
		error("Usage: FillLocalizedClassList(classTable[, isFemale])", 2)
	end

	for _, engClass in ipairs(CLASS_SORT_ORDER) do
		if female then
			tab[engClass] = LBC[engClass]
		else
			tab[engClass] = LBC[gsub(lower(engClass), "^%l", upper)]
		end
	end

	return true
end

FillLocalizedClassList(LOCALIZED_CLASS_NAMES_MALE)
FillLocalizedClassList(LOCALIZED_CLASS_NAMES_FEMALE, true)

local zoneInfo = {
	-- Battlegrounds
	[LBZ["Warsong Gulch"]] = {mapID = 443, maxPlayers = 10},
	[LBZ["Arathi Basin"]] = {mapID = 461, maxPlayers = 15},
	[LBZ["Alterac Valley"]] = {mapID = 401, maxPlayers = 40},

	-- Raids
	[LBZ["Zul'Gurub"]] = {mapID = 309, maxPlayers = 20},
	[LBZ["Onyxia's Lair"]] = {mapID = 249, maxPlayers = 40},
	[LBZ["Molten Core"]] = {mapID = 409, maxPlayers = 40},
	[LBZ["Ruins of Ahn'Qiraj"]] = {mapID = 509, maxPlayers = 20},
	[LBZ["Temple of Ahn'Qiraj"]] = {mapID = 531, maxPlayers = 40},
	[LBZ["Blackwing Lair"]] = {mapID = 469, maxPlayers = 40},
	[LBZ["Naxxramas"]] = {mapID = 533, maxPlayers = 40},
}

local mapByID = {}
for mapName in pairs(zoneInfo) do
	mapByID[zoneInfo[mapName].mapID] = mapName
end

local function GetMaxPlayersByType(instanceType, zoneName)
	if instanceType == "none" then
		return 40
	elseif instanceType == "party" then
		return 5
	elseif zoneName ~= "" and zoneInfo[zoneName] then
		if instanceType == "pvp" then
			return zoneInfo[zoneName].maxPlayers
		elseif instanceType == "raid" then
			return zoneInfo[zoneName].maxPlayers
		end
	else
		return 0
	end
end

function GetInstanceInfo()
	local inInstance, instanceType = IsInInstance()
	if not inInstance then return end

	local name = GetRealZoneText()

	local difficulty = 1
	local maxPlayers = GetMaxPlayersByType(instanceType, name)
	local difficultyName = format("%d %s", maxPlayers, DUNGEON_DIFFICULTY1)

	return name, instanceType, difficulty, difficultyName, maxPlayers
end

function GetCurrentMapAreaID()
	if not IsInInstance() then return end
	local zoneName = GetRealZoneText()

	return zoneInfo[zoneName] and zoneInfo[zoneName].mapID or 0
end

function GetMapNameByID(id)
	if not (type(id) == "string" or type(id) == "number") then
		error(format("Bad argument #1 to \"GetMapNameByID\" (number expected, got %s)", id ~= nil and type(id) or "no value"), 2)
	end

	return mapByID[tonumber(id)]
end

local arrow
function GetPlayerFacing()
	if not arrow then
		local obj = Minimap
		for i = 1, obj:GetNumChildren() do
			local child = select(i, obj:GetChildren())
			if child and child.GetModel and child:GetModel() == "Interface\\Minimap\\MinimapArrow" then
				arrow = child
				break
			end
		end
	end

	return arrow and arrow:GetFacing()
end

function ToggleFrame(frame)
	if frame:IsShown() then
		HideUIPanel(frame)
	else
		ShowUIPanel(frame)
	end
end

local function OnOrientationChanged(self, orientation)
	self.texturePointer.verticalOrientation = orientation == "VERTICAL"

	if self.texturePointer.verticalOrientation then
		self.texturePointer:SetPoint("BOTTOMLEFT", self)
	else
		self.texturePointer:SetPoint("LEFT", self)
	end
end

local function OnSizeChanged()
	local width, height = this:GetWidth(), this:GetHeight()

	this.texturePointer.width = width
	this.texturePointer.height = height
	this.texturePointer:SetWidth(width)
	this.texturePointer:SetHeight(height)
end

local function OnValueChanged()
	local _, max = this:GetMinMaxValues()

	if this.texturePointer.verticalOrientation then
		this.texturePointer:SetHeight(this.texturePointer.height * (arg1 / max))
	else
		this.texturePointer:SetWidth(this.texturePointer.width * (arg1 / max))
	end
end

function CreateStatusBarTexturePointer(statusbar)
	if type(statusbar) ~= "table" then
		error(format("Bad argument #1 to \"CreateStatusBarTexturePointer\" (table expected, got %s)", statusbar ~= nil and type(statusbar) or "no value"), 2)
	elseif not (statusbar.GetObjectType and statusbar:GetObjectType() == "StatusBar") then
		error("Bad argument #1 to \"CreateStatusBarTexturePointer\" (statusbar object expected)", 2)
	end

	local f = statusbar:CreateTexture()
	f.width = statusbar:GetWidth()
	f.height = statusbar:GetHeight()
	f.vertical = statusbar:GetOrientation() == "VERTICAL"
	f:SetWidth(f.width)
	f:SetHeight(f.height)

	if f.verticalOrientation then
		f:SetPoint("BOTTOMLEFT", statusbar)
	else
		f:SetPoint("LEFT", statusbar)
	end

	statusbar.texturePointer = f

	statusbar:SetScript("OnSizeChanged", OnSizeChanged)
	statusbar:SetScript("OnValueChanged", OnValueChanged)

	hooksecurefunc(statusbar, "SetOrientation", OnOrientationChanged)

	return f
end

local function removeScript(self, script)
	local func = self:GetScript(script)

	if func then
		self:SetScript(script, nil)
	end

	return func
end

local nbsp = string.char(255)
function EditBoxGetCursorPosition(self)
	if self == WowLuaFrameEditBox or self == WowLuaFrameCommandEditBox then return 0 end

	if self:GetText() == "" then return 0 end

	local occ = removeScript(self, "OnCursorChanged")
	local otc = removeScript(self, "OnTextChanged")
	local ots = removeScript(self, "OnTextSet")

	local charsChanged, numeric

	local maxChars = self:GetMaxLetters()
	if maxChars == self:GetNumLetters() then
		self:SetMaxLetters(maxChars + 1)
		charsChanged = true
	end
	if self:IsNumeric() then
		self:SetNumeric(false)
		numeric = true
	end

	self:Insert(nbsp)

	local pos = find(self:GetText(), nbsp)
	if not pos then
		pos = len(self:GetText())
		print(format("CursorPosition position for `%s` not found!", self.GetName and self:GetName() or tostring(self)))
	else
		self:HighlightText(pos - 1, pos)
		self:Insert("")
	end

	if charsChanged then
		self:SetMaxLetters(maxChars)
	end
	if numeric then
		self:SetNumeric(true)
	end

	if occ then self:SetScript("OnCursorChanged", occ) end
	if otc then self:SetScript("OnTextChanged", otc) end
	if ots then self:SetScript("OnTextSet", ots) end

	return pos - 1
end

function EditBoxSetCursorPosition(self, pos)
	if self == WowLuaFrameEditBox or self == WowLuaFrameCommandEditBox then return end

	if self:GetText() == "" then return end

	local occ = removeScript(self, "OnCursorChanged")
	local otc = removeScript(self, "OnTextChanged")
	local ots = removeScript(self, "OnTextSet")

	local text = self:GetText()
	local size = len(text)

	if pos < 0 then
		pos = 0
	elseif pos > size then
		pos = size
	end

	if pos == 0 then
		local charsChanged, numeric

		local maxChars = self:GetMaxLetters()
		if maxChars == self:GetNumLetters() then
			self:SetMaxLetters(maxChars + 1)
			charsChanged = true
		end
		if self:IsNumeric() then
			self:SetNumeric(false)
			numeric = true
		end

		text = sub(text, 0, 1)
		self:HighlightText(0, 1)
		self:Insert(nbsp)
		self:Insert(text)
		self:HighlightText(0, 1)
		self:Insert("")

		if charsChanged then
			self:SetMaxLetters(maxChars)
		end
		if numeric then
			self:SetNumeric(true)
		end
	else
		text = sub(text, pos, pos)
		self:HighlightText(pos - 1, pos)
		self:Insert(text)
	end

	if occ then self:SetScript("OnCursorChanged", occ) end
	if otc then self:SetScript("OnTextChanged", otc) end
	if ots then self:SetScript("OnTextSet", ots) end
end

local threatColors = {
	[0] = {0.69, 0.69, 0.69},
	[1] = {1, 1, 0.47},
	[2] = {1, 0.6, 0},
	[3] = {1, 0, 0}
}

function GetThreatStatusColor(statusIndex)
	if not (type(statusIndex) == "number" and statusIndex >= 0 and statusIndex < 4) then
		statusIndex = 0
	end

	return threatColors[statusIndex][1], threatColors[statusIndex][2], threatColors[statusIndex][3]
end

function GetThreatStatus(currentThreat, maxThreat)
	if type(currentThreat) ~= "number" or type(maxThreat) ~= "number" then
		error("Usage: GetThreatStatus(currentThreat, maxThreat)", 2)
	end

	if not maxThreat or maxThreat == 0 then
		maxThreat = 1
	end

	local threatPercent = currentThreat / maxThreat * 100

	if threatPercent >= 100 then
		return 3, threatPercent
	elseif threatPercent < 100 and threatPercent >= 80 then
		return 2, threatPercent
	elseif threatPercent < 80 and threatPercent >= 50 then
		return 1, threatPercent
	else
		return 0, threatPercent
	end
end

function GetQuestItemStarterInfo(itemLink)
	local isQuestItem, isQuestStarter, invalidQuestItem = false, false, false

	if itemLink then
		local _, _, itemID = find(itemLink, "(%d+):")

		if QIS.QuestItemKeyIDs[itemID] then
			isQuestItem = true
		else
			local _, _, _, _, _, itemType = GetItemInfo(itemID)
			if itemType == "Quest" then
				isQuestItem = true
			end
		end

		if QIS.QuestItemStarterIDs[itemID] then
			isQuestStarter = true
		end

		if QIS.InvalidQuestItemIDs[itemID] then
			invalidQuestItem = true
		end
	end

	return isQuestItem, isQuestStarter, invalidQuestItem
end

local LAST_ITEM_ID = 24283
local itemInfoDB = {}

function GetItemInfoByName(itemName)
	if type(itemName) ~= "string" then
		error("Usage: GetItemInfoByName(itemName)", 2)
	end

	if find(itemName, " of ") then
		-- random enchantments
		itemName = gsub(itemName, " of Spirit", "")
		itemName = gsub(itemName, " of Intellect", "")
		itemName = gsub(itemName, " of Strength", "")
		itemName = gsub(itemName, " of Stamina", "")
		itemName = gsub(itemName, " of Agility", "")
		itemName = gsub(itemName, " of Defense", "")
		itemName = gsub(itemName, " of Nimbleness", "")
		itemName = gsub(itemName, " of Power", "")
		itemName = gsub(itemName, " of Speed", "")
		itemName = gsub(itemName, " of Frozen Wrath", "")
		itemName = gsub(itemName, " of Arcane Wrath", "")
		itemName = gsub(itemName, " of Fiery Wrath", "")
		itemName = gsub(itemName, " of Nature's Wrath", "")
		itemName = gsub(itemName, " of Shadow Wrath", "")
		itemName = gsub(itemName, " of Holy Wrath", "")
		itemName = gsub(itemName, " of Healing", "")
		itemName = gsub(itemName, " of Magic", "")
		itemName = gsub(itemName, " of Concentration", "")
		itemName = gsub(itemName, " of Regeneration", "")
		itemName = gsub(itemName, " of Fire Resistance", "")
		itemName = gsub(itemName, " of Nature Resistance", "")
		itemName = gsub(itemName, " of Arcane Resistance", "")
		itemName = gsub(itemName, " of Frost Resistance", "")
		itemName = gsub(itemName, " of Shadow Resistance", "")
		itemName = gsub(itemName, " of the Tiger", "")
		itemName = gsub(itemName, " of the Bear", "")
		itemName = gsub(itemName, " of the Gorilla", "")
		itemName = gsub(itemName, " of the Boar", "")
		itemName = gsub(itemName, " of the Monkey", "")
		itemName = gsub(itemName, " of the Falcon", "")
		itemName = gsub(itemName, " of the Wolf", "")
		itemName = gsub(itemName, " of the Eagle", "")
		itemName = gsub(itemName, " of the Whale", "")
		itemName = gsub(itemName, " of the Owl", "")
		itemName = gsub(itemName, " of Blocking", "")
		itemName = gsub(itemName, " of Eluding", "")
		itemName = gsub(itemName, " of the Invoker", "")
	end

	if not itemInfoDB[itemName] then
		local name
		for itemID = 1, LAST_ITEM_ID do
			name = GetItemInfo(itemID)

			if name ~= nil and name ~= "" then
				itemInfoDB[name] = itemID

				if name == itemName then
					break
				end
			end
		end
	end

	if not itemInfoDB[itemName] then return end

	return GetItemInfo(itemInfoDB[itemName])
end

function GetItemFamily(item, isBag)
	if not item or (type(item) ~= "number" and type(item) ~= "string") then return end

	if type(item) == "string" then
		local _, _, itemID = find(item, "(%d+):")
		if not itemID then return end
		item = tonumber(itemID)
	end

	if item > LAST_ITEM_ID then return end

	return (isBag and IFDB.BagFamily[item] or IFDB.ItemFamily[item]) or 0
end

function GetItemCount(itemName)
	if type(itemName) ~= "string" then return 0 end

	local count = 0

	for bag = NUM_BAG_FRAMES, 0, -1 do
		for slot = 1, GetContainerNumSlots(bag) do
			local _, itemCount = GetContainerItemInfo(bag, slot)

			if itemCount then
				local itemLink = GetContainerItemLink(bag, slot)
				local _, _, itemID = find(itemLink, "(%d+):")
				local queryName = GetItemInfo(itemID)

				if queryName and queryName == itemName then
					count = count + itemCount
				end
			end
		end
	end

	return count
end

function GetContainerNumFreeSlots(bagID)
	local maxSlots = 0
	local usedSlots = 0

	for bag = bagID, NUM_BAG_FRAMES do
		local bagSize = GetContainerNumSlots(bag)
		maxSlots = maxSlots + bagSize
		for i = 1, bagSize do
			itemLink = GetContainerItemLink(bag, i)
			if itemLink then
				usedSlots = usedSlots + 1
			end
		end
	end

	local freeSlots = maxSlots - usedSlots
	return freeSlots
end

local scanTooltip
function GetInventoryItemDurability(slot)
	if not GetInventoryItemTexture("player", slot) then return end

	if not scanTooltip then
		scanTooltip = CreateFrame("GameTooltip", "Compatibility_ScanTooltip", nil, "ShoppingTooltipTemplate")
		scanTooltip:SetOwner(UIParent, "ANCHOR_NONE")
	end

	scanTooltip:ClearLines()
	scanTooltip:SetInventoryItem("player", slot)

	for i = 4, scanTooltip:NumLines() do
		local text = _G["Compatibility_ScanTooltipTextLeft"..i]:GetText()
		for current, maximum in gmatch(text, DURABILITY_TEMPLATE) do
			return tonumber(current), tonumber(maximum)
		end
	end
end

local slots = {
	["HeadSlot"] = "INVTYPE_HEAD",
	["NeckSlot"] = "INVTYPE_NECK",
	["ShoulderSlot"] = "INVTYPE_SHOULDER",
	["BackSlot"] = "INVTYPE_CLOAK",
	["ChestSlot"] = "INVTYPE_ROBE",
	["WristSlot"] = "INVTYPE_WRIST",
	["HandsSlot"] = "INVTYPE_HAND",
	["WaistSlot"] = "INVTYPE_WAIST",
	["LegsSlot"] = "INVTYPE_LEGS",
	["FeetSlot"] = "INVTYPE_FEET",
	["Finger0Slot"] = "INVTYPE_FINGER",
	["Finger1Slot"] = "INVTYPE_FINGER",
	["Trinket0Slot"] = "INVTYPE_TRINKET",
	["Trinket1Slot"] = "INVTYPE_TRINKET",
	["MainHandSlot"] = "INVTYPE_WEAPONMAINHAND",
	["SecondaryHandSlot"] = "INVTYPE_HOLDABLE",
	["RangedSlot"] = "INVTYPE_RANGEDRIGHT",
}

local bagsTable = {}
function GetAverageItemLevel()
	local itemLink, itemLevel, itemEquipLoc
	local total, totalBag, item, bagItem, isBagItemLevel = 0, 0, 0, 0

	for bag = 0, 4 do
		for slot = 1, GetContainerNumSlots(bag) do
			itemLink = GetContainerItemLink(bag, slot)
			if itemLink then
				_, _, _, itemLevel, _, _, _, _, itemEquipLoc = GetItemInfo(tonumber(string.match(itemLink, "item:(%d+)")))
				if itemEquipLoc and itemEquipLoc ~= "" then
					if not bagsTable[itemEquipLoc] then
						bagsTable[itemEquipLoc] = itemLevel
					else
						if itemLevel > bagsTable[itemEquipLoc] then
							bagsTable[itemEquipLoc] = itemLevel
						end
					end
				end
			end
		end
	end

	for slotName, itemLoc in pairs(slots) do
		itemLink = GetInventoryItemLink("player", GetInventorySlotInfo(slotName))
		if itemLink then
			_, _, _, itemLevel, _, _, _, _, itemEquipLoc = GetItemInfo(tonumber(string.match(itemLink, "item:(%d+)")))
			if itemLevel and itemLevel > 0 then
				item = item + 1
				bagItem = bagItem + 1

				isBagItemLevel = bagsTable[itemEquipLoc]
				if isBagItemLevel and isBagItemLevel > itemLevel then
					totalBag = totalBag + isBagItemLevel
				else
					totalBag = totalBag + itemLevel
				end

				total = total + itemLevel
			end
		else
			isBagItemLevel = bagsTable[itemLoc]
			if isBagItemLevel then
				bagItem = bagItem + 1
				totalBag = totalBag + isBagItemLevel
			end
		end
	end

	wipe(bagsTable)

	if total < 1 then
		return 0, 0
	end

	return (totalBag / bagItem), (total / item)
end

function GetItemLevelColor(unit)
	if not unit then unit = "player" end

	local i = 0
	local sumR, sumG, sumB = 0, 0, 0
	for slotName, _ in pairs(slots) do
		local slotID = GetInventorySlotInfo(slotName)
		if GetInventoryItemTexture(unit, slotID) then
			local itemLink = GetInventoryItemLink(unit, slotID)
			if itemLink then
				local _, _, quality = GetItemInfo(tonumber(string.match(itemLink, "item:(%d+)")))
				if quality then
					i = i + 1
					local r, g, b = GetItemQualityColor(quality)
					sumR = sumR + r
					sumG = sumG + g
					sumB = sumB + b
				end
			end
		end
	end

	if i > 0 then
		return (sumR / i), (sumG / i), (sumB / i)
	else
		return 1, 1, 1
	end
end

local shapeshiftForms = {
    -- Druid
    ["Bear Form"] = 1,
    ["Aquatic Form"] = 2,
    ["Cat Form"] = 3,
    ["Travel Form"] = 4,
    ["Moonkin Form"] = 5,

    -- Rogue
    ["Stealth"] = 1,

    -- Warrior
    ["Battle Stance"] = 1,
    ["Defensive Stance"] = 2,
    ["Berserker Stance"] = 3,
}

function GetShapeshiftForm()
    local numForms = GetNumShapeshiftForms()
    for i = 1, NUM_SHAPESHIFT_SLOTS do
        if i <= numForms then
            local _, name, isActive = GetShapeshiftFormInfo(i)
            name = name == "Dire Bear Form" and "Bear Form" or name
            local formIndex = shapeshiftForms[name]
            if formIndex and isActive then
                return formIndex
            end
        end
    end

    return 0
end