local ALName, ALPrivate = ...

local _G = getfenv(0)
local AtlasLoot = _G.AtlasLoot
local Addons = AtlasLoot.Addons
local AL = AtlasLoot.Locales
local ALIL = AtlasLoot.IngameLocales
local Sources = Addons:RegisterNewAddon("Sources")
local Tooltip = AtlasLoot.Tooltip
local Droprate = AtlasLoot.Data.Droprate
local Profession = AtlasLoot.Data.Profession
local Recipe = AtlasLoot.Data.Recipe
local VendorPrice = AtlasLoot.Data.VendorPrice

-- lua
local type = type
local format = format
local str_split = string.split

-- WoW
local GetCurrencyInfo, GetItemIcon = C_CurrencyInfo.GetCurrencyInfo, C_Item.GetItemIconByID

-- AtlasLoot
local PRICE_INFO = VendorPrice.GetPriceInfoList()
local PRICE_ICON_REPLACE = ALPrivate.PRICE_ICON_REPLACE
local DIFFICULTY = AtlasLoot.DIFFICULTY
local TOKEN_NUMBER_DUMMY = AtlasLoot.Data.Token.GetTokenDummyNumberRange()


-- locals
local TT_F = "%s |cFF00ccff%s|r"
local DUMMY_ICON = "Interface\\Icons\\INV_Misc_QuestionMark"
local TEXTURE_ICON_F, TEXTURE_ICON_FN, ATLAS_ICON_F = "|T%s:0|t ", "|T%d:0|t ", "|A:%s:0:0|a "
local TT_F_PRICE_T, TT_F_PRICE_TN = "|T%s:0|t|cFFffffff%s|r", "|T%d:0|t|cFFffffff%s|r"
local RECIPE_ICON = format(TEXTURE_ICON_F, "134939")
local ICON_TEXTURE = {
	[0]  = format(TEXTURE_ICON_F, DUMMY_ICON),                    -- UNKNOWN
	[1]  = format(ATLAS_ICON_F, "ParagonReputation_Bag"),         -- Loot
	[2]  = format(ATLAS_ICON_F, "QuestNormal"),                   -- Quest
	[3]  = format(ATLAS_ICON_F, "Auctioneer"),                    -- Buy
	[4]  = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(3273)), -- First Aid
	[5]  = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(2018)), -- Blacksmithing
	[6]  = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(2108)), -- Leatherworking
	[7]  = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(2259)), -- Alchemy
	[8]  = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(2366)), -- Herbalism
	[9]  = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(2550)), -- Cooking
	[10] = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(2575)), -- Mining
	[11] = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(3908)), -- Tailoring
	[12] = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(4036)), -- Engineering
	[13] = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(7411)), -- Enchanting
	[14] = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(7732)), -- Fishing
	[15] = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(8618)), -- Skinning
	[16] = format(TEXTURE_ICON_F, C_Spell.GetSpellTexture(2842)), -- Rogue: Poisons
	[17] = format(TEXTURE_ICON_F, 134071),                        -- Jewelcrafting
	[18] = format(TEXTURE_ICON_F, 237171),                        -- Inscription
}
local SOURCE_TYPES = {
	[0]  = UNKNOWN,                              -- UNKNOWN
	[1]  = AL["Loot"],                           -- Loot
	[2]  = AL["Quest"],                          -- Quest
	[3]  = AL["Vendor"],                         -- Buy
	[4]  = ALIL["First Aid"],                    -- First Aid
	[5]  = ALIL["Blacksmithing"],                -- Blacksmithing
	[6]  = ALIL["Leatherworking"],               -- Leatherworking
	[7]  = ALIL["Alchemy"],                      -- Alchemy
	[8]  = ALIL["Herbalism"],                    -- Herbalism
	[9]  = ALIL["Cooking"],                      -- Cooking
	[10] = ALIL["Mining"],                       -- Mining
	[11] = ALIL["Tailoring"],                    -- Tailoring
	[12] = ALIL["Engineering"],                  -- Engineering
	[13] = ALIL["Enchanting"],                   -- Enchanting
	[14] = ALIL["Fishing"],                      -- Fishing
	[15] = ALIL["Skinning"],                     -- Skinning
	[16] = ALIL["ROGUE"]..": "..ALIL["Poisons"], -- Rogue: Poisons
	[17] = ALIL["Jewelcrafting"],                -- Jewelcrafting
	[18] = ALIL["Inscription"],                  -- Inscription
}
local SOURCE_DATA = {}
local KEY_WEAK_MT = { __mode = "k" }
local AL_MODULE = "AtlasLootClassic_DungeonsAndRaids"
local PRICE_STRING_SPLIT_OR = "-"
local PRICE_STRING_SPLIT_AND = ":"
local PRICE_DELIMITER = " |cFFffffff&|r  "
local PRICE_INFO_TT_START = format(TT_F.."  ", ICON_TEXTURE[3], AL["Vendor"]..":")
local DIFF_SPLIT_STRING = " / "

local TooltipsHooked = false
local TooltipCache, TooltipTextCache = {}, nil

-- Addon
Sources.DbDefaults = {
	enabled = true,
	showDropRate = true,
	showProfRank = true,
	showRecipeSource = true,
	showLineBreak = true,
	showVendorPrices = true,
	["Sources"] = {
		["*"] = true,
		[16] = false,
	}
}

--Sources.GlobalDbDefaults = {}
local function BuildSource(ini, boss, typ, item, diffID)
	if typ and typ > 3 then
		-- Profession
		local src = ""
		--RECIPE_ICON
		if Sources.db.showRecipeSource then
			local recipe = Recipe.GetRecipeForSpell(item)
			local sourceData
			for i = #SOURCE_DATA, 1, -1 do
				if recipe and SOURCE_DATA[i].ItemData[recipe] then
					sourceData = SOURCE_DATA[i]
				end
			end
			if recipe and sourceData then
				if type(sourceData.ItemData[item]) == "number" then
					sourceData.ItemData[item] = sourceData.ItemData[sourceData.ItemData[item]]
				end

				local data = sourceData.ItemData[recipe]
				if type(data[1]) == "table" then
					for i = 1, #data do
						src = src..format(TT_F, RECIPE_ICON, BuildSource(sourceData.AtlasLootIDs[data[i][1]], data[i][2], data[i][3], data[i][4] or item))..(i == #data and "" or "\n")
					end
				else
					src = src..format(TT_F, RECIPE_ICON, BuildSource(sourceData.AtlasLootIDs[data[1]], data[2], data[3], data[4] or item))
				end
			end
		end
		if Sources.db.showProfRank then
			local prof = Profession.GetProfessionData(item)
			if prof and prof[3] > 1 then
				return SOURCE_TYPES[typ].." ("..prof[3]..")"..(src ~= "" and "\n"..src or src)
			else
				return SOURCE_TYPES[typ]..(src ~= "" and "\n"..src or src)
			end
		else
			return SOURCE_TYPES[typ]..src
		end
	end
	if ini then
		local iniName, bossName = AtlasLoot.ItemDB:GetNameData_UNSAFE(AL_MODULE, ini, boss)
		local dropRate
		if Sources.db.showDropRate then
			local npcID = AtlasLoot.ItemDB:GetNpcID_UNSAFE(AL_MODULE, ini, boss)
			if type(npcID) == "table" then npcID = npcID[1] end
			dropRate = Droprate:GetData(npcID, item)
		end
		if bossName and diffID then
			-- diff 0 means just heroic
			if diffID == 0 then
				bossName = bossName.." <"..DIFFICULTY.HEROIC.sourceLoc..">"
			elseif type(diffID) == "table" then
				local diffString
				for i = 1, #diffID do
					diffString = i > 1 and (diffString..DIFF_SPLIT_STRING..DIFFICULTY[diffID[i]].sourceLoc) or (DIFFICULTY[diffID[i]].sourceLoc)
				end
				if diffString then
					bossName = bossName.." <"..diffString..">"
				end
			else
				bossName = bossName.." <"..DIFFICULTY[diffID].sourceLoc..">"
			end
		end
		if iniName and bossName then
			if dropRate then
				return iniName.." - "..bossName.." ("..dropRate.."%)"
			else
				return iniName.." - "..bossName
			end
		elseif iniName then
			if dropRate then
				return iniName.." ("..dropRate.."%)"
			else
				return iniName
			end
		end
	end
	return ""
end

local function GetPriceToolTipString(icon, value)
	if type(icon) == "number" then
		return format(TT_F_PRICE_TN, icon, value)
	else
		return format(TT_F_PRICE_T, icon, value)
	end
end

local function GetPriceFormatString(priceList)
	local fullString = PRICE_INFO_TT_START
	for i = 1, #priceList, 2 do
		local priceType, priceValue = priceList[i], priceList[i + 1]
		if i > 1 then fullString = fullString..PRICE_DELIMITER end

		if PRICE_INFO[priceType] then
			if PRICE_INFO[priceType].func then
				fullString = fullString..PRICE_INFO[priceType].func(priceValue)
			elseif PRICE_INFO[priceType].icon then
				fullString = fullString..GetPriceToolTipString(PRICE_INFO[priceType].icon, priceValue)
			elseif PRICE_INFO[priceType].currencyID then
				local info = GetCurrencyInfo(PRICE_INFO[priceType].currencyID)
				if info then
					fullString = fullString..GetPriceToolTipString(PRICE_ICON_REPLACE[priceType] or info.iconFileID, priceValue)
				end
			elseif PRICE_INFO[priceType].itemID then
				PRICE_INFO[priceType].icon = GetItemIcon(PRICE_INFO[priceType].itemID)
				fullString = fullString..GetPriceToolTipString(PRICE_INFO[priceType].icon, priceValue)
			end
		elseif tonumber(priceType) then
			fullString = fullString..GetPriceToolTipString(GetItemIcon(priceType), priceValue)
		end
	end

	return fullString ~= PRICE_INFO_TT_START and fullString or nil
end

local function GetTokenIcon(token)
	if token >= TOKEN_NUMBER_DUMMY then
		return ICON_TEXTURE[1]
	else
		return format(TEXTURE_ICON_FN, GetItemIcon(token))
	end
end

local function BuildSourceFromItemData(item, destTable, itemData, sourceData, iconTexture)
	if not item or not itemData[item] then return end
	if type(itemData[item][1]) == "table" then
		for i, data in ipairs(itemData[item]) do
			if data[3] and Sources.db.Sources[data[3]] then
				destTable[#destTable + 1] = format(TT_F, iconTexture or ICON_TEXTURE[data[3] or 0], BuildSource(sourceData.AtlasLootIDs[data[1]], data[2], data[3], data[4] or item, data[5]))
			end
		end
	else
		local data = itemData[item]
		if data[3] and Sources.db.Sources[data[3]] then
			destTable[#destTable + 1] = format(TT_F, iconTexture or ICON_TEXTURE[data[3] or 0], BuildSource(sourceData.AtlasLootIDs[data[1]], data[2], data[3], data[4] or item, data[5]))
		end
	end
end

local function OnTooltipSetItem_Hook(self)
	if self:IsForbidden() or not SOURCE_DATA or not Sources.db.enabled then return end
	local _, item = self:GetItem()
	if not item then return end
	if not TooltipCache[item] then
		TooltipCache[item] = tonumber(strmatch(item, "item:(%d+)"))
	end

	item = TooltipCache[item]

	local sourceData
	for i = #SOURCE_DATA, 1, -1 do
		if item and SOURCE_DATA[i].ItemData[item] then
			sourceData = SOURCE_DATA[i]
		end
	end

	if item then
		local newAdded
		-- sources from loot tables
		if sourceData and TooltipTextCache[item] ~= false and not TooltipTextCache[item] then
			TooltipTextCache[item] = {}

			-- token data
			if type(sourceData.ItemData[item]) == "number" then
				BuildSourceFromItemData(sourceData.ItemData[item], TooltipTextCache[item], sourceData.ItemData, sourceData, GetTokenIcon(sourceData.ItemData[item]))
			else
				if sourceData.ItemData[item][6] then
					for i, v in ipairs(sourceData.ItemData[item][6]) do
						BuildSourceFromItemData(v, TooltipTextCache[item], sourceData.ItemData, sourceData, GetTokenIcon(v))
					end
				end
				BuildSourceFromItemData(item, TooltipTextCache[item], sourceData.ItemData, sourceData)
			end


			if #TooltipTextCache[item] < 1 then
				TooltipTextCache[item] = false
			end
			newAdded = true
		end

		-- price sources
		if Sources.db.showVendorPrices and (newAdded or not TooltipTextCache[item]) and VendorPrice.ItemHasVendorPrice(item) then
			if not TooltipTextCache[item] then
				TooltipTextCache[item] = {}
			end
			local priceString = VendorPrice.GetVendorPriceForItem(item)
			-- split the price string into parts
			local priceInfo = { str_split(PRICE_STRING_SPLIT_OR, priceString) }
			if priceInfo[2] then
				for i = 1, #priceInfo do
					priceInfo[i] = { str_split(PRICE_STRING_SPLIT_AND, priceInfo[i]) }
				end
			else
				priceInfo = { { str_split(PRICE_STRING_SPLIT_AND, priceInfo[1]) } }
			end

			for priceSumCount = 1, #priceInfo do
				TooltipTextCache[item][#TooltipTextCache[item] + 1] = GetPriceFormatString(priceInfo[priceSumCount])
			end
		end
		if TooltipTextCache[item] then
			if Sources.db.showLineBreak then
				self:AddLine(" ")
			end
			for i = 1, #TooltipTextCache[item] do
				self:AddLine(TooltipTextCache[item][i])
			end
		end
	end
end

local function InitTooltips()
	if TooltipsHooked then return end
	Tooltip:AddHookFunction("OnTooltipSetItem", OnTooltipSetItem_Hook)
	TooltipsHooked = true
end

function Sources:UpdateDb()
	self.db = self:GetDb()

	TooltipTextCache = {}
	setmetatable(TooltipTextCache, KEY_WEAK_MT)

	if self.db.enabled then
		AtlasLoot.Loader:LoadModule("AtlasLootClassic_Data", InitTooltips)
		if self.db.showDropRate then
			AtlasLoot.Loader:LoadModule("AtlasLootClassic_DungeonsAndRaids")
		end
	end
end

function Sources.OnInitialize()
	Sources:UpdateDb()
end

function Sources:OnProfileChanged()
	Sources:UpdateDb()
end

function Sources:OnStatusChanged()
	Sources:UpdateDb()
end

function Sources:SetData(dataTable)
	SOURCE_DATA[#SOURCE_DATA + 1] = dataTable
end

function Sources:GetSourceTypes()
	return SOURCE_TYPES
end

function Sources:ItemSourcesUpdated(itemID)
	if not itemID then return end
	TooltipTextCache[itemID] = nil
end

Sources:Finalize()
