----------------------------------------------------------------------------------------------------------------------
--	Схема патруль. Мужики в патруле
----------------------------------------------------------------------------------------------------------------------
function printf()
end

local key = nil
local data = nil

--'patrol states
local patrol_move   =   0       -- просто движение
local patrol_hide   =   1       -- ракоход
local patrol_sprint =   2       -- очень быстро бежим
local patrol_run    =   3       -- просто несемся, аки лоси в кукурузу
local patrol_stop   =   4       -- стоим

local patrols = {}

local formations = {}
formations["line"] = {
                     { dir = vector ():set (-1, 0, 0), dist = 2 },
                     { dir = vector ():set (-1, 0, 0), dist = 4 },
                     { dir = vector ():set (-1, 0, 0), dist = 6 },
                     { dir = vector ():set (1, 0, 0), dist = 2 },
                     { dir = vector ():set (1, 0, 0), dist = 4 },
                     { dir = vector ():set (1, 0, 0), dist = 6 }
                     }
                     
formations["back"] = {
                     { dir = vector ():set (0.3, 0, -1), dist = 1.2 },
                     { dir = vector ():set (-0.3, 0, -1), dist = 2.4 },
                     { dir = vector ():set (0.3, 0, -1), dist = 3.6 },
                     { dir = vector ():set (-0.3, 0, -1), dist = 4.8 },
                     { dir = vector ():set (0.3, 0, -1), dist = 6 },
                     { dir = vector ():set (-0.3, 0, -1), dist = 7.2 }

--[[
                     { dir = vector ():set (-0.89442718, 0, -0.44721359), dist = 2.2360680 },
                     { dir = vector ():set (0.89442718, 0, -0.44721359), dist = 2.2360680 },
                     { dir = vector ():set (-0.37139067, 0, -0.92847669), dist = 5.3851647 },
                     { dir = vector ():set (0.37139067, 0, -0.92847669), dist = 5.3851647 },
                     { dir = vector ():set (-0.55470020, 0, -0.83205032), dist = 3.6055512 },
                     { dir = vector ():set (0.55470020, 0, -0.83205032), dist = 3.6055512 }
]]

                     }
formations["around"] = {
                     { dir = vector ():set (0.44721359, 0, -0.89442718), dist = 2.2360680 },
                     { dir = vector ():set (-0.44721359, 0, -0.89442718), dist = 2.2360680 },
                     { dir = vector ():set (1.0000000, 0, 0), dist = 2 },
                     { dir = vector ():set (-1, 0, 0), dist = 2 },
                     { dir = vector ():set (0.44721359, 0, 0.89442718), dist = 2.2360680 },
                     { dir = vector ():set (-0.44721359, 0, 0.89442718), dist = 2.2360680 }
                     }
                     

local accel_by_curtype = {
						walk	= "run",
						patrol	= "rush",
						raid	= "assault",
						sneak	= "sneak_run",
						sneak_run = "assault"
						}



class "PatrolManager"
function PatrolManager:__init(path_name)
    self.path_name = path_name
    self.npc_list = {}
    self.current_state = "patrol"
    self.commander_id = -1
    self.formation = "back"
    self.commander_lid = -1
    self.commander_dir = vector ():set (0, 0, 1)
    self.npc_count = 0
end
--' Добавление нового персонажа
function PatrolManager:add_npc(npc, leader)
    --'validate npc
    if npc == nil or npc:alive() == false or self.npc_list[npc:id()] ~= nil then 
       return 
    end

	--' Проверка на то что в патруле не больше 7 сталкеров (иначе будет вылетать дальше)
	if self.npc_count == 7 then
		abort("[XR_PATROL] attempt to add more then 7 npc. [%s]", npc:name())
		return
	end
    
    --' if this npc first in command then set him as commander
    self.npc_list[npc:id ()] = {soldier = npc, dir = vector ():set (1, 0, 0), dist = 0}
      
    self.npc_count = self.npc_count + 1
    --' если мужик первый или считается лидером, то установим, как лидера    
    if self.npc_count == 1 or leader == true then
       self.commander_id = npc:id ()
       printf ("[XR_PATROL] ASSIGNED NPC %s AS PATROL COMMANDER", npc:name ())
    end    
    
    printf ("[XR_PATROL] NPC %s added to patrol manager %s", npc:name (), self.path_name) 
    self:reset_positions ()
end
--' Удаление персонажа
function PatrolManager:remove_npc(npc)
    if npc == nil then return end
    if self.npc_list[npc:id()] == nil then
		return
	end
    printf ("[XR_PATROL] NPC %s removed from patrol manager %s", npc:name(), self.path_name) 
    self.npc_list[npc:id ()] = nil
    self.npc_count = self.npc_count - 1
    
    if npc:id () == self.commander_id then
       self.commander_id = -1
       self:reset_positions ()
    end    
end
--' Пересчет занимаемых позиций
function PatrolManager:reset_positions()
    local form_ = formations[self.formation]
    local index = 1
    for key, data in pairs(self.npc_list) do
        --установим командора, если это еще не сделано 
        if self.commander_id == -1 and index == 1 then
           self.commander_id = data.soldier:id ()
        end
        -- пересчитаем позиции
        if self.commander_id ~= self.npc_list[key].soldier:id () then
		   --printf("key[%s] index[%s]", tostring(key), tostring(index))
           self.npc_list[key].dir = form_[index].dir
           self.npc_list[key].dist = form_[index].dist
	       self.npc_list[key].vertex_id = -1
           self.npc_list[key].accepted = true

           index = index + 1
        end   
    end    
end
--' Установление формации
function PatrolManager:set_formation(formation)
    if formation == nil then
       abort ("Invalid formation (nil) for PatrolManager[%s]", self.path_name)
    end
    if formation ~= "around" and formation ~= "back" and formation ~= "line" then
       abort ("Invalid formation (%s) for PatrolManager[%s]", formation, self.path_name)
    end
    self.formation = formation
    self:reset_positions ()   
end
--' Получить командира патруля
function PatrolManager:get_commander(npc)
    if npc == nil then 
       abort ("Invalid NPC on call PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name)
    end
    
    --закешируем айдишник непися
    local npc_id = npc:id ()   
    --проверка непися на присутсвие в списке
    if self.npc_list[npc:id ()] == nil then
       abort ("NPC with name %s can't present in PatrolManager[%s]", npc:name (), self.path_name)
    end
    --проверка, чтобы командир не вздумал задавать глупых вопросов
    if npc:id () == self.commander_id then
       abort ("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name) 
    end       
    --получим данные командира
    local commander = self.npc_list[self.commander_id].soldier
    if commander == nil then
       abort ("Patrol commander not present in PatrolManager[%s]", self.path_name)  
    end   
    return commander
end
--' Получить параметры действия NPC
function PatrolManager:get_npc_command(npc)
    if npc == nil then 
       abort("Invalid NPC on call PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name)
    end
    
    --'закешируем айдишник непися
    local npc_id = npc:id ()   
    --'проверка непися на присутсвие в списке
    if self.npc_list[npc:id ()] == nil then
       abort("NPC with name %s can't present in PatrolManager[%s]", npc:name (), self.path_name)
    end
    --'проверка, чтобы командир не вздумал задавать глупых вопросов
    if npc:id() == self.commander_id then
       abort("Patrol commander called function PatrolManager:get_npc_command in PatrolManager[%s]", self.path_name) 
    end       
    --'получим данные командира
    local commander = self.npc_list[self.commander_id].soldier
    
    local dir = commander:direction()
    
    local pos = vector():set(0, 0, 0)
    local vertex_id = commander:location_on_path(5, pos)
	--' Если командир сам еще не дошел до пути (расстояние до вертекса больше чем 5 метров)
	--' то за основную точку берем вертекс командира
	if level.vertex_position(vertex_id):distance_to(self.npc_list[npc_id].soldier:position()) > 5 then
		vertex_id = commander:level_vertex_id()
	end

    --'уберем влияние вертикальной составляющей
    dir.y = 0
    dir:normalize()
    --'получим данные салаги
    local dir_s = self.npc_list[npc_id].dir
    local dist_s = self.npc_list[npc_id].dist
    
    --'расчет позиции для салаги
    --'сначала получим направление
    local angle = yaw_degree(dir_s, vector():set(0, 0, 1))
    local vvv = vector_cross(dir_s, vector():set(0, 0, 1))
    if vvv.y < 0 then
       angle = -angle
    end    
    dir_s = vector_rotate_y(dir, angle)

    local d = 2
    --'теперь аи ноду относительно командора ордена рыцарей храма
    local vertex = level.vertex_in_direction(level.vertex_in_direction(vertex_id, dir_s, dist_s), dir, d)
    self.npc_list[npc_id].vertex_id = vertex
    
    --' проверим, а не имеет ли смысла неписю скорректировать свою позицию
    --local distance = level.vertex_position(vertex):distance_to(self.npc_list[npc_id].soldier:position())
    -- нужно считать отставание от командира, а не от расчетной точки.
    local distance = commander:position():distance_to(self.npc_list[npc_id].soldier:position())
    if distance > dist_s + 2 then
		--' Игрок сильно отстал. Необходимо изменить тип перемещения.
		--' Тип перемещения задаем в зависимости от базового типа.
		local new_state = accel_by_curtype[self.current_state]
		if new_state ~= nil then
			return vertex, dir, new_state
		end
    end   
    
    return vertex, dir, self.current_state
end
--' Установить действие НПС
function PatrolManager:set_command(npc, command, formation)
    if npc == nil or npc:alive () == false then
       abort ("NPC commander possible dead in PatrolManager[%s]", self.path_name)
    end    
    if npc:id () ~= self.commander_id then
       return --abort ("NPC %s is not commander in PatrolManager[%s]", npc:name (), self.path_name) 
    end   
    self.current_state = command
    if self.formation ~= formation then
       self.formation = formation
       self:set_formation (formation)
    end   
    self.commander_lid = npc:level_vertex_id ()
    self.commander_dir = npc:direction ()
    self:update ()
end
--' Является ли указанный НПС командиром.
function PatrolManager:is_commander(npc_id)
    return npc_id == self.commander_id    
end
--' Находится ли командир патруля в схеме XR_MEET
function PatrolManager:is_commander_in_meet()
    if self.commander_id == -1 then return false end
    local npc = self.npc_list[self.commander_id].soldier
    if npc ~= nil and npc:alive () == true then
       return xr_meet.is_meet (npc)
    end
    return false    
end
--' Апдейт
function PatrolManager:update ()
    if tm_enabled == true then
       self.tm:update ()
    end
end


--'-------------------------------------------------------------------------------------------------------------------
--'Evaluators
--'--------------------------------------------------------------------------------------------------------------------
class "evaluator_patrol_end" (property_evaluator)
function evaluator_patrol_end:__init(name, storage) super (nil, name)
	self.st = storage
end
function evaluator_patrol_end:evaluate()
    return not xr_logic.is_active(self.object, self.st)
end

class "evaluator_patrol_comm" (property_evaluator)
function evaluator_patrol_comm:__init(name, storage) super (nil, name)
	self.st = storage
end
function evaluator_patrol_comm:evaluate()
    return patrols[self.st.patrol_key]:is_commander(self.object:id())
end

--'-------------------------------------------------------------------------------------------------------------------
--' Actions
--'--------------------------------------------------------------------------------------------------------------------
class "action_commander" (action_base)
function action_commander:__init(npc, action_name, storage) super(nil, action_name)
	self.st = storage
	self.move_mgr = db.storage[npc:id()].move_mgr
	self.was_reset = false
	self.cur_state = "patrol"
end
function action_commander:initialize()
	action_base.initialize (self)
	self.object:set_desired_position()
	self.object:set_desired_direction()
	self:activate_scheme ()
end
function action_commander:activate_scheme()
	self.st.signals = {}
	printf ("[XR_PATROL] SET PATROL PATH : %s", self.st.path_walk)
	if self.st.path_walk_info == nil then
		self.st.path_walk_info = utils.path_parse_waypoints(self.st.path_walk)
	end
	if self.st.path_look_info == nil then
		self.st.path_look_info = utils.path_parse_waypoints(self.st.path_look)
	end
	self.move_mgr:reset(self.st.path_walk, self.st.path_walk_info, self.st.path_look, self.st.path_look_info,
		self.st.team, self.st.suggested_state, {obj = self, func = self.formation_callback})

    patrols[self.st.patrol_key]:set_command(self.object, self.cur_state, self.st.formation)
end
function action_commander:execute()
	action_base.execute(self)
	self.move_mgr:update()

	local new_state = state_mgr.get_state(self.object)
	local old_state = self.old_state 
	if old_state ~= new_state then
		if new_state == "sneak" then
			xr_sound.set_sound_play(self.object:id(), "patrol_sneak")
		elseif new_state == "sneak_run" then
			xr_sound.set_sound_play(self.object:id(), "patrol_run")
		elseif new_state == "run" then
			xr_sound.set_sound_play(self.object:id(), "patrol_run")
		elseif new_state == "assault" then
			xr_sound.set_sound_play(self.object:id(), "patrol_run")
		elseif new_state == "rush" then
			xr_sound.set_sound_play(self.object:id(), "patrol_run")
		else
			if old_state == "sneak" or old_state == "sneak_run" or
			   old_state == "run" or old_state == "assault" or old_state == "rush"
			then			
				xr_sound.set_sound_play(self.object:id(), "patrol_walk")
			end
		end
	
		self.old_state = new_state
	end
	--printf("FORMATION %s", self.st.formation)
    patrols[self.st.patrol_key]:set_command(self.object, new_state, self.st.formation)
end
----------------------
function action_commander:finalize()
    if self.object:alive () == true then
       --printf ("ACTION_COMMANDER:FINALIZE CALLED")
	   patrols[self.st.patrol_key]:set_command(self.object, "guard", self.st.formation)
	   self.move_mgr:finalize()
	end   
    action_base.finalize(self)
end
----------------------
function action_commander:deactivate(npc)
	patrols[self.st.patrol_key]:remove_npc(npc)
end
----------------------
function action_commander:death_callback(npc)
	patrols[self.st.patrol_key]:remove_npc(npc)
end
----------------------
function action_commander:net_destroy(npc)
    self:deactivate(npc)
end
----------------------
function action_commander:formation_callback(mode, number, index)
    if number == 0 then
       self.st.formation = "line"
    elseif number == 1 then
       self.st.formation = "around" 
    elseif number == 2 then 
       self.st.formation = "back"  
    end
    printf("FORMATION CALLBACK. %s", self.st.formation)
end


class "action_patrol" (action_base)
function action_patrol:__init (npc, action_name, storage) super (nil, action_name)
	self.st = storage
	self.move_mgr = db.storage[npc:id()].move_mgr
	self.l_vid = -1
	self.dist = 0
	self.dir = vector():set(0, 0, 1)
	self.cur_state = "patrol"
	self.on_point = false
	self.was_reset = false
	self.time_to_update = time_global () + 1000
end
function action_patrol:initialize()
	action_base.initialize              (self)
	self.object:set_desired_position    ()
	self.object:set_desired_direction   ()
    self.on_point = false
end
function action_patrol:activate_scheme()
	self.st.signals = {}
	printf ("[XR_PATROL] SET SOLDIER PATROL PATH : %s", self.st.path_walk)
	if self.st.path_walk_info == nil then
		self.st.path_walk_info = utils.path_parse_waypoints(self.st.path_walk)
	end
	if self.st.path_look_info == nil then
		self.st.path_look_info = utils.path_parse_waypoints(self.st.path_look)
	end
	self.move_mgr:reset(self.st.path_walk, self.st.path_walk_info, self.st.path_look, self.st.path_look_info,
		self.st.team, self.st.suggested_state, {obj = self, func = self.formation_callback})	
end
function action_patrol:execute()
	action_base.execute (self)

--'	if db.actor and xr_logic.try_switch_to_another_section (self.object, self.st, db.actor) then
--'       return
--'	end	

    if self.time_to_update - time_global() > 0 then
       return 
    end
    
    self.time_to_update = time_global() + 1000
    
	self.l_vid, self.dir, self.cur_state = patrols[self.st.patrol_key]:get_npc_command(self.object)

    self.l_vid = utils.send_to_nearest_accessible_vertex(self.object, self.l_vid)	 
	local desired_direction = self.dir
	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.cur_state) 
end
function action_patrol:finalize()
    if self.object:alive () == true then
	   self.move_mgr:finalize()
	end 
	action_base.finalize (self)
end
function action_patrol:formation_callback(mode, number, index)
end
function action_patrol:death_callback(npc)
	patrols[self.st.patrol_key]:remove_npc(npc)
end
function action_patrol:deactivate(npc)
    patrols[self.st.patrol_key]:remove_npc(npc)
end
function action_patrol:net_destroy(npc)
    self:deactivate (npc)
end


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

	local manager = object:motivation_action_manager ()

	properties["event"] 		= xr_evaluators_id.reaction
	properties["patrol_end"]	= xr_evaluators_id.sidor_patrol_base + 0
	properties["patrol_comm"]	= xr_evaluators_id.sidor_patrol_base + 1

	operators["action_patrol"]	    = xr_actions_id.sidor_act_patrol
	operators["action_commander"]	= xr_actions_id.sidor_act_patrol + 1

	-- Evaluators                                                                            
    manager:add_evaluator (properties["patrol_end"], this.evaluator_patrol_end ("patrol_end", storage, "patrol_end"))
    manager:add_evaluator (properties["patrol_comm"], this.evaluator_patrol_comm ("patrol_comm", storage, "patrol_comm"))

	-- Actions
	-- create action for patrol commander
	local action = this.action_commander (object, "action_commander", 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))
	action:add_precondition(world_property(xr_evaluators_id.smartcover_action + 3, false))
	xr_motivator.addCommonPrecondition (action)
	action:add_precondition		(world_property(properties["patrol_end"], 	false))
	action:add_precondition		(world_property(properties["patrol_comm"], 	true))
	action:add_effect 		    (world_property(properties["patrol_end"], 	true))
	manager:add_action          (operators["action_commander"], action)
	xr_logic.subscribe_action_for_events (object, storage, action)
	
	action = this.action_patrol (object,"action_patrol", 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))
	action:add_precondition(world_property(xr_evaluators_id.smartcover_action + 3, false))
	xr_motivator.addCommonPrecondition(action)
	action:add_precondition		(world_property(properties["patrol_end"], 	false))
	action:add_precondition		(world_property(properties["patrol_comm"], 	false))
	action:add_effect 		    (world_property(properties["patrol_end"], 	true))
	manager:add_action          (operators["action_patrol"], action)
	xr_logic.subscribe_action_for_events (object, storage, action)

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

-- включение патруля
function set_scheme(npc, ini, scheme, section, gulag_name)
    printf ("XR_PATROL CALLED 'SET_SCHEME' FOR NPC %s", npc: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.path_name = utils.cfg_get_string(ini, section, "path_walk", npc, true,  gulag_name)
	st.path_walk = st.path_name
	st.path_look = utils.cfg_get_string(ini, section, "path_look", npc, false, gulag_name)
	st.formation = utils.cfg_get_string(ini, section, "formation", npc, false, "")
	if st.formation == nil then
	   st.formation = "back"
	end    
	st.move_type = utils.cfg_get_string(ini, section, "move_type", npc, false, "")
	if st.move_type == nil then
	   st.move_type = "patrol"
	end    

	st.suggested_state = {}
	st.suggested_state.standing = utils.cfg_get_string(ini, section, "def_state_standing", npc, false, "")
	st.suggested_state.moving  = utils.cfg_get_string(ini, section, "def_state_moving1", npc, false, "")
	st.suggested_state.moving  = utils.cfg_get_string(ini, section, "def_state_moving", npc, false, "", st.suggested_state.moving)
--'	st.animation = utils.cfg_get_string(ini, section, "animation", npc, false, "")

	st.path_walk_info = nil --'Будут инициализированы в reset(), сейчас пути могут быть еще
	st.path_look_info = nil --'не загружены.
	
	st.commander = utils.cfg_get_bool(ini, section, "commander", npc, false, false)

	st.patrol_key = st.path_name

	local squad = db.storage[npc:id()].squad_obj
	if squad ~= nil then
		st.patrol_key = st.patrol_key .. tostring(squad.squad_id)
	end

    if patrols[st.patrol_key] == nil then
       patrols[st.patrol_key] = PatrolManager(st.path_name)
	end
	patrols[st.patrol_key]:add_npc(npc, st.commander)
end