Documentation for this module may be created at Module:Sandbox/LVL/doc

-- File: Module:NpcData
-- Provides functions to access data about non-player characters (NPCs)
-- from Data:NpcData.json. It mirrors the functionality of Module:HeroData.

local p = {}

-- Load data and utility modules
-- It's best practice to load data only once.
local npcs_data = mw.loadJsonData("Data:NpcData.json")
local util_module = require('Module:Utilities')
local lang_module = require('Module:Lang')

--[[
Returns the entire data table for a specific NPC.
This function is intended for use by other Lua modules.
@param name {string} The key of the NPC in Data:NpcData.json (e.g., "npc_boss_tier2").
@return {table|nil} The NPC's data table, or nil if not found.
]]
function p.get_npc_data(name)
	if not name or name == '' then
		return nil
	end
	return npcs_data[name]
end

--[[
Retrieves a specific stat value for a given NPC.
Usage: {{#invoke:LVL/Sandbox|get_npc_var|NPC_NAME|STAT_NAME|sig_figs_or_localize}}
- NPC_NAME: The key of the NPC (e.g., "npc_boss_tier2").
- STAT_NAME: The name of the stat (e.g., "MaxHealth").
- sig_figs_or_localize: (Optional)
    - A number for rounding to significant figures.
    - "true" to localize the value (if it's a localization key).
]]
p.get_npc_var = function(frame)
	-- Get arguments from the #invoke call and trim whitespace
	local args = frame.args
	local npc_name = args[1] and mw.text.trim(args[1]) or nil
	local npc_stat_key = args[2] and mw.text.trim(args[2]) or nil
	local sig_figs_or_localize = args[3] and mw.text.trim(args[3]) or nil

	-- Validate required arguments
	if not npc_name or npc_name == '' then
		return "Error: NPC name not provided."
	end
	if not npc_stat_key or npc_stat_key == '' then
		return "Error: Stat key not provided."
	end

	-- Retrieve the NPC data table
	local npc = npcs_data[npc_name]
	if not npc then
		return "Error: NPC '" .. npc_name .. "' not found."
	end

	-- Get the specific stat value
	local var_value = npc[npc_stat_key]
	if var_value == nil then
		-- Always return a visible error if the stat is not found.
		return '<span class="error">Error: Stat "' .. npc_stat_key .. '" not found for NPC "' .. npc_name .. '".</span>'
	end

	-- Handle optional rounding
	if sig_figs_or_localize and tonumber(sig_figs_or_localize) then
		var_value = util_module.round_to_sig_fig(var_value, sig_figs_or_localize)
		if var_value == nil then
			-- Propagate error from utility module
			return "Error: Rounding failed for value."
		end
	end

	-- Handle optional localization
	if sig_figs_or_localize == "true" then
		return lang_module.get_string(var_value)
	end

	return var_value
end

--[[
Retrieves an element from a list (array) within an NPC's data.
Usage: {{#invoke:NpcData|get_list_elem|NPC_NAME|LIST_NAME|INDEX|localize}}
- NPC_NAME: The key of the NPC.
- LIST_NAME: The key for the list property (e.g., "NearbyEnemyResistanceValues").
- INDEX: The 1-based index of the element to retrieve.
- localize: (Optional) "true" to localize the element.
]]
p.get_list_elem = function(frame)
	-- Get arguments and trim whitespace
	local npc_name = frame.args[1] and mw.text.trim(frame.args[1]) or nil
	local var = frame.args[2] and mw.text.trim(frame.args[2]) or nil
	local number_str = frame.args[3] and mw.text.trim(frame.args[3]) or nil
	local localize = frame.args[4] and mw.text.trim(frame.args[4]) or nil
	
	-- Validate arguments
	if not npc_name or npc_name == '' then return "Error: NPC name not provided." end
	if not var or var == '' then return "Error: List variable name not provided." end
	if not number_str or number_str == '' then return "Error: Index not provided." end
	
	local number_int = tonumber(number_str)
	if not number_int then return "Error: Invalid index provided. Must be a number." end

	-- Retrieve NPC data
	local npc = npcs_data[npc_name]
	if not npc then return "Error: NPC '" .. npc_name .. "' not found." end
	
	-- Retrieve list and element
	local list = npc[var]
	if list == nil or type(list) ~= "table" then return "" end -- Return empty string if list doesn't exist or isn't a table
	
	local element = list[number_int]
	if element == nil then return "" end -- Return empty string if element at index doesn't exist
	
	if localize == "true" then 
		element = lang_module.get_string(element)
	end
	
	return element
end

--[[
Retrieves an ability key from an NPC's "BoundAbilities" map.
Usage: {{#invoke:NpcData|get_ability_key|NPC_NAME|SLOT_NAME}}
- NPC_NAME: The key of the NPC.
- SLOT_NAME: The name of the ability slot (e.g., "ESlot_Signature_1").
]]
p.get_ability_key = function(frame)
	-- Get arguments and trim whitespace
	local npc_name = frame.args[1] and mw.text.trim(frame.args[1]) or nil
	local slot_name = frame.args[2] and mw.text.trim(frame.args[2]) or nil

	-- Validate arguments
	if not npc_name or npc_name == '' then return "Error: NPC name not provided." end
    if not slot_name or slot_name == '' then return "Error: Slot name not provided." end

	-- Retrieve NPC data
	local npc_data = npcs_data[npc_name]
	if not npc_data then return "Error: NPC '"..npc_name.. "' not found." end

	-- Retrieve ability data
	local bound_abilities_data = npc_data["BoundAbilities"]
	if not bound_abilities_data or type(bound_abilities_data) ~= "table" then
		return "Error: NPC '" .. npc_name .. "' has no 'BoundAbilities' data."
	end

	-- Retrieve ability key from the slot
	local ability_key = bound_abilities_data[slot_name]
    if not ability_key then
		return "Error: Slot '" .. slot_name .. "' not found for NPC '" .. npc_name .. "'."
	end
    
	return ability_key
end

return p