--[[

	Copyright (C) 2012-2015 Alundaio
	This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
	
	Callback and module manager for easy plug-in based script management
	by Alundaio
	
	1/31/2016
	axr_main now auto-executes any script that has on_game_load method. To create a new module, simply add a on_game_start function
	
--]]

-- axr_options.ini
config = ini_file_ex("axr_options.ltx",true)

-- load default rspec when axr_options created first time
if not (config:section_exist("temp")) then
	get_console():execute("cfg_load " .. getFS():update_path('$game_config$', '').."rspec_default.ltx")
	config:w_value("temp","rspec_default",true)
	config:save()
end
-------------------------------------------------------------------------------------------------

-- debug scan/simulation
function check_on_launch()
	--get_console():execute("clear_log")
	
-- Simulation
	--treasure_manager.simulate_stash_creation(700)
	--death_manager.simulate_death_drops(10)
	--xrs_rnd_npc_loadout.simulate_npc_loadouts(20,nil,nil,{"primary","secondary"})
	
-- Generating
	--itms_manager.generate_boosters_list()
	
-- Others
	--z_xml_calculator.start()
	--z_generate_prefetch.start()
end
check_on_launch()
--

-------------------------------------------------------------------------------------------------

-- Valid Callbacks (named intercepts to avoid namespace conflicts)
-- You must register a callback before or during on_game_load (ie. bind_actor net_spawn)
-- All unused callbacks will have their table deleted from intercepts (see on_game_load for implementation)
local intercepts = {

	--// Player
	on_before_level_changing	            = {}, -- Params: ()
	on_level_changing			            = {}, -- Params: ()
	actor_on_before_death		            = {}, -- Params: (<number>,<table>)
	actor_on_net_destroy		            = {}, -- Params: (<binder>)
	actor_on_first_update		            = {}, -- Params: (<binder>,<?>)
	actor_on_update				            = {}, -- Params: (<binder>,<?>)
	actor_on_weapon_fired		            = {}, -- Params: (<game_object>,<game_object>,<number>,<number>,<number>,<number>)
	actor_on_weapon_jammed		            = {}, -- Params: (<game_object>)
	actor_on_weapon_no_ammo		            = {}, -- Params: (<game_object>,<number>)
	actor_on_weapon_lower		            = {}, -- Params: (<game_object>)
	actor_on_weapon_raise	         	    = {}, -- Params: (<game_object>)
	actor_on_weapon_reload		            = {}, -- Params: (<game_object>,<number>)
	actor_on_weapon_zoom_in		            = {}, -- Params: (<game_object>)
	actor_on_weapon_zoom_out	            = {}, -- Params: (<game_object>)
	actor_on_item_take			            = {}, -- Params: (<game_object>)
	actor_on_item_take_from_box             = {}, -- Params: (<game_object>,<game_object>)
	actor_on_item_put_in_box 	            = {}, -- Params: (<game_object>,<game_object>)
	actor_on_item_drop			            = {}, -- Params: (<game_object>)
	actor_on_item_use			            = {}, -- Params: (<game_object>,<string>)
	actor_on_item_before_use			    = {}, -- Params: (<game_object>,<table>)
	actor_on_item_before_pickup				= {}, -- Params: (<game_object>,<table>)
	actor_item_to_belt			            = {}, -- Params: (<game_object>)
	actor_item_to_ruck			            = {}, -- Params: (<game_object>)
	actor_item_to_slot			            = {}, -- Params: (<game_object>)
	actor_on_trade				            = {}, -- Params: (<game_object>,<?>,<number>)
	actor_on_init				            = {}, -- Params: (<binder>)
	actor_on_reinit				            = {}, -- Params: (<binder>)
--	actor_on_torch_enabled		            = {},	-- TODO: Export from engine instead
--	actor_on_torch_disabled		            = {},	-- TODO: Export from engine instead
	actor_on_info_callback		            = {}, -- Params: (<game_object>,<number>)
	actor_on_hit_callback		            = {}, -- Params: (<game_object>,<number>,<vector>,<game_object>,<number>)
	actor_on_attach_vehicle		            = {}, -- Params: (<game_object>)
	actor_on_detach_vehicle		            = {}, -- Params: (<game_object>)
	actor_on_use_vehicle		            = {}, -- Params: (<game_object>)
	actor_on_hud_animation_play				= {}, -- Params: (<table>,<game_object>)
	actor_on_hud_animation_end              = {}, -- Params: (<game_object>,<string>,<?>,<?>,<number>)
	actor_on_hud_animation_mark				= {}, -- Params: (<number>,<string>)
	actor_on_sleep				            = {}, -- Params: (<number>)
	actor_on_foot_step			            = {}, -- Params: (<game_object>,<number>,<?>,<?>,<?>)
	actor_on_interaction		            = {}, -- Params: (<string>,<game_object>,<string>)
	actor_on_before_hit			            = {}, -- Params: (<game_object>,<SHit>,<number>,<table>)
	actor_on_before_hit_belt				= {}, -- Params: (<table>,<number>,<number>)
	actor_on_weapon_before_fire	            = {}, -- Params: (<table>)
	actor_on_feeling_anomaly	            = {}, -- Params: (<game_object>,<table>)
	actor_on_leave_dialog			        = {}, -- Params: (<number>)
	actor_on_stash_create                   = {}, -- Params: (<table>)
	actor_on_stash_remove                   = {}, -- Params: (<table>)
	actor_on_frequency_change               = {}, -- Params: (<number>,<number>)
	actor_on_achievement_earned		        = {}, -- Params: (<string>,<string>)
	actor_on_movement_changed				= {}, -- Params: (<number>)
	actor_on_footstep						= {}, -- Params: (<string>,<number>,<boolean>,<table>)
	actor_on_jump							= {}, -- Params: ()
	actor_on_land							= {}, -- Params: (<number>)
	actor_on_before_throwable_select		= {}, -- Params: (<table>)

	--// NPCs
	npc_on_use 					            = {}, -- Params: (<game_object>,<game_object>)
	npc_on_choose_weapon					= {}, -- Params: (<game_object>,<game_object>,<table>)
	npc_on_item_take 			            = {}, -- Params: (<game_object>,<game_object>)
	npc_on_item_take_from_box	            = {}, -- Params: (<game_object>,<game_object>,<game_object>)
	npc_on_item_drop 			            = {}, -- Params: (<game_object>,<game_object>)
	npc_on_net_spawn			            = {}, -- Params: (<game_object>,<server_object>)
	npc_on_net_destroy 			            = {}, -- Params: (<game_object>)
	npc_on_update				            = {}, -- Params: (<game_object>,<table>)
	npc_on_before_hit						= {}, -- Params: (<game_object>,<SHit>,<number>,<table>)
	npc_on_hit_callback			            = {}, -- Params: (<game_object>,<number>,<vector>,<game_object>,<number>)
	npc_on_death_callback		            = {}, -- Params: (<game_object>,<game_object>)
	npc_on_fighting_actor		            = {}, -- Params: (<game_object>)
	npc_on_weapon_strapped		            = {}, -- Params: (<game_object>,<game_object>)
	npc_on_weapon_unstrapped	            = {}, -- Params: (<game_object>,<game_object>)
	npc_on_weapon_drop			            = {}, -- Params: (<game_object>,<game_object>)
	npc_on_hear_callback		            = {}, -- Params: (<game_object>,<number>,<?>,<number>,<number>,<vector>)
	npc_on_get_all_from_corpse 	            = {}, -- Params: (<game_object>,<game_object>,<game_object>,<boolean>)
	npc_on_eval_danger 	            		= {}, -- Params: (<game_object>,<table>)
	npc_shot_dispersion						= {}, -- Params: (<game_object>,<game_object>,<number>,<number>,<table>)
	anomaly_on_before_activate             	= {}, -- Params: (<game_object>,<game_object>)
	
	--// Mutants
	monster_on_update				        = {}, -- Params: (<game_object>,<table>)
	monster_on_before_hit					= {}, -- Params: (<game_object>,<SHit>,<number>,<table>)
	monster_on_hit_callback			        = {}, -- Params: (<game_object>,<number>,<vector>,<game_object>,<number>)
	monster_on_net_spawn			        = {}, -- Params: (<game_object>,<server_object>)
	monster_on_net_destroy 			        = {}, -- Params: (<game_object>)
	monster_on_death_callback		        = {}, -- Params: (<game_object>,<game_object>)
	monster_on_actor_use_callback           = {}, -- Params: (<game_object>,<game_object>)
	monster_on_loot_init                    = {}, -- Params: (<game_object>,<table>)
	burer_on_before_weapon_drop             = {}, -- Params: (<game_object>,<game_object>)
	
	--// Physical objects
	physic_object_on_hit_callback 	        = {}, -- Params: (<game_object>,<number>,<vector>,<game_object>,<number>)
	physic_object_on_use_callback 	        = {}, -- Params: (<game_object>,<game_object>)
	
	--// Vehicles
	heli_on_hit_callback			        = {}, -- Params: (<game_object>,<number>,<nil>,<game_object>,<nil>)
	vehicle_on_death_callback				        = {}, -- Params: (<number>)

	--// Squads
	squad_on_npc_creation		            = {}, -- Params: (<server_object>,<server_object>,<server_object>)
	squad_on_enter_smart		            = {}, -- Params: (<server_object>,<server_object>)
	squad_on_leave_smart		            = {}, -- Params: (<server_object>,<server_object>)
	squad_on_npc_death			            = {}, -- Params: (<server_object>,<server_object>,<server_object>)
	squad_on_update				            = {}, -- Params: (<server_object>)
	squad_on_first_update		            = {}, -- Params: (<server_object>)
	squad_on_add_npc			            = {}, -- Params: (<server_object>,<server_object>,<string>,<vector>,<number>,<number>)
--	squad_on_register			            = {}, -- use server_entity_on_register check "sim_squad_scripted" as type_name
--	squad_on_unregister			        	= {}, -- use server_entity_on_unregister

--[[
	WARNING!!!!!:
	the following 2 callbacks will ALWAYS fire on level change/loaded save because the old data isn't saved for compatibility purpose
	it's up to the user to check for the case in which old level name/old game vertex is nil in their code if they want to use this
]]
	squad_on_after_game_vertex_change		= {}, -- Params: (<server_object>,<number>,<number>,<boolean>)
	squad_on_after_level_change				= {}, -- Params: (<server_object>,<string>,<string>)


	--// smart_terrain.script
	smart_terrain_on_update		            = {}, -- Params: (<server_object>)
	on_try_respawn 				            = {}, -- Params: (<server_object>,<table>)

	--// Server objects
	server_entity_on_register				= {}, -- Params: (<server_object>,<string>)
	server_entity_on_unregister				= {}, -- Params: (<server_object>,<string>)
	fill_start_position			            = {}, -- Params: ()
	se_stalker_on_spawn			            = {}, -- Params: (<server_object>)
--	se_actor_on_register		            = {},	-- use server_entity_on_register check "se_actor" as type_name
--	se_actor_on_unregister		        	= {},	-- use server_entity_on_unregister
	se_actor_on_STATE_Write		            = {}, -- Params: (<server_object>)
	se_actor_on_STATE_Read		            = {}, -- Params: (<server_object>)

	--// GUI
	ActorMenu_on_before_init_mode			= {}, -- Params: (<string>,<table>,<game_object>)
	ActorMenu_on_mode_changed			    = {}, -- Params: (<number>,<number>)
	ActorMenu_on_item_drag_drop		        = {}, -- Params: (<game_object>,<game_object>,<number>,<number>)
	ActorMenu_on_item_focus_receive         = {}, -- Params: (<game_object>)
	ActorMenu_on_item_focus_lost 	        = {}, -- Params: (<game_object>)
	ActorMenu_on_item_before_move 	        = {}, -- Params: (<table>,<number>,<game_object>,<string>,,<number>)
	ActorMenu_on_item_after_move 	        = {}, -- Params: (<number>,<game_object>,<string>,,<number>)
	ActorMenu_on_trade_started				= {}, -- Params: ()
	ActorMenu_on_trade_closed				= {}, -- Params: ()
	
	GUI_on_show                             = {}, -- Params: (<string>,<string>)
	GUI_on_hide                             = {}, -- Params: (<string>,<string>)
	
	map_spot_menu_add_property		        = {}, -- Params: (<CUIWindow>,<number>,<string>,<string>)
	map_spot_menu_property_clicked 	        = {}, -- Params: (<CUIWindow>,<number>,<string>,<string>)

	main_menu_on_keyboard		            = {}, -- Params: (<number>,<number>,<CUIScriptWnd>,<boolean>)
	main_menu_on_init			            = {}, -- Params: (<CUIScriptWnd>)
--	main_menu_on_init_callbacks	            = {},
	main_menu_on_quit			            = {}, -- Params: (<CUIScriptWnd>)

	on_screen_resolution_changed			= {}, -- Params: ()

	--// Technical
	on_game_load				            = {}, -- Params: (<binder>)
	on_key_press				            = {}, -- Params: (<number>)
	on_key_release				            = {}, -- Params: (<number>)
	on_key_hold					            = {}, -- Params: (<number>)
	on_before_key_press						= {}, -- Params: (<number>,<number>,<boolean>,<table>)
	on_before_key_release					= {}, -- Params: (<number>,<number>,<boolean>,<table>)
	on_before_key_hold						= {}, -- Params: (<number>,<number>,<boolean>,<table>)
	on_option_change                        = {}, -- Params: ()
	on_localization_change		            = {}, -- Params: ()
	on_console_execute			            = {}, -- Params: (<string>,<string>,<string>,...) command parts
	on_before_save_input		        	= {}, -- Params: (<number>,<number>,<table>)
	on_before_load_input		        	= {}, -- Params: (<number>,<number>,<table>)
	
	--// Files
	save_state						        = {}, -- Params: (<table>)
	load_state						        = {}, -- Params: (<table>)
	on_pstor_save_all			            = {}, -- Params: (<game_object>,<?>)
	on_pstor_load_all			            = {}, -- Params: (<game_object>,<?>)

	--// Others
	on_enemy_eval							= {}, -- Params: (<game_object>,<game_object>,<table>)
	on_before_surge							= {}, -- Params: (<table>)
	on_before_psi_storm						= {}, -- Params: (<table>)
	on_get_item_cost						= {}, -- look at bottom of utils_item.script for detailed explanation
	bullet_on_hit							= {}, -- Params: (<string>,<game_object>,<vector>,<vector>,<string>,<number>,<number>)
	
}

-----------------------------------------------------------
-- Global Callback Register
-- param 1 - name as type<string> (ie. intercepts[name])
-- param 2 - function to execute as type<function> or userdata with name as function
-----------------------------------------------------------
function callback_add(name)
	if (not intercepts[name]) then
		intercepts[name] = {}
	else
		printf("![axr_main callback_add] callback %s already exists!",name)
		callstack()
	end
end

function callback_set(name,func_or_userdata)
	if (func_or_userdata == nil) then 
		printf("![axr_main callback_set] trying to set callback %s to nil function!",name)
		callstack()
		return
	end
	if (intercepts[name]) then
		intercepts[name][func_or_userdata] = true
	else
		printf("![axr_main callback_set] callback %s doesn't exist!",name)
		callstack()
	end
end

function callback_unset(name,func_or_userdata)
	if (intercepts[name]) then
		intercepts[name][func_or_userdata] = nil
	else
		printf("![axr_main callback_unset] callback %s doesn't exist!",name)
		callstack()
	end
end

function make_callback(name,...)
	if (intercepts[name]) then
		for func_or_userdata,v in pairs(intercepts[name]) do 
			if (type(func_or_userdata) == "function") then 
				func_or_userdata(...)
			elseif (func_or_userdata[name]) then
				func_or_userdata[name](func_or_userdata,...)
			end
		end
	else
		printf("![axr_main make_callback] can't make callback to non existing intercept %s!",name)
		callstack()
	end
end

--------------------------------------------------------------------------------------------------------------------
-- ON GAME START
--------------------------------------------------------------------------------------------------------------------
function on_game_start()
	local ignore = { 
		["_G.script"] = true,
		["class_registrator.script"] = true,
		["game_registrator.script"] = true,
		["ui_registrator.script"] = true,
		["ce_new_attachable_item.script"] = true,
		["ce_new_game_dm.script"] = true,
		["sim_faction_brain_human.script"] = true,
		["sim_faction_brain_mutant.script"] = true,
		["ce_switcher.script"] = true,
		["axr_main.script"] = true,
		["lua_help.script"] = true,
		["rx_gl.script"] = true
	}
	
	local t = {}
	local size_t = 0 
	local f	= getFS()
	local flist = f:file_list_open_ex("$game_scripts$",bit_or(FS.FS_ListFiles,FS.FS_RootOnly),"*.script")
	local f_cnt = flist:Size()
	for	it=0, f_cnt-1 do
		local file = flist:GetAt(it)
		local file_name = file:NameShort()
		--printf("%s size=%s",file_name,file:Size())
		if (file:Size() > 0 and ignore[file_name] ~= true) then
			file_name = file_name:sub(0,file_name:len()-7)
			if (_G[file_name] and _G[file_name].on_game_start) then
				size_t = size_t + 1 
				t[size_t] = file_name -- load all scripts first
			end
		end
	end
	for i=1,#t do
		--printf("%s.on_game_start()",t[i])
		_G[ t[i] ].on_game_start()
	end
end

------------------
-- main_menu_on_init()
------------------
function main_menu_on_init(menu)
	xrs_dyn_music.main_menu_on(menu)
end

------------------
-- main_menu_on_quit()
------------------
function main_menu_on_quit(menu)
	xrs_dyn_music.main_menu_off(menu)
end

function actor_on_weapon_zoom_in()
	local wpn = db.actor:active_item()
	
	scoped_weapon_is_zoomed = wpn and IsWeapon(wpn) and wpn:weapon_is_scope()
	binoc_is_zoomed = wpn and (wpn:section() == "wpn_binoc_inv")
	weapon_is_zoomed = true
	
	SendScriptCallback("GUI_on_show", "UIZoom") -- to force hiding HUD UIs on Zooming
end 

function actor_on_weapon_zoom_out()
	scoped_weapon_is_zoomed = false
	binoc_is_zoomed = false
	weapon_is_zoomed = false
	SendScriptCallback("GUI_on_hide", "UIZoom") -- to force hiding HUD UIs on Zooming
end

------------------
-- server_entity_on_register
------------------
function server_entity_on_register(se_obj,type_name)
	local id = se_obj.id
	if (id == AC_ID) then
		story_objects.register(id,"actor")
	else
		story_objects.check_spawn_ini_for_story_id(se_obj)
	end
end

------------------
-- server_entity_on_unregister
------------------
-- good place to remove ids from persistent tables
function server_entity_on_unregister(se_obj,type_name)
	local id = se_obj.id
	local m_data = alife_storage_manager.get_state()
	if (m_data) then 
		if (m_data.se_object) then
			m_data.se_object[id] = nil
			--printf("$ alife_storage | cleaning server object [%s](%s) mdata", se_obj:name(), id)
		end
		if (m_data.game_object) then
			m_data.game_object[id] = nil
			--printf("$ alife_storage | cleaning game object [%s](%s) mdata", se_obj:name(), id)
		end
	end
	story_objects.unregister(id)
end