--'******************************************************
--'*	Объект группировки.
--'******************************************************
local TASK_DISTANCE = 300*300  -- Квадрат радиуса, в котором выдаются таски игроку

class "faction_brain_human"
function faction_brain_human:__init(faction)
	self.faction = faction

	self.human = true
	self.use_issue_order = true
	self.use_cover_attack = true
	self.use_prepare_phase = true

	self.use_delay_attack = false
	
	if self.faction.player_name == "killer" then
		self.use_counter_attack = false
	else
		self.use_counter_attack = true
	end
end
-- Рассчет текущей экспансии
function faction_brain_human:calculate_current_expansion()
	local faction = self.faction
	local relative_power = faction:get_relative_power()
	--callstack()
	--printf("faction [%s] brain update %s", faction.player_name, relative_power)
	for k, expansion in pairs(faction.expansion_level) do
		--printf("check %s, current %s", k, faction.current_expansion_level)
		if faction.current_expansion_level > k then
			-- Если проверяемый уровень экспансии ниже, чем текущий
			local power_valid = relative_power > expansion.precondition_power
			local advance_valid = self:check_sim_targets_advance(expansion)
			local cond_valid = xr_logic.pick_section_from_condlist(db.actor, self, expansion.precondition_cond) == "true"			
			local actor_valid = expansion.precondition_actor_in_faction ~= true or faction.community_player == true
			
			-- Проверяем все ли условия выполняются
			if not (cond_valid and power_valid and advance_valid and actor_valid) then
				-- Если не выполняются - выдаем сообщение о потери уровня, ресетим квесты, прекращаем работу цикла.
				--printf("LEVEL DOWN %s %s %s %s", self.faction.player_name, tostring(cond_valid), tostring(power_valid), tostring(advance_valid))
				if faction.community_player == true then
					if db.actor ~= nil and faction.expansion_level[faction.current_expansion_level].level_down_snd ~= nil then
						xr_sound.set_sound_play(faction.id, faction.expansion_level[faction.current_expansion_level].level_down_snd, faction.player_name)
					end
				end
				faction.current_expansion_level = k
				break
			end

		elseif faction.current_expansion_level == k then
			-- Если проверяемый уровень равен текущему.
			-- Проверяем, все ли условия выполняются.
			local power_valid = relative_power > expansion.precondition_power
			local advance_valid = self:check_sim_targets_advance(expansion)
			local cond_valid = xr_logic.pick_section_from_condlist(db.actor, self, expansion.precondition_cond) == "true"
			local actor_valid = expansion.precondition_actor_in_faction ~= true or faction.community_player == true
			
			if not (cond_valid and power_valid and advance_valid and actor_valid) then
				-- Если не выполняются - прекращаем работу цикла.
				break
			end
		else
			-- Если проверяемый уровень выше текущего.
			-- Выдаем сообщение о поднятии уровня, ресетим квесты, прекращаем работу цикла.
			--printf("LEVEL UP %s from %s", faction.player_name, faction.current_expansion_level)				
			if faction.community_player == true then				
				local old_exp = faction.expansion_level[faction.current_expansion_level]
				if db.actor ~= nil and old_exp.level_up_snd ~= nil then
					xr_sound.set_sound_play(faction.id, old_exp.level_up_snd, faction.player_name)
				end
			end
			
			faction.current_expansion_level = k
			break
		end
	end
	self:calculate_expansion_targets()
end

-- Формирование целей на основе текущей экспансии
function faction_brain_human:calculate_expansion_targets()
	local faction = self.faction
	local relative_power = faction:get_relative_power()
	faction.target_smart_value = {}

	--printf("faction %s calculate_expansion_targets for %s", faction.player_name, faction.current_expansion_level)

	if faction.current_expansion_level == 0 then
		return
	end

	-- Сперва определяем нужно ли нам больше силы
	self:get_sim_targets_power(relative_power < faction.expansion_level[faction.current_expansion_level].precondition_power)
	
	-- Потом определяем ключевые точки.
	for i = 1,faction.current_expansion_level do
		--printf("%s", i)
		self:get_sim_targets_advance(faction.expansion_level[i])
	end

	-- Добавляем просто важные точки
	for k,v in pairs(self.faction.secondary_targets) do
		local smart_obj = self.faction.board.smarts_by_names[k]
		self:register_target_point(smart_obj, false, false, true)
	end

	
	if db.actor ~= nil and self.faction.community_player == true then
		self:reset_actor_tasks()
	end

	--' Считаем максимальный потолок отрядов.
	faction:calculate_total_squads() 
	-- Раздаем цели
	faction:calculate_squad_tasks()	
end

--' Составляет список нужных для захвата смартов, в который включает ресурсные и территориальные смарты.
function faction_brain_human:get_sim_targets_power(need_power_targets)
	--printf("faction [%s] check_sim_targets_power [%s]", self.faction.player_name, tostring(need_power_targets))
	--' Итерируемся по всем смартам, выискиваем попадающие под наше определение
	--' и устанавливаем им стартовую оценку.
	local faction = self.faction
	for k,v in pairs(faction.board.smarts) do
		if v.smrt.sim_type == "territory" or v.smrt.sim_type == "resource" then
			-- Если не need_power_targets, то добавляем только те точки, которые уже под нашим контролем.
			if need_power_targets or v.smrt.player_name == faction.player_name then
				if sim_board.is_point_avail(v.smrt, faction.player_name) then
					self:register_target_point(v.smrt, false)
				end
			end
		end
	end
end
-- Проверяем, все ли у нас захвачено из важных точек. Автоматом добавляет их в список задач (НО НЕ КВЕСТОВ)
function faction_brain_human:check_sim_targets_advance(expansion)
	for k,v in pairs(expansion.precondition_target) do
		local smart_obj = self.faction.board.smarts_by_names[v]
		if smart_obj.player_name ~= self.faction.player_name then
			return false
		end
	end

	for k,v in pairs(expansion.precondition_target_optional) do
		local smart_obj = self.faction.board.smarts_by_names[v]
		if smart_obj.player_name ~= self.faction.player_name then
			return false
		end
	end
	
	for k,v in pairs(expansion.precondition_target_eliminate) do		
		local smart_obj = self.faction.board.smarts_by_names[v]
		local smart_owner = smart_obj.player_name

		--printf("   check target advance %s owner %s", smart_obj:name(), tostring(smart_owner))
		
		if smart_owner ~= "none" then
			if game_relations.is_factions_enemies(self.faction.player_name, smart_owner) then
				return false
			end
		end
	end	
	return true
end

-- Проверяем, все ли у нас захвачено из важных точек. Автоматом добавляет их в список задач (НО НЕ КВЕСТОВ)
function faction_brain_human:get_sim_targets_advance(expansion)
	for k,v in pairs(expansion.precondition_target) do
		local smart_obj = self.faction.board.smarts_by_names[v]
		self:register_target_point(smart_obj, true)
	end

	for k,v in pairs(expansion.precondition_target_optional) do
		local smart_obj = self.faction.board.smarts_by_names[v]
		self:register_target_point(smart_obj, true)
	end
	
	for k,v in pairs(expansion.precondition_target_eliminate) do
		local smart_obj = self.faction.board.smarts_by_names[v]		
		local smart_owner = smart_obj.player_name
		
		if smart_owner ~= "none" then
			if game_relations.is_factions_enemies(self.faction.player_name, smart_owner) then
				self:register_target_point(smart_obj, false, true)
			end
		end
		
	end
end

-- Регистрит таргет смарт. При этом может изменить его капасити
function faction_brain_human:register_target_point(smart_obj, advance_point, eliminate, no_task)
	local point_capacity = smart_obj.squad_capacity
	--printf("faction [%s] register_target_point [%s]", self.faction.player_name, smart_obj:name())
	--printf("  name %s == %s", smart_obj.player_name, self.faction.player_name)
	if smart_obj.player_name == self.faction.player_name or smart_obj.player_name == "none" then
		-- Если точка уже захвачена или пустая
		-- Проверяем есть ли в соседних точках враги
		local enemy_exist = false
		for k,v in pairs(self.faction.board:get_nearest_smarts(smart_obj)) do
            if game_relations.is_factions_enemies(v.player_name, self.faction.player_name) then
				enemy_exist = true
				break
			end
		end
		--printf("  enemy exist %s", tostring(enemy_exist))
--		if not enemy_exist then
			-- Если точка принадлежит группировке и вокруг нет врагов - уменьшаем капасити до 1.
			-- Однако если точка мегаважная (есть флажок), то всегда стараемся заполнить до максимума.
			--printf("  important_point %s", tostring(smart_obj.important_point))
			if not smart_obj.important_point then
				point_capacity = 1
			else
				if smart_obj.respawn_sector ~= nil then
					point_capacity = point_capacity - 1
				end			
			end
--		else
			-- Если точка - место появления отряда, то оставляем окошко в 1, чтобы мог появиться новый отряд.
--			if smart_obj.respawn_sector ~= nil then
--				point_capacity = point_capacity - 1
--			end
--		end
	end
	self.faction.target_smart_value[smart_obj.id] = {smart = smart_obj, cap = point_capacity, num = 0, smart_name = smart_obj:name(),
													 smart_id = smart_obj.id, advance_point = advance_point, eliminate = eliminate, no_task = no_task}
	--printf("   cap %s", point_capacity)
end

-- Выдаем игроку квесты на основе выбранных целей для группировки
function faction_brain_human:reset_actor_tasks()
	--printf("faction [%s] reset_actor_tasks", self.faction.player_name)
	--callstack()
	--print_table(self.faction.target_smart_value)

	local tm = task_manager.get_task_manager()
	local faction = self.faction

	self.interfirience_points = {}
	-- Теперь определяем точки, которые мешают пройти нашим отрядам.
	for k,squad in pairs(faction.squads) do
		-- бежим по отрядам
		-- Выбираем которые stay_point
		local current_action = squad.current_action
		if current_action ~= nil then 
			if current_action.name == "stay_point" then
				--printf("CHECK interfirience squad %s next_point_id %s", squad.squad_id, tostring(current_action.next_point_id))
				if current_action.next_point_id ~= nil then				
					-- Берем у них next_point_id
					local point = faction.board.smarts[current_action.next_point_id].smrt

					if faction.target_smart_value[current_action.next_point_id] ~= nil then
						--printf("found allies [%s] for [%s]", squad.squad_id, point:name())
						faction.target_smart_value[current_action.next_point_id].allies_exist = true
					else
						-- Из next_point убираем точки, которые под атакой.
						--printf("CHECK interfirience %s under_attack %s", point:name(), tostring(point.combat_manager:point_under_attack()))
						if not point.combat_manager:point_under_attack() and
						   game_relations.is_factions_enemies(squad.player_id, point.player_name)
						then
							-- Добавляем в список помех.
							--printf("ADD interfirience point %s", point:name())						
							self.interfirience_points[point.id] = point							
						end
					end
				end
			else
				-- На случай атаки более сильного противника.
				local point = faction.board.smarts[current_action.dest_smrt_id].smrt

				if faction.target_smart_value[current_action.dest_smrt_id] ~= nil then
					--printf("found allies [%s] for [%s]", squad.squad_id, point:name())
					faction.target_smart_value[current_action.dest_smrt_id].allies_exist = true
				else
					--printf("CHECK attack interfirience %s", point:name())
					if not point.combat_manager:point_under_attack() and
					   game_relations.is_factions_enemies(squad.player_id, point.player_name)
					then
						-- Добавляем в список помех.
						--printf("ADD attack interfirience point %s", point:name())
						self.interfirience_points[point.id] = point
					end
				end				
			end
		end
	end


	-- Проходим по всем квестам
	for k,v in pairs(faction.random_tasks) do
		if faction.target_smart_value[k] == nil and self.interfirience_points[k] == nil then
			-- Фейлим неактуальные квесты, то есть те, которых нет в target_smart_value
			local task = tm:get_task_by_entity_id(v)
			printf("Reverting task %s", task.id)
			task.last_check_task = "reversed"
		end
	end

	-- Итерируемся по текущим целям и инитим квесты на них
	for k,v in pairs(faction.target_smart_value) do
		-- Инитим только в том случае, если нет уже проиниченного таска
		self:init_player_task(v)
	end

	-- выдаем квест "расчистим дорогу"
	for k,v in pairs(self.interfirience_points) do
		if faction.random_tasks[k] == nil and 
		   utils.graph_distance(db.actor:game_vertex_id(), v.m_game_vertex_id) <= 300
		then
			-- Инитим только в том случае, если нет других квестов на эту точку.
			local exist = false
			local ktask = task_manager.get_task_manager():get_tasks_by_smart(k)
			if ktask ~= nil then
				if ktask.target == k then
					exist = true					
				end
			end	
			
			if not exist then
		    	local task = tm:select_task("eliminate_smart", v, faction.player_name)
		    	task.faction = faction
		    	self.faction.random_tasks[v.id] = task.entity_id
		    	if task.status ~= "selected" then
		        	task:give_task(faction.player_name)
		    	end
		    end
	    end
	end
end

--' Выдача таска игроку на точку для захвата
function faction_brain_human:init_player_task(target_data)
	--printf("IPT [%s] smart %s", self.faction.player_name, target_data.smart_name)

	--if target_data.no_task == true then
--		return
	--end

	-- Важная ли точка.
	local advance_point 	= target_data.advance_point
	local eliminate_point	= target_data.eliminate
	local allies_exist 		= target_data.allies_exist == true
	local smart 			= target_data.smart
	if smart.player_name == self.faction.player_name then
		--printf("  my smart")
		return
	end

	if sim_board.faction_cluster[self.faction.player_name] ~= sim_board.sim_level_cluster[smart.smart_level] then
		--printf("  cluster")
		return
	end

	local obj_gv = game_graph():vertex(db.actor:game_vertex_id())
	local job_gv = game_graph():vertex(smart.m_game_vertex_id )
	
	local empty_point 		= smart.player_name == "none" 
	local enemy_point		= game_relations.is_factions_enemies(self.faction.player_name, smart.player_name)
	
	local actor_in_radius 	= db.actor:position():distance_to_sqr(smart.position) <= TASK_DISTANCE
	local actor_in_level 	= obj_gv:level_id() == job_gv:level_id()

	-- Определяем какой тип таска,  мы должны щас выдать	
	local task_type = nil
	if eliminate_point == true then
		if enemy_point then
			task_type = "eliminate_smart"
		end
	elseif advance_point then
		if actor_in_level then
			task_type = "capture_smart"
		end
	elseif not empty_point then
		if enemy_point then
			if allies_exist then
				task_type = "capture_smart"
			else
				if actor_in_level and actor_in_radius then
					task_type = "eliminate_smart"
				end
			end
		end
	end
	if task_type == nil then
		--printf("  no type")
		return
	end

	local tm 				= task_manager.get_task_manager()
	local faction 			= self.faction
	
	-- Сравниваем с текущим. Если они разные, то дизаблим текущий таск и выдаем новый.
	local current_faction_task = faction.random_tasks[smart.id]
	if current_faction_task ~= nil then
		local current_task = tm:get_task_by_entity_id(current_faction_task)
		if current_task.type == task_type then
			return
		end
		task.last_check_task = "reversed"
		return
	end

    local task = tm:select_task(task_type, smart, self.faction.player_name)
    task.faction = self.faction
    self.faction.random_tasks[smart.id] = task.entity_id
    if task.status ~= "selected" then
        task:give_task(self.faction.player_name)
    end
end