local _, ns = ...
local B, C, L, DB = unpack(ns)
local TT = B:GetModule("Tooltip")

-- Credit: Cloudy Unit Info, by Cloudyfa
local select, max, strfind, format, strsplit = select, math.max, string.find, string.format, string.split
local GetTime, CanInspect, NotifyInspect, ClearInspectPlayer, IsShiftKeyDown = GetTime, CanInspect, NotifyInspect, ClearInspectPlayer, IsShiftKeyDown
local UnitGUID, UnitClass, UnitIsUnit, UnitIsPlayer, UnitIsVisible, UnitIsDeadOrGhost, UnitOnTaxi = UnitGUID, UnitClass, UnitIsUnit, UnitIsPlayer, UnitIsVisible, UnitIsDeadOrGhost, UnitOnTaxi
local GetInventoryItemTexture, GetInventoryItemLink, GetAverageItemLevel = GetInventoryItemTexture, GetInventoryItemLink, GetAverageItemLevel
local HEIRLOOMS = _G.HEIRLOOMS

local levelPrefix = STAT_AVERAGE_ITEM_LEVEL..": "..DB.InfoColor
local isPending = LFG_LIST_LOADING
local resetTime, frequency = 900, .5
local inspectFrameLockTime, inspectNotifyLockTime = 10, 3
local cache, weapon, currentUNIT, currentGUID, inspectGUID = {}, {}
local updater
local inspectFrameTime, inspectNotifyTime = 0, 0
local tooltipInspecting

TT.TierSets = {
	-- WARRIOR
	[249950] = true, [249951] = true, [249952] = true, [249953] = true, [249955] = true,
	-- PALADIN
	[249959] = true, [249960] = true, [249961] = true, [249962] = true, [249964] = true,
	-- HUNTER
	[249986] = true, [249987] = true, [249988] = true, [249989] = true, [249991] = true,
	-- ROGUE
	[250004] = true, [250005] = true, [250006] = true, [250007] = true, [250009] = true,
	-- PRIEST
	[250049] = true, [250054] = true, [250050] = true, [250051] = true, [250052] = true,
	-- DEATHKNIGHT
	[249968] = true, [249969] = true, [249970] = true, [249971] = true, [249973] = true,
	-- SHAMAN
	[249977] = true, [249978] = true, [249979] = true, [249980] = true, [249982] = true,
	-- MAGE
	[250058] = true, [250059] = true, [250060] = true, [250061] = true, [250063] = true,
	-- WARLOCK
	[250040] = true, [250041] = true, [250042] = true, [250043] = true, [250045] = true,
	-- MONK
	[250013] = true, [250014] = true, [250015] = true, [250016] = true, [250018] = true,
	-- DRUID
	[250022] = true, [250023] = true, [250024] = true, [250025] = true, [250027] = true,
	-- DEMONHUNTER
	[250031] = true, [250032] = true, [250033] = true, [250034] = true, [250036] = true,
	-- EVOKER
	[249995] = true, [249996] = true, [249997] = true, [249998] = true, [250000] = true,
}

local formatSets = {
	[1] = " |cff14b200(1/4)", -- green
	[2] = " |cff0091f2(2/4)", -- blue
	[3] = " |cff0091f2(3/4)", -- blue
	[4] = " |cffc745f9(4/4)", -- purple
	[5] = " |cffc745f9(5/5)", -- purple
}

local function checkUnitGUID(unit)
	local guid = UnitGUID(unit)
	return B:NotSecretValue(guid) and guid
end

local function StopInspectUpdate(clearGUID)
	if updater then updater:Hide() end
	if clearGUID then inspectGUID = nil end
end

local function CancelInspectUpdate()
	StopInspectUpdate(true)
	if TT.GetInspectInfo then
		B:UnregisterEvent("INSPECT_READY", TT.GetInspectInfo)
	end
end

local function InspectFrameIsBusy()
	local now = GetTime()
	if inspectFrameTime > 0 and now - inspectFrameTime < inspectFrameLockTime then return true end
	if inspectNotifyTime > 0 and now - inspectNotifyTime < inspectNotifyLockTime then return true end

	return InspectFrame and InspectFrame:IsShown()
end

local function OnInspectUnit()
	inspectFrameTime = GetTime()
	CancelInspectUpdate()
end

local function HookInspectUnit()
	if not InspectUnit or TT.inspectUnitHooked then return end

	hooksecurefunc("InspectUnit", OnInspectUnit)
	TT.inspectUnitHooked = true
end

local function OnInspectUILoaded(event, addon)
	if addon ~= "Blizzard_InspectUI" then return end

	HookInspectUnit()
	B:UnregisterEvent(event, OnInspectUILoaded)
end

hooksecurefunc("NotifyInspect", function()
	if not tooltipInspecting then
		inspectNotifyTime = GetTime()
		CancelInspectUpdate()
	end
end)

function TT:InspectOnUpdate(elapsed)
	self.elapsed = (self.elapsed or frequency) + elapsed
	if self.elapsed > frequency then
		self.elapsed = 0

		if currentUNIT and checkUnitGUID(currentUNIT) == currentGUID then
			if InspectFrameIsBusy() then
				CancelInspectUpdate()
				return
			end

			B:RegisterEvent("INSPECT_READY", TT.GetInspectInfo)
			inspectGUID = currentGUID
			StopInspectUpdate()
			tooltipInspecting = true
			NotifyInspect(currentUNIT)
			tooltipInspecting = nil
		else
			CancelInspectUpdate()
		end
	end
end

updater = CreateFrame("Frame")
updater:SetScript("OnUpdate", TT.InspectOnUpdate)
updater:Hide()

HookInspectUnit()
if not TT.inspectUnitHooked then
	B:RegisterEvent("ADDON_LOADED", OnInspectUILoaded)
end

local lastTime = 0
function TT:GetInspectInfo(...)
	if self == "UNIT_INVENTORY_CHANGED" then
		if InCombatLockdown() then return end
		local thisTime = GetTime()
		if thisTime - lastTime > .1 then
			lastTime = thisTime

			local unit = ...
			if checkUnitGUID(unit) == currentGUID then
				TT:InspectUnit(unit, true)
			end
		end
	elseif self == "INSPECT_READY" then
		local guid = ...
		if B:NotSecretValue(guid) and guid == currentGUID and guid == inspectGUID then
			inspectGUID = nil

			local level = TT:GetUnitItemLevel(currentUNIT)
			cache[guid].level = level
			cache[guid].getTime = GetTime()

			if level then
				TT:SetupItemLevel(level)
			else
				TT:InspectUnit(currentUNIT, true)
			end

			if not InspectFrameIsBusy() then
				ClearInspectPlayer()
			end
		else
			inspectGUID = nil
		end
		B:UnregisterEvent(self, TT.GetInspectInfo)
	end
end
B:RegisterEvent("UNIT_INVENTORY_CHANGED", TT.GetInspectInfo)

function TT:SetupItemLevel(level)
	if not TT:UnitExists("mouseover") or UnitGUID("mouseover") ~= currentGUID then return end

	local levelLine
	for i = 2, GameTooltip:NumLines() do
		local line = _G["GameTooltipTextLeft"..i]
		local text = line:GetText()
		if text and B:NotSecretValue(text) and strfind(text, levelPrefix) then
			levelLine = line
		end
	end

	level = levelPrefix..(level or isPending)
	if levelLine then
		levelLine:SetText(level)
	else
		GameTooltip:AddLine(level)
	end
end

function TT:GetUnitItemLevel(unit)
	if not unit or checkUnitGUID(unit) ~= currentGUID then return end

	local class = select(2, UnitClass(unit))
	local ilvl, boa, total, haveWeapon, twohand, sets = 0, 0, 0, 0, 0, 0
	local delay, mainhand, offhand, hasArtifact
	weapon[1], weapon[2] = 0, 0

	for i = 1, 17 do
		if i ~= 4 then
			local itemTexture = GetInventoryItemTexture(unit, i)

			if itemTexture then
				local itemLink = GetInventoryItemLink(unit, i)

				if not itemLink then
					delay = true
				else
					local _, _, quality, level, _, _, _, _, slot = C_Item.GetItemInfo(itemLink)
					if (not quality) or (not level) then
						delay = true
					else
						if quality == Enum.ItemQuality.Heirloom then
							boa = boa + 1
						end

						local itemID = GetItemInfoFromHyperlink(itemLink)
						if TT.TierSets[itemID] then
							sets = sets + 1
						end

						if unit ~= "player" then
							level = B.GetItemLevel(itemLink) or level
							if i < 16 then
								total = total + level
							elseif i > 15 and quality == Enum.ItemQuality.Artifact then
								local relics = {select(4, strsplit(":", itemLink))}
								for i = 1, 3 do
									local relicID = relics[i] ~= "" and relics[i]
									local relicLink = select(2, C_Item.GetItemGem(itemLink, i))
									if relicID and not relicLink then
										delay = true
										break
									end
								end
							end

							if i == 16 then
								if quality == Enum.ItemQuality.Artifact then hasArtifact = true end

								weapon[1] = level
								haveWeapon = haveWeapon + 1
								if slot == "INVTYPE_2HWEAPON" or slot == "INVTYPE_RANGED" or (slot == "INVTYPE_RANGEDRIGHT" and class == "HUNTER") then
									mainhand = true
									twohand = twohand + 1
								end
							elseif i == 17 then
								weapon[2] = level
								haveWeapon = haveWeapon + 1
								if slot == "INVTYPE_2HWEAPON" then
									offhand = true
									twohand = twohand + 1
								end
							end
						end
					end
				end
			end
		end
	end

	if not delay then
		if unit == "player" then
			ilvl = select(2, GetAverageItemLevel())
		else
			if hasArtifact or twohand == 2 then
				local higher = max(weapon[1], weapon[2])
				total = total + higher*2
			elseif twohand == 1 and haveWeapon == 1 then
				total = total + weapon[1]*2 + weapon[2]*2
			elseif twohand == 1 and haveWeapon == 2 then
				if mainhand and weapon[1] >= weapon[2] then
					total = total + weapon[1]*2
				elseif offhand and weapon[2] >= weapon[1] then
					total = total + weapon[2]*2
				else
					total = total + weapon[1] + weapon[2]
				end
			else
				total = total + weapon[1] + weapon[2]
			end
			ilvl = total / 16
		end

		if ilvl > 0 then ilvl = format("%.1f", ilvl) end
		if boa > 0 then ilvl = ilvl.." |cff00ccff("..boa..HEIRLOOMS..")" end
		if sets > 0 then ilvl = ilvl..formatSets[sets] end
	else
		ilvl = nil
	end

	return ilvl
end

function TT:InspectUnit(unit, forced)
	local level

	if UnitIsUnit(unit, "player") then
		level = self:GetUnitItemLevel("player")
		self:SetupItemLevel(level)
	else
		if not unit or checkUnitGUID(unit) ~= currentGUID then return end
		if not UnitIsPlayer(unit) then return end

		local currentDB = cache[currentGUID]
		level = currentDB.level
		self:SetupItemLevel(level)

		if not C.db["Tooltip"]["SpecLevelByShift"] and IsShiftKeyDown() then forced = true end
		if level and not forced and (GetTime() - currentDB.getTime < resetTime) then updater.elapsed = frequency return end
		if not UnitIsVisible(unit) or UnitIsDeadOrGhost("player") or UnitOnTaxi("player") then return end
		if InspectFrameIsBusy() then return end

		self:SetupItemLevel()
		updater:Show()
	end
end

function TT:InspectUnitItemLevel(unit)
	if C.db["Tooltip"]["SpecLevelByShift"] and not IsShiftKeyDown() then return end

	if not unit or not CanInspect(unit) then return end
	currentUNIT, currentGUID = unit, checkUnitGUID(unit)
	if not cache[currentGUID] then cache[currentGUID] = {} end

	TT:InspectUnit(unit)
end