-- Path of Building
--
-- Module: Data
-- Contains static data used by other modules.
--

LoadModule("Data/Global")

local skillTypes = {
	"act_str",
	"act_dex",
	"act_int",
	"other",
	"glove",
	"minion",
	"spectre",
	"sup_str",
	"sup_dex",
	"sup_int",
}
local itemTypes = {
	"axe",
	"bow",
	"claw",
	"dagger",
	"mace",
	"staff",
	"sword",
	"wand",
	"helmet",
	"body",
	"gloves",
	"boots",
	"shield",
	"quiver",
	"amulet",
	"ring",
	"belt",
	"jewel",
	"flask",
}

local function makeSkillMod(modName, modType, modVal, flags, keywordFlags, ...)
	return {
		name = modName,
		type = modType,
		value = modVal,
		flags = flags or 0,
		keywordFlags = keywordFlags or 0,
		...
	}
end
local function makeFlagMod(modName, ...)
	return makeSkillMod(modName, "FLAG", true, 0, 0, ...)
end
local function makeSkillDataMod(dataKey, dataValue, ...)
	return makeSkillMod("SkillData", "LIST", { key = dataKey, value = dataValue }, 0, 0, ...)
end
local function processMod(grantedEffect, mod)
	mod.source = grantedEffect.modSource
	if type(mod.value) == "table" and mod.value.mod then
		mod.value.mod.source = "Skill:"..grantedEffect.id
	end
	for _, tag in ipairs(mod) do
		if tag.type == "GlobalEffect" then
			grantedEffect.hasGlobalEffect = true
			break
		end
	end
end

-----------------
-- Common Data --
-----------------

data = { }

data.skillColorMap = { colorCodes.STRENGTH, colorCodes.DEXTERITY, colorCodes.INTELLIGENCE, colorCodes.NORMAL }

data.jewelRadius = {
	{ rad = 800, col = "^xBB6600", label = "Small" },
	{ rad = 1200, col = "^x66FFCC", label = "Medium" },
	{ rad = 1500, col = "^x2222CC", label = "Large" }
}

data.labyrinths = {
	{ name = "ENDGAME", label = "Eternal" },
	{ name = "MERCILESS", label = "Merciless" },
	{ name = "CRUEL", label = "Cruel" },
	{ name = "NORMAL", label = "Normal" },
}

data.monsterExperienceLevelMap = { [71] = 70.94, [72] = 71.82, [73] = 72.64, [74] = 73.40, [75] = 74.10, [76] = 74.74, [77] = 75.32, [78] = 75.84, [79] = 76.30, [80] = 76.70, [81] = 77.04, [82] = 77.32, [83] = 77.54, [84] = 77.70, }
for i = 1, 70 do
	data.monsterExperienceLevelMap[i] = i
end

data.weaponTypeInfo = {
	["None"] = { oneHand = true, melee = true, flag = "Unarmed" },
	["Bow"] = { oneHand = false, melee = false, flag = "Bow" },
	["Claw"] = { oneHand = true, melee = true, flag = "Claw" },
	["Dagger"] = { oneHand = true, melee = true, flag = "Dagger" },
	["Staff"] = { oneHand = false, melee = true, flag = "Staff" },
	["Wand"] = { oneHand = true, melee = false, flag = "Wand" },
	["One Handed Axe"] = { oneHand = true, melee = true, flag = "Axe" },
	["One Handed Mace"] = { oneHand = true, melee = true, flag = "Mace" },
	["One Handed Sword"] = { oneHand = true, melee = true, flag = "Sword" },
	["Sceptre"] = { oneHand = true, melee = true, flag = "Mace", label = "One Handed Mace" },
	["Thrusting One Handed Sword"] = { oneHand = true, melee = true, flag = "Sword", label = "One Handed Sword" },
	["Two Handed Axe"] = { oneHand = false, melee = true, flag = "Axe" },
	["Two Handed Mace"] = { oneHand = false, melee = true, flag = "Mace" },
	["Two Handed Sword"] = { oneHand = false, melee = true, flag = "Sword" },
}
data.unarmedWeaponData = {
	[0] = { type = "None", AttackRate = 1.2, CritChance = 0, PhysicalMin = 2, PhysicalMax = 6 }, -- Scion
	[1] = { type = "None", AttackRate = 1.2, CritChance = 0, PhysicalMin = 2, PhysicalMax = 8 }, -- Marauder
	[2] = { type = "None", AttackRate = 1.2, CritChance = 0, PhysicalMin = 2, PhysicalMax = 5 }, -- Ranger
	[3] = { type = "None", AttackRate = 1.2, CritChance = 0, PhysicalMin = 2, PhysicalMax = 5 }, -- Witch
	[4] = { type = "None", AttackRate = 1.2, CritChance = 0, PhysicalMin = 2, PhysicalMax = 6 }, -- Duelist
	[5] = { type = "None", AttackRate = 1.2, CritChance = 0, PhysicalMin = 2, PhysicalMax = 6 }, -- Templar
	[6] = { type = "None", AttackRate = 1.2, CritChance = 0, PhysicalMin = 2, PhysicalMax = 5 }, -- Shadow
}

data.specialBaseTags = {
	["Amulet"] = { shaper = "amulet_shaper", elder = "amulet_elder", },
	["Ring"] = { shaper = "ring_shaper", elder = "ring_elder", },
	["Claw"] = { shaper = "claw_shaper", elder = "claw_elder", },
	["Dagger"] = { shaper = "dagger_shaper", elder = "dagger_elder", },
	["Wand"] = { shaper = "wand_shaper", elder = "wand_elder", },
	["One Handed Sword"] = { shaper = "sword_shaper", elder = "sword_elder", },
	["Thrusting One Handed Sword"] = { shaper = "sword_shaper", elder = "sword_elder", },
	["One Handed Axe"] = { shaper = "axe_shaper", elder = "axe_elder", },
	["One Handed Mace"] = { shaper = "mace_shaper", elder = "mace_elder", },
	["Bow"] = { shaper = "bow_shaper", elder = "bow_elder", },
	["Staff"] = { shaper = "staff_shaper", elder = "staff_elder", },
	["Two Handed Sword"] = { shaper = "2h_sword_shaper", elder = "2h_sword_elder", },
	["Two Handed Axe"] = { shaper = "2h_axe_shaper", elder = "2h_axe_elder", },
	["Two Handed Mace"] = { shaper = "2h_mace_shaper", elder = "2h_mace_elder", },
	["Quiver"] = { shaper = "quiver_shaper", elder = "quiver_elder", },
	["Belt"] = { shaper = "belt_shaper", elder = "belt_elder", },
	["Gloves"] = { shaper = "gloves_shaper", elder = "gloves_elder", },
	["Boots"] = { shaper = "boots_shaper", elder = "boots_elder", },
	["Body Armour"] = { shaper = "body_armour_shaper", elder = "body_armour_elder", },
	["Helmet"] = { shaper = "helmet_shaper", elder = "helmet_elder", },
	["Shield"] = { shaper = "shield_shaper", elder = "shield_elder", },
	["Sceptre"] = { shaper = "sceptre_shaper", elder = "sceptre_elder", },
}

---------------------------
-- Version-specific Data --
---------------------------

for _, targetVersion in ipairs(targetVersionList) do
	local verData = setmetatable({ }, { __index = data })
	data[targetVersion] = verData
	local function dataModule(mod, ...)
		return LoadModule("Data/"..targetVersion.."/"..mod, ...)
	end

	-- Misc data tables
	dataModule("Misc", verData)

	-- Stat descriptions
	if targetVersion ~= "2_6" then
		verData.describeStats = LoadModule("Modules/StatDescriber", targetVersion)
	end

	-- Load item modifiers
	verData.itemMods = {
		Item = dataModule("ModItem"),
		Flask = dataModule("ModFlask"),
		Jewel = dataModule("ModJewel"),
		JewelAbyss = targetVersion ~= "2_6" and dataModule("ModJewelAbyss") or { },
		JewelCluster = targetVersion ~= "2_6" and dataModule("ModJewelCluster") or { },
	}
	verData.masterMods = dataModule("ModMaster")
	verData.enchantments = {
		Helmet = dataModule("EnchantmentHelmet"),
		Boots = dataModule("EnchantmentBoots"),
		Gloves = dataModule("EnchantmentGloves"),
	}
	verData.essences = dataModule("Essence")

	-- Cluster jewel data
	if targetVersion ~= "2_6" then	
		verData.clusterJewels = dataModule("ClusterJewels")
	end

	-- Load skills
	verData.skills = { }
	verData.skillStatMap = dataModule("SkillStatMap", makeSkillMod, makeFlagMod, makeSkillDataMod)
	verData.skillStatMapMeta = {
		__index = function(t, key)
			local map = verData.skillStatMap[key]
			if map then
				t[key] = copyTable(map, true)
				for _, mod in ipairs(map) do
					processMod(t._grantedEffect, mod)
				end
				return map
			end
		end
	}
	for _, type in pairs(skillTypes) do
		dataModule("Skills/"..type, verData.skills, makeSkillMod, makeFlagMod, makeSkillDataMod)
	end
	for skillId, grantedEffect in pairs(verData.skills) do
		grantedEffect.id = skillId
		grantedEffect.modSource = "Skill:"..skillId
		-- Add sources for skill mods, and check for global effects
		for _, list in pairs({grantedEffect.baseMods, grantedEffect.qualityMods, grantedEffect.levelMods}) do
			for _, mod in pairs(list) do
				if mod.name then
					processMod(grantedEffect, mod)
				else
					for _, mod in ipairs(mod) do
						processMod(grantedEffect, mod)
					end
				end
			end
		end
		-- Install stat map metatable
		grantedEffect.statMap = grantedEffect.statMap or { }
		setmetatable(grantedEffect.statMap, verData.skillStatMapMeta)
		grantedEffect.statMap._grantedEffect = grantedEffect
		for _, map in pairs(grantedEffect.statMap) do
			for _, mod in ipairs(map) do
				processMod(grantedEffect, mod)
			end
		end
	end

	-- Load gems
	verData.gems = dataModule("Gems")
	verData.gemForSkill = { }
	verData.gemForBaseName = { }
	for gemId, gem in pairs(verData.gems) do
		gem.id = gemId
		gem.grantedEffect = verData.skills[gem.grantedEffectId]
		verData.gemForSkill[gem.grantedEffect] = gemId
		verData.gemForBaseName[gem.name .. (gem.grantedEffect.support and " Support" or "")] = gemId
		gem.secondaryGrantedEffect = gem.secondaryGrantedEffectId and verData.skills[gem.secondaryGrantedEffectId]
		gem.grantedEffectList = {
			gem.grantedEffect,
			gem.secondaryGrantedEffect
		}
		gem.defaultLevel = gem.defaultLevel or (#gem.grantedEffect.levels > 20 and #gem.grantedEffect.levels - 20) or (gem.grantedEffect.levels[3][1] and 3) or 1
	end

	-- Load minions
	verData.minions = { }
	dataModule("Minions", verData.minions, makeSkillMod)
	verData.spectres = { }
	dataModule("Spectres", verData.spectres, makeSkillMod)
	for name, spectre in pairs(verData.spectres) do
		spectre.limit = "ActiveSpectreLimit"
		verData.minions[name] = spectre
	end
	local missing = { }
	for _, minion in pairs(verData.minions) do
		for _, skillId in ipairs(minion.skillList) do
			if launch.devMode and not verData.skills[skillId] and not missing[skillId] then
				ConPrintf("'%s' missing skill '%s'", minion.name, skillId)
				missing[skillId] = true
			end
		end
		for _, mod in ipairs(minion.modList) do
			mod.source = "Minion:"..minion.name
		end
	end

	-- Item bases
	verData.itemBases = { }
	for _, type in pairs(itemTypes) do
		dataModule("Bases/"..type, verData.itemBases)
	end

	-- Build lists of item bases, separated by type
	verData.itemBaseLists = { }
	for name, base in pairs(verData.itemBases) do
		if not base.hidden then
			local type = base.type
			if base.subType then
				type = type .. ": " .. base.subType
			end
			verData.itemBaseLists[type] = verData.itemBaseLists[type] or { }
			table.insert(verData.itemBaseLists[type], { label = name:gsub(" %(.+%)",""), name = name, base = base })
		end
	end
	verData.itemBaseTypeList = { }
	for type, list in pairs(verData.itemBaseLists) do
		table.insert(verData.itemBaseTypeList, type)
		table.sort(list, function(a, b) 
			if a.base.req and b.base.req then
				if a.base.req.level == b.base.req.level then
					return a.name < b.name
				else
					return (a.base.req.level or 1) > (b.base.req.level or 1)
				end
			elseif a.base.req and not b.base.req then
				return true
			elseif b.base.req and not a.base.req then
				return false
			else
				return a.name < b.name
			end
		end)
	end
	table.sort(verData.itemBaseTypeList)

	-- Rare templates
	verData.rares = dataModule("Rares")
end

-- Uniques (loaded after version-specific data because reasons)
data.uniques = { }
for _, type in pairs(itemTypes) do
	data.uniques[type] = LoadModule("Data/Uniques/"..type)
end
LoadModule("Data/New")
