-- Original name: $NS_RegisterUtils.lua
-- Original author: Nihilist Penguin (https://steamcommunity.com/id/bickback/)

-- Copyright 2023 Nihilist Penguin. This item is not authorized for posting on Steam,
-- except under the Steam account named Nihilist Penguin, or with the copyright holder's explicit permission.
-- Redistribution of this mod without explicit permission from the original creator is prohibited under any circumstances.
-- This includes, but not limited to, uploading these mods to the Steam Workshop or any other site,
-- distribution as part of another mod or modpack, and distribution of modified versions.

-- This file contains utility functions that are used to access or edit mod's register data

if not NSGlobal then
	print("NSGlobal not found, skipping $NS_RegisterUtils.lua")
	return
end

NSGlobal.utils = {}
NSGlobal.utils.register = {}

-- ########################################
-- ########### Section: General ###########
-- ########################################

-- Add ranged weapon type to register
-- @param weapon_name string
-- @param type string
-- @param override boolean (optional)
NSGlobal.utils.register.add_rweapon_type = function(weapon_name, rtype, override)
	local override = override or false

	if not override and NSGlobal.register.default.rweapon_types[weapon_name] ~= nil then
		return
	end

	NSGlobal.register.default.rweapon_types[weapon_name] = rtype
end

-- Find ranged weapon type based on weapon name from register
-- @param weapon_name string
-- @param search_if_not_found boolean (optional)
-- @param skip_registered boolean (optional)
-- @return string type
NSGlobal.utils.register.get_rweapon_type = function(weapon_name, search_if_not_found, skip_registered)
	local skip_registered = skip_registered or false
	local search_if_not_found = search_if_not_found or false
	local out = nil

	if not skip_registered then
		out = NSGlobal.register.default.rweapon_types[weapon_name]
	end

	if not out and search_if_not_found then
		out = NSGlobal.utils.register.search_rweapon_type(weapon_name)
	end

	return out
end

-- Search ranged weapon type based on weapon name from register
-- @param weapon_name string
-- @return string type
NSGlobal.utils.register.search_rweapon_type = function(weapon_name)
	local search_keywords = NSGlobal.register.default.rweapon_types_keywords
	for rtype, weapons in pairs(search_keywords) do
		local f_full_name = string.lower(weapon_name)
		for _, w in ipairs(weapons) do
			if string.find(f_full_name, string.lower(w)) then
				return rtype
			end
		end
	end

	return nil
end

-- Get all ammo types with their weapons
NSGlobal.utils.register.get_all_ammo_types = function()
	local ammos = {}

	local all_script_items = getScriptManager():getAllItems()
	for i = 1, all_script_items:size() do
		local script_item = all_script_items:get(i - 1)
		if script_item:getType() == Type.Weapon then
			local ammo_type = script_item:getAmmoType()
			if ammo_type then
				local weapon_name = script_item:getFullName()
				if ammos[ammo_type] == nil then
					ammos[ammo_type] = {}
				end
				table.insert(ammos[ammo_type], weapon_name)
			end
		end
	end

	return ammos
end

-- Get all default ranged weapon details including ammo type, sound volume, sound radius, swing sound
NSGlobal.utils.register.get_all_rweapon_details = function()
	local weapons = {}

	local all_script_items = getScriptManager():getAllItems()
	for i = 1, all_script_items:size() do
		local script_item = all_script_items:get(i - 1)
		if script_item:getType() == Type.Weapon then
			if script_item:getAmmoType() then
				local weapon_name = script_item:getFullName()
				weapons[weapon_name] = {
					ammo_type = script_item:getAmmoType(),
					sound_volume = script_item:getSoundVolume(),
					sound_radius = script_item:getSoundRadius(),
					rtype = nil,
					swing_sound = script_item:getSwingSound(),
					silenced_swing_sound = nil,
				}
			end
		end
	end

	return weapons
end

-- Get silencer item name for given ammo type
-- @param ammo_type string
-- @return string silenced_swing_sound_name
NSGlobal.utils.register.get_ammo_silencer_item_name = function(ammo_type)
	return NSGlobal.register.mapping.ammo_type_to_silencer_item_name[ammo_type]
end

NSGlobal.utils.register.add_ammo_type_support = function(ammo_type, target_name, silencer_item_name)
	-- assign multiplier id to given ammo type
	NSGlobal.register.mapping.ammo_mult_ingame_to_sandbox[ammo_type] = "m" .. target_name
	table.insert(NSGlobal.register.mapping.ammo_mult_sandbox_to_ingame["m" .. target_name], ammo_type)
	-- assign sound increase id to given ammo type
	NSGlobal.register.mapping.ammo_sound_to_sandbox[ammo_type] = "s" .. target_name
	table.insert(NSGlobal.register.mapping.ammo_sound_sandbox_to_ingame["s" .. target_name], ammo_type)
	-- assign distribution id to given ammo type
	NSGlobal.register.mapping.ammo_dist_ingame_to_sandbox[ammo_type] = "d" .. target_name
	table.insert(NSGlobal.register.mapping.ammo_dist_sandbox_to_ingame["d" .. target_name], ammo_type)
	-- assign crafting id to given ammo type
	NSGlobal.register.mapping.ammo_crafting_ingame_to_sandbox[ammo_type] = "c" .. target_name
	table.insert(NSGlobal.register.mapping.ammo_crafting_sandbox_to_ingame["c" .. target_name], ammo_type)
	-- assign silencer item name to given ammo type
	if silencer_item_name ~= nil then -- custom silencer item name
		NSGlobal.register.mapping.ammo_type_to_silencer_item_name[ammo_type] = silencer_item_name
		table.insert(NSGlobal.register.default.silencer_item_names, silencer_item_name)
	else
		NSGlobal.register.mapping.ammo_type_to_silencer_item_name[ammo_type] = "Base.Silencer" .. target_name
	end
	-- assign ammo type to the supported list
	table.insert(NSGlobal.register.default.supported_ammo_types, ammo_type)
end

-- #######################################
-- ########### Section: Sounds ###########
-- #######################################

-- Add ranged weapon swing sound to register
-- @param weapon_name string
-- @param swing_sound_name string
-- @param override boolean (optional)
NSGlobal.utils.register.add_rweapon_sound = function(weapon_name, swing_sound_name, override)
	local override = override or false

	if not override and NSGlobal.register.override.rweapon_sounds[weapon_name] ~= nil then
		return
	end

	NSGlobal.register.override.rweapon_sounds[weapon_name] = swing_sound_name
end

-- Get ranged weapon swing sound from register
-- @param weapon_name string
-- @return string swing_sound_name
NSGlobal.utils.register.get_rweapon_sound = function(weapon_name)
	return NSGlobal.register.override.rweapon_sounds[weapon_name]
end

-- Add silenced swing sound for ranged weapon
-- @param weapon_name string
-- @param swing_sound_name string
-- @param override boolean (optional)
NSGlobal.utils.register.add_rweapon_silenced_sound = function(weapon_name, swing_sound_name, override)
	local override = override or false

	if not override and NSGlobal.register.override.rweapon_silenced_sounds[weapon_name] ~= nil then
		return
	end

	NSGlobal.register.override.rweapon_silenced_sounds[weapon_name] = swing_sound_name
end

-- Get silenced swing sound for ranged weapon
-- @param weapon_name string
-- @return string swing_sound_name
NSGlobal.utils.register.get_rweapon_silenced_sound = function(weapon_name)
	return NSGlobal.register.override.rweapon_silenced_sounds[weapon_name]
end

-- Get default silenced swing sound for given type
-- @param rtype string
-- @return string swing_sound_name
NSGlobal.utils.register.get_default_silenced_sound = function(rtype)
	return NSGlobal.register.default.rweapon_type_silenced_sounds[rtype]
end

-- Get base sound increase value for given ammo type
-- @param ammo_type string
-- @return number sound value
NSGlobal.utils.register.get_ammo_type_sound_increase = function(ammo_type)
	local sandbox_name = NSGlobal.utils.register.convert_to_sandbox_name(ammo_type, "sound")
	if sandbox_name == nil then
		return nil
	end
	return SandboxVars.NS[sandbox_name]
end

-- ########################################
-- ######### Section: Experimental ########
-- ########################################

-- Add search string for given type to search list
-- @param search_string string
-- @param rtype string
NSGlobal.utils.register.add_search_rweapon_type = function(search_string, rtype)
	if NSGlobal.register.default.rweapon_types_keywords[rtype] ~= nil then
		table.insert(NSGlobal.register.default.rweapon_types_keywords[rtype], search_string)
	end
end

-- ########################################
-- ########### Section: Sandbox ###########
-- ########################################

-- Convert ingame sandbox variable name to sandbox name
-- @param ingame_name string
-- @param type string
-- @return string sandbox_name
NSGlobal.utils.register.convert_to_sandbox_name = function(ingame_name, sandbox_type)
	if sandbox_type == "ammo" then
		return NSGlobal.register.mapping.ammo_mult_ingame_to_sandbox[ingame_name]
	elseif sandbox_type == "sound" then
		return NSGlobal.register.mapping.ammo_sound_to_sandbox[ingame_name]
	elseif sandbox_type == "distribution" then
		return NSGlobal.register.mapping.ammo_dist_ingame_to_sandbox[ingame_name]
	elseif sandbox_type == "craft" then
		return NSGlobal.register.mapping.ammo_crafting_ingame_to_sandbox[ingame_name]
	else
		return nil
	end
end

-- Convert sandbox variable name to ingame name
-- @param sandbox_name string
-- @return string ingame_name
NSGlobal.utils.register.convert_to_ingame_name = function(sandbox_name, sandbox_type)
	if sandbox_type == "ammo" then
		return NSGlobal.register.mapping.ammo_mult_sandbox_to_ingame[sandbox_name]
	elseif sandbox_type == "sound" then
		return NSGlobal.register.mapping.ammo_sound_sandbox_to_ingame[sandbox_name]
	elseif sandbox_type == "distribution" then
		return NSGlobal.register.mapping.ammo_dist_sandbox_to_ingame[sandbox_name]
	elseif sandbox_type == "craft" then
		return NSGlobal.register.mapping.ammo_crafting_sandbox_to_ingame[sandbox_name]
	else
		return nil
	end
end

-- Get ammo multiplier for given ammo type
-- @param ammo_type string
-- @return number multiplier
NSGlobal.utils.register.get_ammo_type_multiplier = function(ammo_type)
	local sandbox_name = NSGlobal.utils.register.convert_to_sandbox_name(ammo_type, "ammo")
	if sandbox_name == nil then
		return 1.0
	end
	return 1 - SandboxVars.NS[sandbox_name] / 100
end

-- Get whether ammo type is craftable
-- @param ammo_type string
-- @return boolean
NSGlobal.utils.register.get_ammo_type_craftable = function(ammo_type)
	local sandbox_name = NSGlobal.utils.register.convert_to_sandbox_name(ammo_type, "craft")
	if sandbox_name == nil then
		return false
	end
	return SandboxVars.NS[sandbox_name]
end

-- ########################################
-- ########### Section: Validate ##########
-- ########################################

-- Check if weapon name is valid
-- @param weapon_name string
-- @return boolean
NSGlobal.utils.register.is_weapon_valid = function(weapon_name)
	-- check whether weapon name is excluded specifically
	local weapon_exclusions = NSGlobal.register.override.denylist_weapon_name
	for _, v in ipairs(weapon_exclusions) do
		if v == weapon_name then
			return false
		end
	end
	return true
end

-- Check if ammo type is valid
-- @param ammo_type string
-- @param include_exclude boolean (optional)
-- @return boolean
NSGlobal.utils.register.is_ammo_type_valid = function(ammo_type, include_exclude)
	local include_exclude = include_exclude or true
	-- first, check whether ammo is excluded specifically
	if include_exclude then
		local ammo_exlusions = NSGlobal.register.override.denylist_ammo_type
		for _, v in ipairs(ammo_exlusions) do
			if v == ammo_type then
				return false
			end
		end
	end

	-- then, check whether ammo is supported
	local supported_ammo_names = NSGlobal.register.default.supported_ammo_types
	for _, v in ipairs(supported_ammo_names) do
		if v == ammo_type then
			return true
		end
	end

	return false
end

-- ########################################
-- ######### Section: Debugging ###########
-- ########################################

-- Print debug message if debug is enabled
-- @param text string
NSGlobal.utils.register.print_debug = function(text)
	if NSGlobal.register.debug then
		print(text)
	end
end
