local _, addonTable = ...

local Collections = {}

-- Locals
local archfragments = {}

-- Upvalues
local R = Spearity
local CONSTANTS = addonTable.constants

-- Lua APIs
local pairs = pairs
local type = type
local select = select
local tonumber = tonumber

-- WOW APIs
local PlayerHasToy = PlayerHasToy
local InCombatLockdown = InCombatLockdown
local C_TransmogCollection = C_TransmogCollection
local C_MountJournal = C_MountJournal
local C_PetJournal = C_PetJournal
-- The legacy companion API was removed on modern clients; fall back to no-ops so scans don't error out
local GetNumCompanions = GetNumCompanions or function()
	return 0
end
local GetCompanionInfo = GetCompanionInfo or function()
	return nil
end
local GetAchievementInfo = GetAchievementInfo
local GetNumArchaeologyRaces = GetNumArchaeologyRaces or function()
	return 0
end
local GetNumArtifactsByRace = GetNumArtifactsByRace
local GetArtifactInfoByRace = GetArtifactInfoByRace
local IsQuestComplete = _G.C_QuestLog.IsComplete
local GetArchaeologyRaceInfo = GetArchaeologyRaceInfo
local GetActiveArtifactByRace = GetActiveArtifactByRace

-- UIParentLoadAddOn was removed from the FrameXML globals; C_AddOns.LoadAddOn is the modern equivalent
local LoadAddOn = (_G.C_AddOns and _G.C_AddOns.LoadAddOn) or _G.UIParentLoadAddOn

function Collections:ScanTransmog(reason)
	self = Spearity
	self:Debug("Scanning transmog (" .. (reason or "") .. ")")

	for k, v in pairs(R.db.profile.groups) do
		if type(v) == "table" then
			for kk, vv in pairs(v) do
				if type(vv) == "table" then
					if vv.itemId and not vv.repeatable and select(2, C_TransmogCollection.GetItemInfo(vv.itemId)) then -- Don't scan for items that aren't gear that have a transmog collection appearance
						if C_TransmogCollection.PlayerHasTransmog(vv.itemId) then -- You have the appearance of the item you're tracking
							vv.known = true
							vv.enabled = false
							vv.found = true
						end
					end
				end
			end
		end
	end
end

function Collections:ScanToys(reason)
	if C_ToyBox.GetNumToys() == 0 then
		-- Can't load Blizzard_Collections since it's broken in Classic Era
		-- Toys are actually regular items here, so tracking should still work
		return
	end

	self = Spearity
	self:Debug("Scanning toys (" .. (reason or "") .. ")")

	-- Load the Collections add-on if needed
	if not Spearity.toysScanned then
		if not ToyBox_OnLoad then
			self:Debug("Loading Blizzard_Collections addon so the ToyBox can be scanned")
			if LoadAddOn then
				LoadAddOn("Blizzard_Collections")
			end
		end
	end

	-- Scan all Spearity items to see if we already have a toy
	Spearity.toysScanned = true
	for k, v in pairs(R.db.profile.groups) do
		if type(v) == "table" then
			for kk, vv in pairs(v) do
				if type(vv) == "table" then
					if vv.itemId and not vv.repeatable then
						if PlayerHasToy(vv.itemId) then
							vv.known = true
							vv.enabled = false
							vv.found = true
						end
					end
				end
			end
		end
	end
end

function Collections:ScanExistingItems(reason)
	self = Spearity
	-- Don't allow this scan in combat; it takes too long and the script will receive a "script ran too long" error
	-- Under normal conditions this shouldn't be called during combat, except during the 5-minute final init, or
	-- if the user is messing around with Options in combat.
	if InCombatLockdown() then
		return
	end

	self:Debug("Scanning for existing items (" .. reason .. ")")
	self.Profiling:StartTimer("Collections.ScanExistingItems")

	-- Scans need to index by spellId, creatureId, achievementId, raceId, itemId (for toys), statisticId (which is a table; for stats)

	-- Mounts (pre-7.0)
	if C_MountJournal.GetMountInfo ~= nil then
		-- Mounts (7.0+)
		for id = 1, C_MountJournal.GetNumMounts() do
			local creatureName, spellId, icon, active, isUsable, sourceType, isFavorite, isFactionSpecific, faction, hideOnChar, isCollected =
				C_MountJournal.GetMountInfo(id)
			local creatureDisplayID, descriptionText, sourceText, isSelfMount, mountType =
				C_MountJournal.GetMountInfoExtra(id)

			Spearity.mount_sources[spellId] = sourceText

			if isCollected then
				for k, v in pairs(R.db.profile.groups) do
					if type(v) == "table" then
						for kk, vv in pairs(v) do
							if type(vv) == "table" then
								if vv.spellId and vv.spellId == spellId then
									vv.known = true
								end
								if vv.spellId and vv.spellId == spellId and not vv.repeatable then
									vv.enabled = false
									vv.found = true
								end
							end
						end
					end
				end
			end
		end
	else
		for i, id in pairs(C_MountJournal.GetMountIDs()) do
			local _, spellId, _, _, _, _, _, _, _, _, isCollected = C_MountJournal.GetMountInfoByID(id)
			local _, _, sourceText = C_MountJournal.GetMountInfoExtraByID(id)

			Spearity.mount_sources[spellId] = sourceText

			if isCollected then
				for k, v in pairs(R.db.profile.groups) do
					if type(v) == "table" then
						for kk, vv in pairs(v) do
							if type(vv) == "table" then
								if vv.spellId and vv.spellId == spellId then
									vv.known = true
								end
								if vv.spellId and vv.spellId == spellId and not vv.repeatable then
									vv.enabled = false
									vv.found = true
								end
							end
						end
					end
				end
			end
		end
	end

	-- Companions that this character learned
	for id = 1, GetNumCompanions("CRITTER") or 0 do
		local spellId = select(3, GetCompanionInfo("CRITTER", id))
		for k, v in pairs(R.db.profile.groups) do
			if type(v) == "table" then
				for kk, vv in pairs(v) do
					if type(vv) == "table" then
						if vv.spellId and vv.spellId == spellId and not vv.repeatable then
							vv.enabled = false
							vv.found = true
						end
					end
				end
			end
		end
	end

	-- Battle pets across your account
	local petFilter = _G.Enum and _G.Enum.PetJournalFilter
	if C_PetJournal.SetFlagFilter ~= nil then -- Pre-7.0
		local flagCollected = _G.LE_PET_JOURNAL_FLAG_COLLECTED
		local flagFavorites = _G.LE_PET_JOURNAL_FLAG_FAVORITES
		local flagNotCollected = _G.LE_PET_JOURNAL_FLAG_NOT_COLLECTED
		if flagCollected then
			C_PetJournal.SetFlagFilter(flagCollected, true)
		end
		if flagFavorites then
			C_PetJournal.SetFlagFilter(flagFavorites, false)
		end
		if flagNotCollected then
			C_PetJournal.SetFlagFilter(flagNotCollected, true)
		end
		C_PetJournal.AddAllPetTypesFilter()
		C_PetJournal.AddAllPetSourcesFilter()
	else -- 7.0+
		-- These used to be LE_PET_JOURNAL_FILTER_* globals; on modern clients they live in Enum.PetJournalFilter
		local filterCollected = _G.LE_PET_JOURNAL_FILTER_COLLECTED or (petFilter and petFilter.Collected)
		local filterNotCollected = _G.LE_PET_JOURNAL_FILTER_NOT_COLLECTED or (petFilter and petFilter.NotCollected)
		if filterCollected then
			C_PetJournal.SetFilterChecked(filterCollected, true)
		end
		if filterNotCollected then
			C_PetJournal.SetFilterChecked(filterNotCollected, true)
		end
		C_PetJournal.SetAllPetTypesChecked(true)
		C_PetJournal.SetAllPetSourcesChecked(true)
	end
	local total, numOwnedPets = C_PetJournal.GetNumPets()
	for i = 1, total do
		local petID, speciesID, owned, customName, level, favorite, isRevoked, speciesName, icon, petType, companionID, tooltip, description, isWild, canBattle, isTradeable, isUnique, obtainable =
			C_PetJournal.GetPetInfoByIndex(i)
		Spearity.pet_sources[companionID] = tooltip
		if owned then
			for k, v in pairs(R.db.profile.groups) do
				if type(v) == "table" then
					for kk, vv in pairs(v) do
						if type(vv) == "table" then
							if vv.creatureId and vv.creatureId == companionID then
								vv.known = true
							end
							if vv.creatureId and vv.creatureId == companionID and not vv.repeatable then
								vv.enabled = false
								vv.found = true
							end
						end
					end
				end
			end
		end
	end

	-- Achievements
	for k, v in pairs(R.db.profile.groups) do
		if type(v) == "table" then
			for kk, vv in pairs(v) do
				if type(vv) == "table" then
					if vv.achievementId and tonumber(vv.achievementId) then
						local IDNumber, Name, Points, Completed, Month, Day, Year, Description, Flags, Image, RewardText, isGuildAch =
							GetAchievementInfo(vv.achievementId)
						if Completed and not vv.repeatable then
							vv.enabled = false
							vv.found = true
						end
					end
				end
			end
		end
	end

	-- Scan all archaeology races and set any item attempts to the number of solves for that race
	-- (if we've never seen attempts for the race before)
	local s = 0
	for x = 1, GetNumArchaeologyRaces() do
		local c = GetNumArtifactsByRace(x)
		local a = 0
		for y = 1, c do
			local t = select(10, GetArtifactInfoByRace(x, y))
			a = a + t
			s = s + t
		end

		for k, v in pairs(self.db.profile.groups) do
			if type(v) == "table" then
				for kk, vv in pairs(v) do
					if type(vv) == "table" then
						if vv.enabled ~= false then
							if vv.method == CONSTANTS.DETECTION_METHODS.ARCH and vv.raceId ~= nil then
								if vv.raceId == x then
									-- We've never seen any attempts for this race yet, so set our attempts to this character's current amount
									if a > (vv.attempts or 0) then
										vv.attempts = a
									end
								end
							end
						end
					end
				end
			end
		end
	end

	-- Scan all items for Obtained Quest IDs and mark completed if the quest is completed
	for k, v in pairs(R.db.profile.groups) do
		if type(v) == "table" then
			for kk, vv in pairs(v) do
				if type(vv) == "table" then
					if vv.obtainedQuestId and tonumber(vv.obtainedQuestId) then
						if IsQuestComplete(tonumber(vv.obtainedQuestId)) then
							vv.enabled = false
							vv.found = true
						end
					end
				end
			end
		end
	end

	-- Other scans
	self.Profiling:StartTimer("Collections.ScanStatistics")
	self:ScanStatistics(reason)
	self.Profiling:EndTimer("Collections.ScanStatistics")

	self.Profiling:StartTimer("Collections.ScanToys")
	Spearity.Collections:ScanToys(reason)
	self.Profiling:EndTimer("Collections.ScanToys")

	self.Profiling:StartTimer("Collections.ScanTransmog")
	Spearity.Collections:ScanTransmog(reason)
	self.Profiling:EndTimer("Collections.ScanTransmog")

	self.Profiling:StartTimer("Collections.ScanCalendar")
	self:ScanCalendar(reason)
	self.Profiling:EndTimer("Collections.ScanCalendar")

	self.Profiling:StartTimer("Collections.ScanInstanceLocks")
	self:ScanInstanceLocks(reason)
	self.Profiling:EndTimer("Collections.ScanInstanceLocks")

	self.Profiling:EndTimer("Collections.ScanExistingItems")
end

-------------------------------------------------------------------------------------
-- Archaeology detection. Basically we look to see if you spent any fragments, and rescan your projects if so.
-------------------------------------------------------------------------------------

function R:ScanAllArch(event)
	self:ScanArchFragments(event)
	self:ScanArchProjects(event)
end

function R:ScanArchFragments(event)
	local scan = false
	if GetNumArchaeologyRaces() == 0 then
		return
	end
	for race_id = 1, GetNumArchaeologyRaces() do
		local _, _, _, currencyAmount = GetArchaeologyRaceInfo(race_id)
		local diff = currencyAmount - (archfragments[race_id] or 0)
		archfragments[race_id] = currencyAmount
		if diff < 0 then
			-- We solved an artifact. If any of our items depend on this race ID, increment their attempt count.
			for k, v in pairs(self.db.profile.groups) do
				if type(v) == "table" then
					for kk, vv in pairs(v) do
						if type(vv) == "table" then
							if vv.enabled ~= false then
								local found = false
								if vv.method == CONSTANTS.DETECTION_METHODS.ARCH and vv.raceId ~= nil then
									if vv.raceId == race_id then
										found = true
									end
								end
								if found then
									if vv.attempts == nil then
										vv.attempts = 1
									else
										vv.attempts = vv.attempts + 1
									end
									self:OutputAttempts(vv)
								end
							end
						end
					end
				end
			end
			scan = true
		end
	end

	-- We solved an artifact; scan projects
	if scan then
		-- Scan now, and later. The server takes a while to decide on the next project. The time it takes varies considerably.
		self:ScanArchProjects(event)
		self:ScheduleTimer(function()
			R:ScanArchProjects("SOLVED AN ARTIFACT - DELAYED 1")
		end, 2)
		self:ScheduleTimer(function()
			R:ScanArchProjects("SOLVED AN ARTIFACT - DELAYED 2")
		end, 5)
		self:ScheduleTimer(function()
			R:ScanArchProjects("SOLVED AN ARTIFACT - DELAYED 3")
		end, 10)
		self:ScheduleTimer(function()
			R:ScanArchProjects("SOLVED AN ARTIFACT - DELAYED 4")
		end, 20)
	end
end

function R:ScanArchProjects(reason)
	self:Debug("Scanning archaeology projects (%s)", reason)
	if GetNumArchaeologyRaces() == 0 then
		return
	end
	for race_id = 1, GetNumArchaeologyRaces() do
		local name = GetActiveArtifactByRace(race_id)
		if Spearity.architems[name] then
			-- We started a project we were looking for!
			local id = Spearity.architems[name].itemId
			if id then
				self:OnItemFound(id, Spearity.items[id])
			end
		end
	end
end

Spearity.Collections = Collections
return Collections
