----------------------------------------------------------------------------------------------------------------------
--	Схема лагерь. Чудак(и) у костра.
--	автор: Диденко Руслан (Stohe)
--	TODO:
----------------------------------------------------------------------------------------------------------------------
--function printf()
--end

local ActionToStateTable = 	{
	idle = {director = { "sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}},
	harmonica = {director = {"harmonica"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}},
	guitar = {director = {"guitar"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}},
	story = {director = {"sit", "sit_ass", "sit_knee"}, listener = {"sit", "sit_ass", "sit_knee", "eat_bread", "eat_kolbasa", "eat_vodka", "eat_energy"}},
}

local ActionAvailabilityTable = {
	eat_bread = "kamp_eat_bread",
	eat_kolbasa = "kamp_eat_kolbasa",
	eat_vodka = "kamp_drink_vodka",
	eat_energy = "kamp_drink_energy",
}

local ActionTimingTable = {
	sit = {min = 60*2*1000,max = 60*10*1000},
	sit_ass = {min =60*2*1000,max = 60*10*1000},
	sit_knee = {min = 60*2*1000,max = 60*10*1000}
}


kamps = {}
---------------------------------------------------------------------------------------------------------------------
--Evaluators
----------------------------------------------------------------------------------------------------------------------
--' Условие завершения скрипта
class "evaluator_kamp_end" (property_evaluator)
function evaluator_kamp_end:__init(name, storage) super (nil, name)
	self.a = storage
end
function evaluator_kamp_end:evaluate()
	return not xr_logic.is_active(self.object, self.a)
end
--' Находимся ли мы на заданной позиции
class "evaluator_on_position" (property_evaluator)
function evaluator_on_position:__init(name, storage) super (nil, name)
	self.a = storage
end
function evaluator_on_position:evaluate()
		if self.object:level_vertex_id() == self.a.pos_vertex then
		return true
	end
	return false
end
----------------------------------------------------------------------------------------------------------------------
--Actions
----------------------------------------------------------------------------------------------------------------------
--' Идет в заданную область
class "action_go_position" (action_base)
function action_go_position:__init (npc_name,action_name,storage) super (nil,action_name)
	self.a = storage
end
function action_go_position:initialize()
	action_base.initialize(self)
--	self.object:set_node_evaluator()
--	self.object:set_path_evaluator()
	self.object:set_desired_position()
	self.object:set_desired_direction()

	self.a.pos_vertex = nil
	self.a.npc_position_num = nil
	self.a.signals = {}
end
function action_go_position:execute ()
	action_base.execute (self)

	-- Спрашиваем где сидеть
	local tmp_pos_vertex, npc_position_num = kamps[self.a.center_point]:getDestVertex(self.object, self.a.radius)

    if tmp_pos_vertex == nil then
        return
    end

	if self.a.npc_position_num ~= npc_position_num then
		self.a.npc_position_num = npc_position_num
		self.a.pos_vertex = tmp_pos_vertex

		--' Определяем куда смотреть.
		self.a.pp = patrol(self.a.center_point):point(0)

		local dir = vector():set(math.random(-1,1), 0, math.random(-1,1))
		dir:normalize()

		local delta_dist = math.random(0,0.5)
 		self.a.pp.x = self.a.pp.x + dir.x * delta_dist
		self.a.pp.z = self.a.pp.z + dir.z * delta_dist



		self.object:set_dest_level_vertex_id(self.a.pos_vertex)
		--printf("vertex_position")
		local desired_direction = vector():sub(self.a.pp,level.vertex_position(self.a.pos_vertex))
		--printf("desired_direction = %s", vec_to_str(desired_direction))
		if desired_direction ~= nil and not utils.vector_cmp(desired_direction, vector():set(0,0,0)) then
			desired_direction:normalize()
			self.object:set_desired_direction(desired_direction)
		end
		self.object:set_path_type(game_object.level_path)
		state_mgr.set_state(self.object, self.a.def_state_moving)
	end
end
function action_go_position:finalize ()
	action_base.finalize (self)
end

--' Просто сидит и втыкает
class "action_wait" (action_base)
function action_wait:__init (npc_name,action_name,storage) super (nil,action_name)
	self.a = storage
end
function action_wait:initialize()
	action_base.initialize(self)
--	self.object:set_node_evaluator()
--	self.object:set_path_evaluator()
	self.object:set_desired_position()
	self.object:set_desired_direction()

	local avail_actions = xr_animpoint_predicates.associations[self.a.description]
	self.a.approved_actions = {}

	for k,v in pairs(avail_actions) do
		-- Убираем те действия, которые не подходят по прекондишну
		if v.predicate(self.object:id(),true)==true then
			table.insert(self.a.approved_actions, v)
			kamps[self.a.center_point]:AddAvailableAction(self.object,v.name)
		end
	end
	
	kamps[self.a.center_point]:increasePops(self.object)
	
end
function action_wait:activate_scheme()
	self.a.signals = {}
end
function action_wait:execute()
	-- action_base.execute (self)

	-- --' повернуть его лицом к центру
	-- state_mgr.set_state(self.object, "sit", nil, nil, {look_position = self.a.pp})

	action_base.execute (self)
	local state = kamps[self.a.center_point]:updateNpc(self.object)

	--' повернуть его лицом к центру
	state_mgr.set_state(self.object, state, nil, nil, {look_position = self.a.pp})
end
function action_wait:finalize()
	kamps[self.a.center_point]:decreasePops(self.object)
	action_base.finalize (self)
end
function action_wait:deactivate(npc)
	kamps[self.a.center_point]:removeNpc(npc)
end
function action_wait:death_callback(npc)
	kamps[self.a.center_point]:removeNpc(npc)
end
function action_wait:net_destroy(npc)
	kamps[self.a.center_point]:removeNpc(npc)
end


class "CKampManager"
function CKampManager:__init(path)
	self.kamp_name = path
	self.patrol = patrol(path)
	--self.center = self.patrol:level_vertex_id(0)
	self.position = {{dir = vector():set(1, 0, 0),	used = nil},
					 {dir = vector():set(1, 0, 1),	used = nil},
					 {dir = vector():set(0, 0, 1),	used = nil},
					 {dir = vector():set(-1, 0, 1),	used = nil},
					 {dir = vector():set(-1, 0, 0),	used = nil},
					 {dir = vector():set(-1, 0, -1),used = nil},
					 {dir = vector():set(0, 0, -1),	used = nil},
					 {dir = vector():set(1, 0, -1),	used = nil}}
	self.npc = {}
	self.population = 0
end
function CKampManager:selectPosition(npc_id)
	-- создаем список доступных позиций
	--printf("KAMP. [%s] called select position", npc_id)
	local free = {}
	for k,v in pairs(self.position) do
		if v.used == nil then
			table.insert(free, k)
		end
	end
	--' затем из доступных позиций выбрать рандомно одну.
	if #free > 0 then
		--printf("KAMP [%s] free node > 0", npc_id)
		local rr = math.random(#free)
		self.position[free[rr]].used = npc_id
		self.npc[npc_id].position = free[rr]
	end
	--printf("KAMP [%s] npc table", npc_id)
	--print_table(self.npc)
	--printf("KAMP [%s] position table", npc_id)
	--print_table(self.position)
end
function CKampManager:getDestVertex(npc, radius)
	local npc_id = npc:id()
	--printf("get dest Vertex called [%s]", npc_id)
	if self.npc[npc_id].position == nil then
		--printf("-------debug_info------------- %s", self.kamp_name)
		--print_table(self.npc)
		--printf("-------debug_info------------- %s", self.kamp_name)
		--print_table(self.position)
		--printf("-------debug_info------------- %s", self.kamp_name)
		abort("get dest Vertex: nil [%s]", npc_id)
		return nil
	end

	-- высчитываем вертех по направлению
	-- Берем позицию в заданном направлении, затем берем ниарест точку от нее.

	local pp = self.patrol:point(0)
	local dir = self.position[self.npc[npc_id].position].dir

	-- Считаем рандомное отклонение направления.
	dir.x = dir.x + math.random(-1,1)/5
	dir.z = dir.z + math.random(-1,1)/5
	dir:normalize()

	radius = radius + math.random(-0.3,0.3)

 	local dest_vertex = 4294967295
	
 	while dest_vertex == 4294967295 do
 		local tmp_pos = vector():set(0,0,0)
		tmp_pos.x = pp.x + dir.x * radius
		tmp_pos.z = pp.z + dir.z * radius
		tmp_pos.y = pp.y
	 	dest_vertex = level.vertex_id(tmp_pos)
	 	if dest_vertex == 4294967295 then
	 		if radius < 1 then
                SemiLog("Invalid AI map at kamp point ["..self.kamp_name.."]")
                return nil
	 		else
	 			radius = radius - 0.5
	 		end
	 	end
 	end

	if not npc:accessible(dest_vertex) then
		--printf("vertex_position %s", tostring(dest_vertex))
		local vp = level.vertex_position(dest_vertex)
		--printf("Nearest for npc[%s] kamp [%s] position [%s:%s:%s]", npc:name(), tostring(self.kamp_name),tostring(vp.x), tostring(vp.y), tostring(vp.z))
		local nearest_vertex = npc:accessible_nearest(vp, vector():set(0,0,0))
		--printf("Nearest for npc[%s] kamp [%s] position [%s:%s:%s]", npc:name(), tostring(self.kamp_name), vec_to_str(nearest_vertex))
		return nearest_vertex, self.npc[npc_id].position
	end

	return dest_vertex, self.npc[npc_id].position
end

function CKampManager:updateNpc(npc)

	local tbl = {}
	local npc_id = npc:id()
	local camp_action, is_director

	if not self.camp then 
		camp_action = "idle"
		is_director = false
	else
		camp_action, is_director = self.camp:get_camp_action(npc_id)
	end

		
	if(is_director) then
		tbl = ActionToStateTable[camp_action].director
	else
		tbl = ActionToStateTable[camp_action].listener
	end

	if self.npc[npc_id].begin == nil or time_global() - self.npc[npc_id].begin >= self.npc[npc_id].state_idle or self.npc[npc_id].is_director ~= is_director then
		self.npc[npc_id].begin = time_global()
		if (self.npc[npc_id].camp_action == "story" or self.npc[npc_id].camp_action == "idle") and (camp_action =="idle" or camp_action == "story") and self.npc[npc_id].is_director ~= is_director then
			self.npc[npc_id].is_director  = is_director
			self.npc[npc_id].camp_action = camp_action
			return self.npc[npc_id].state_selected
		end
		local Action = "kamp"
		repeat 
			local rnd = math.random(#tbl)
			self.npc[npc_id].state_selected = tbl[rnd]
			if ActionAvailabilityTable[self.npc[npc_id].state_selected] then 
				Action = ActionAvailabilityTable[self.npc[npc_id].state_selected]
			else
				Action = "kamp"
			end
		until self.npc[npc_id].AvailableActions[Action]

		if ActionTimingTable[self.npc[npc_id].state_selected] ~= nil then
			self.npc[npc_id].state_idle = math.random(ActionTimingTable[self.npc[npc_id].state_selected].min,ActionTimingTable[self.npc[npc_id].state_selected].max)
		else
			self.npc[npc_id].state_idle = math.random(15*1000,60*1000)
		end
		self.npc[npc_id].is_director  = is_director
		self.npc[npc_id].camp_action = camp_action
	end
	return self.npc[npc_id].state_selected
	
end

function CKampManager:addNpc(npc)
	if self.npc[npc:id()] ~= nil then
		return
	end

	self.npc[npc:id()] = {name = npc:name(), position = nil, AvailableActions = {}}
	self:selectPosition(npc:id())
	
end
function CKampManager:removeNpc(npc)
	local npc_id = npc:id()
	if self.npc[npc_id] ~= nil then
		self.position[self.npc[npc_id].position].used = nil
		self.npc[npc_id] = nil
	end
	
	if self.camp ~= nil then
		self.camp:unregister_npc(npc:id())
	end
end

function CKampManager:increasePops(npc)
	self.population = self.population + 1
	
	if not self.camp then
		self.camp = sr_camp.get_current_camp(self.patrol:point(0))
	end
	
	if self.camp ~= nil then
		self.camp:register_npc(npc:id())
	end
	-- local campfire = bind_campfire.campfire_table[self.kamp_name.."_campfire"]
	-- if self.population > 0 and campfire ~= nil and not campfire:is_on() then
	-- 	campfire:turn_on()
	-- end
end
function CKampManager:decreasePops(npc)
	self.population = self.population - 1
	-- local campfire = bind_campfire.campfire_table[self.kamp_name.."_campfire"]
	-- if self.population < 1 and campfire ~= nil and campfire:is_on() then
	-- 	campfire:turn_off()
	-- end
end

function CKampManager:AddAvailableAction(NPC,StateName)
	self.npc[NPC:id()].AvailableActions[StateName] = true
end



----------------------------------------------------------------------------------------------------------------------
--Kamp binder
----------------------------------------------------------------------------------------------------------------------
function add_to_binder(object, ini, scheme, section, storage)
	local operators		= {}
	local properties	= {}

	local manager = object:motivation_action_manager()

	properties["kamp_end"]		= xr_evaluators_id.stohe_kamp_base + 1
	properties["on_position"]	= xr_evaluators_id.stohe_kamp_base + 2
	properties["contact"]		= xr_evaluators_id.stohe_meet_base + 1

	operators["go_position"]	= xr_actions_id.stohe_kamp_base + 1
	operators["wait"]		= xr_actions_id.stohe_kamp_base + 3
	properties["state_mgr_logic_active"] 	= xr_evaluators_id.state_mgr + 4

	-- Evaluators
	manager:add_evaluator (properties["kamp_end"], 		this.evaluator_kamp_end		("kamp_end", storage, "kamp_end"))
	manager:add_evaluator (properties["on_position"],	this.evaluator_on_position	("kamp_on_position", storage, "kamp_on_position"))

	-- Actions
	local action = this.action_wait (object:name(),"action_kamp_wait", storage)
	action:add_precondition		(world_property(stalker_ids.property_alive, true))
	action:add_precondition		(world_property(stalker_ids.property_danger,false))
	action:add_precondition		(world_property(stalker_ids.property_enemy,	false))
	action:add_precondition		(world_property(stalker_ids.property_anomaly,false))
	xr_motivator.addCommonPrecondition(action)
	action:add_precondition		(world_property(properties["on_position"], 	true))
	action:add_effect 			(world_property(properties["kamp_end"], 	true))
	action:add_effect			(world_property(properties["state_mgr_logic_active"], false))
	manager:add_action (operators["wait"], action)
	xr_logic.subscribe_action_for_events(object, storage, action)

	action = this.action_go_position (object:name(),"action_go_kamp", storage)
	action:add_precondition		(world_property(stalker_ids.property_alive, true))
	action:add_precondition		(world_property(stalker_ids.property_danger,false))
	action:add_precondition		(world_property(stalker_ids.property_enemy,	false))
	action:add_precondition		(world_property(stalker_ids.property_anomaly,false))
	xr_motivator.addCommonPrecondition(action)
	action:add_precondition 	(world_property(properties["on_position"], 	false))
	action:add_effect 			(world_property(properties["on_position"], 	true))
	action:add_effect			(world_property(properties["state_mgr_logic_active"], false))
	manager:add_action (operators["go_position"], action)

	action = manager:action (xr_actions_id.alife)
	action:add_precondition		(world_property(properties["kamp_end"],		true))

end
-- включение лагеря
function set_scheme(npc, ini, scheme, section, gulag_name)
	local st = xr_logic.assign_storage_and_bind(npc, ini, scheme, section)

	st.logic     = xr_logic.cfg_get_switch_conditions(ini, section, npc)

	st.center_point = utils.cfg_get_string(ini, section, "center_point", npc, true,  gulag_name)
	st.radius		= utils.cfg_get_number(ini, section, "radius", npc, false, 2)
	st.description  = "kamp"
	st.base_action  = "kamp"



	if kamps[st.center_point] == nil then
		kamps[st.center_point] = CKampManager(st.center_point)
	end
	kamps[st.center_point]:addNpc(npc)
	st.pos_vertex = nil

	st.def_state_moving = utils.cfg_get_string(ini, section, "def_state_moving", npc, false, "", "walk")
end
