-- ------------------------------------------------------------------------------ --
--                                TradeSkillMaster                                --
--                http://www.curse.com/addons/wow/tradeskill-master               --
--                                                                                --
--             A TradeSkillMaster Addon (http://tradeskillmaster.com)             --
--    All Rights Reserved* - Detailed license information included with addon.    --
-- ------------------------------------------------------------------------------ --

-- This file contains APIs related to items (itemLinks/itemStrings/etc)

local _, TSM = ...
local private = { bonusIdCache = {}, bonusIdTemp = {}, itemStringCache = {} }
local ITEM_UPGRADE_VALUE_SHIFT = 1000000



-- ============================================================================
-- TSMAPI Functions
-- ============================================================================

function TSMAPI_FOUR.Item.ToItemString(item)
	if not item then return end
	assert(type(item) == "number" or type(item) == "string")

	-- quickly return if we're certain it's already a valid itemString
	if type(item) == "string" and strmatch(item, "^[ip]:[0-9]+$") then return item end

	if not private.itemStringCache[item] then
		private.itemStringCache[item] = private.ToItemString(item)
	end
	return private.itemStringCache[item]
end

function TSMAPI_FOUR.Item.ToItemID(itemString)
	itemString = TSMAPI_FOUR.Item.ToItemString(itemString)
	if type(itemString) ~= "string" then return end
	return tonumber(strmatch(itemString, "^i:(%d+)"))
end

function TSMAPI_FOUR.Item.ToBaseItemString(itemString, doGroupLookup)
	-- make sure it's a valid itemString
	itemString = TSMAPI_FOUR.Item.ToItemString(itemString)
	if not itemString then return end

	-- quickly return if we're certain it's already a valid baseItemString
	if type(itemString) == "string" and strmatch(itemString, "^[ip]:[0-9]+$") then return itemString end

	local baseItemString = strmatch(itemString, "([ip]:%d+)")

	if not doGroupLookup or (TSM.db.profile.userData.items[baseItemString] and not TSM.db.profile.userData.items[itemString]) then
		-- either we're not doing a group lookup, or the base item is in a group and the specific item is not, so return the base item
		return baseItemString
	end
	return itemString
end

function TSMAPI_FOUR.Item.ToWowItemString(itemString)
	local _, itemId, rand, numBonus = (":"):split(itemString)
	local level = UnitLevel("player")
	local spec = GetSpecialization()
	spec = spec and GetSpecializationInfo(spec) or ""
	local upgradeValue = private.GetUpgradeValue(itemString)
	if upgradeValue and numBonus then
		local bonusIds = strmatch(itemString, "i:[0-9]+:[0-9%-]*:[0-9]+:(.+):"..upgradeValue.."$")
		upgradeValue = upgradeValue - ITEM_UPGRADE_VALUE_SHIFT
		return "item:"..itemId.."::::::"..(rand or "").."::"..level..":"..spec..":512::"..numBonus..":"..bonusIds..":"..upgradeValue..":::"
	end
	return "item:"..itemId.."::::::"..(rand or "").."::"..level..":"..spec..":::"..(numBonus and strmatch(itemString, "i:[0-9]+:[0-9%-]*:(.*)") or "")..":::"
end



-- ============================================================================
-- Helper Functions
-- ============================================================================

function private.ToItemString(item)
	if tonumber(item) then
		-- assume this is an itemId
		return "i:"..item
	else
		item = strtrim(item)
	end

	-- test if it's already (likely) an item string or battle pet string
	local result = nil
	if strmatch(item, "^p:([0-9%-:]+)$") then
		result = strjoin(":", strmatch(item, "^(p):(%d+:%d+:%d+)"))
		if result then
			return result
		end
		return item
	elseif strmatch(item, "^i:([0-9%-:]+)$") then
		return private.FixItemString(item)
	end

	result = strmatch(item, "^\124cff[0-9a-z]+\124[Hh](.+)\124h%[.+%]\124h\124r$")
	if result then
		-- it was a full item link which we've extracted the itemString from
		item = result
	end

	-- test if it's an old style item string
	result = strjoin(":", strmatch(item, "^(i)tem:([0-9%-]+):[0-9%-]+:[0-9%-]+:[0-9%-]+:[0-9%-]+:[0-9%-]+:([0-9%-]+)$"))
	if result then
		return private.FixItemString(result)
	end

	-- test if it's an old style battle pet string (or if it was a link)
	result = strjoin(":", strmatch(item, "^battle(p)et:(%d+:%d+:%d+)"))
	if result then
		return result
	end
	result = strjoin(":", strmatch(item, "^battle(p)et:(%d+)[:]*$"))
	if result then
		return result
	end
	result = strjoin(":", strmatch(item, "^(p):(%d+:%d+:%d+)"))
	if result then
		return result
	end

	-- test if it's a long item string
	result = strjoin(":", strmatch(item, "(i)tem:([0-9%-]+):[0-9%-]*:[0-9%-]*:[0-9%-]*:[0-9%-]*:[0-9%-]*:([0-9%-]*):[0-9%-]*:[0-9%-]*:[0-9%-]*:[0-9%-]*:[0-9%-]*:([0-9%-:]+)"))
	if result and result ~= "" then
		return private.FixItemString(result)
	end

	-- test if it's a shorter item string (without bonuses)
	result = strjoin(":", strmatch(item, "(i)tem:([0-9%-]+):[0-9%-]*:[0-9%-]*:[0-9%-]*:[0-9%-]*:[0-9%-]*:([0-9%-]*)"))
	if result and result ~= "" then
		return result
	end
end

function private.RemoveExtra(itemString)
	local num = 1
	while num > 0 do
		itemString, num = gsub(itemString, ":0?$", "")
	end
	return itemString
end

function private.FixItemString(itemString)
	itemString = gsub(itemString, ":0:", "::")-- remove 0s which are in the middle
	itemString = private.RemoveExtra(itemString)
	-- make sure we have the correct number of bonusIds
	-- get the number of bonusIds (plus one for the count)
	local numParts = select("#", (":"):split(itemString)) - 3
	if numParts > 0 then
		-- get the number of extra parts we have
		local count = select(4, (":"):split(itemString))
		count = tonumber(count) or 0
		local numExtraParts = numParts - 1 - count
		local lastExtraPart = tonumber(strmatch(itemString, ":([0-9]+)$"))
		for i=1, numExtraParts do
			itemString = gsub(itemString, ":[0-9]*$", "")
		end
		-- we might have already applied the upgrade value shift
		if numExtraParts == 1 and (lastExtraPart >= 98 and lastExtraPart <= 110) or (lastExtraPart - ITEM_UPGRADE_VALUE_SHIFT >= 90 and lastExtraPart - ITEM_UPGRADE_VALUE_SHIFT <= 110) then
			-- this extra part is likely the upgradeValue which we want to keep so increase it by UPGRADE_VALUE_SHIFT
			if lastExtraPart < ITEM_UPGRADE_VALUE_SHIFT then
				lastExtraPart = lastExtraPart + ITEM_UPGRADE_VALUE_SHIFT
			end
			itemString = itemString..":"..lastExtraPart
		end
		itemString = private.RemoveExtra(itemString)
		-- filter out bonusIds we don't care about
		return private.FilterImportantBonsuIds(itemString)
	end
	return itemString
end

function private.FilterImportantBonsuIds(itemString)
	local itemId, rand, bonusIds = strmatch(itemString, "i:([0-9]+):([0-9%-]*):[0-9]*:(.+)$")
	if not bonusIds then return itemString end
	if not private.bonusIdCache[bonusIds] then
		wipe(private.bonusIdTemp)
		local adjust = 0
		for id in gmatch(bonusIds, "[0-9]+") do
			id = tonumber(id)
			if id > ITEM_UPGRADE_VALUE_SHIFT then
				if not tContains(private.bonusIdTemp, id) then
					tinsert(private.bonusIdTemp, id)
					adjust = adjust + 1
				end
			else
				id = TSM.CONST.IMPORTANT_BONUS_ID_MAP[id]
				if id and not tContains(private.bonusIdTemp, id) then
					tinsert(private.bonusIdTemp, id)
				end
			end
		end
		sort(private.bonusIdTemp)
		private.bonusIdCache[bonusIds] = { num = #private.bonusIdTemp - adjust, value = strjoin(":", unpack(private.bonusIdTemp)) }
	end
	if private.bonusIdCache[bonusIds].num == 0 then
		if rand == "" or tonumber(rand) == 0 then
			return strjoin(":", "i", itemId)
		else
			return strjoin(":", "i", itemId, rand)
		end
	else
		return strjoin(":", "i", itemId, rand, private.bonusIdCache[bonusIds].num, private.bonusIdCache[bonusIds].value)
	end
end

function private.GetUpgradeValue(itemString)
	local bonusIds = strmatch(itemString, "i:[0-9]+:[0-9%-]*:[0-9]*:(.+)$")
	if not bonusIds then return end
	for id in gmatch(bonusIds, "[0-9]+") do
		id = tonumber(id)
		if id > ITEM_UPGRADE_VALUE_SHIFT then
			return id
		end
	end
end
