dofile('Data/Scripts/Common/HOLogic/Task.lua')

-------------------------------------------------------------------------------
class 'HiddenObjectLogic' (HiddenBaseLogic)
-------------------------------------------------------------------------------

function HiddenObjectLogic:__init(game_scene)
	HiddenBaseLogic.__init(self, game_scene)
	
	self.indicator			= HiddenObjectIndicator(self.game_scene)
	self.key_prefix			= ""
	self.k_max_active_tasks	= 12
	self.using_cheat		= false
	self.task_objects_count	= 0

	game_scene:getPlayField():getMainEntity().onClickBegin:add( bind(HiddenObjectLogic.onMouseDown, self))

	if isCheatMode() then
		game_scene:getWindow():getHotkeySignal(ERSK_SPACE):removeAll()
		game_scene:getWindow():getHotkeySignal(ERSK_SPACE):add( bind(self.findAllObjects, self) )
	end
end
-------------------------------------------------------------------------------

function HiddenObjectLogic:destroy()
	self.indicator:destroy()
end
-------------------------------------------------------------------------------


--------------------------------------------------------------------------------
-- INTERFACE METHODS -----------------------------------------------------------
--------------------------------------------------------------------------------

function HiddenObjectLogic:registerTasks(key_prefix, random_objects_names, action_tasks_objects_names, ...)
	self.key_prefix = (key_prefix) and key_prefix or self.key_prefix
	self.nodes = {}
	---------
	
	local scene_name = self.game_scene:getPlayField():getName()
	
	local dealsObjects  = {...}
	local taskTemplates = {}
	---------
	
	table.insert(taskTemplates, self:_uniteObjectsToTaskTemplates(random_objects_names))
	for i, objects in ipairs(dealsObjects) do
		table.insert(taskTemplates, self:_uniteObjectsToTaskTemplates(objects))
	end
	
	for i=2, #taskTemplates do
		self:_moveIdenticalTaskTemplates(taskTemplates[1], taskTemplates[i])
	end
	---------
	
	local tasks = {}
	
	for i, templates in ipairs(taskTemplates) do
		tasks[i] = {}
		
		for text, template in pairs(templates) do
			assert(#template > 0)
			
			local textKey = self:_getKey(template[1])
			local newTask = self:_createTask(textKey, template)
			
			self:_setActionObjects(newTask, template, action_tasks_objects_names)
			tasks[i][text] = newTask
		end
	end
	---------

	if isDebugLocalization() then
		for groupIndex, tasksGroup in ipairs(tasks) do
			for text, task in pairs(tasksGroup) do
				if IsActiveTask(scene_name, task) then
					RemoveActiveTaskState(scene_name, task)
				end
			end
		end

		local dealTasks = tasks[self.dealIndex + 1]

		if dealTasks then
			for text, task in pairs(dealTasks) do
				self:_addTask(task)
			end
		end

		if self.dealIndex == 1 then
			local randomTasks = tasks[1]
			for text, task in pairs(randomTasks) do
				self:_addTask(task)
			end
		end
	else
		local has_generated_tasks = false
		local dealTasks = tasks[self.dealIndex + 1]
		
		if dealTasks == nil or table.size(dealTasks) == 0 then
			for text, task in pairs(tasks[1]) do
				if IsActiveTask(scene_name, task) then
					has_generated_tasks = true
					break
				end
			end
		else
			for text, task in pairs(dealTasks) do
				if IsActiveTask(scene_name, task) then
					has_generated_tasks = true
				end
				
				self:_addTask(task)
			end
		end
		---------
		
		local random_tasks = {}

		for text, task in pairs(tasks[1]) do
			if (not has_generated_tasks and task:getNumberOfObjects() > 0)
				or IsActiveTask(scene_name, task)
			then
				table.insert(random_tasks, task)
			end
		end
		
		table.shuffle(random_tasks)
		local n = #random_tasks
		
		-- if not IsDebugLocalization() then
			if n > self.k_max_active_tasks - table.size(self.tasks) then
				n = self.k_max_active_tasks - table.size(self.tasks)
			end
		-- end

		for i = 1, n do
			local task = random_tasks[i]
			self:_addTask(task)
		end
	end
	
	local playfield = self.game_scene:getPlayField()
	for task_name, task in pairs(self.tasks) do
		local task_objects = task:getObjects()
		
		for _, object_name in pairs(task_objects) do
			local object = playfield:getEntityNamed(object_name)
			object.onClickBegin:add(function() 
										self:onClickObject(object_name) 
									end)
		end
	end

	self.task_objects_count = self:getTaskObjectsCount()
	if self.task_objects_count == 0 then
		self:onLastItemFound()
	end
end
-------------------------------------------------------------------------------

function HiddenObjectLogic:start()
	self.indicator:fill()
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:onClickObject(object_name)
	self.num_wrong_clicks = self.num_wrong_clicks - 1
	if not self:isPunishing() and self:isTaskObject(object_name) then
		SetTaskObjectFound(self.game_scene:getPlayField():getName(), object_name)
		self:onObjectFound(object_name)
	end
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:onObjectFound(object_name)
	local playfield = self.game_scene:getPlayField()
	local playfield_timer = playfield:getTimer()

	if playfield:hasEntityNamed(object_name) then
		local node = path_manager:getConnectedNode(NodeType.TASK, object_name)
		assert(node and 'No node for task')
		node:trigger()
		
		self.game_scene:getAchievmentsManager():handleEvent('on_object_found', {['scene_name'] = playfield:getName(), ['object_name'] = object_name})

		local found_object = self.game_scene:getPlayField():getEntityNamed(object_name)	
		found_object:setRespondable(false)
		Helpers.attachInPlace(found_object, self.indicator:getAnchor())

		GetScoreManager():addScoreHOPick(found_object)

		local task = self:getTaskByObjectName(object_name)
		local mover = self.indicator:createFlyAnimation(found_object, task)
		mover:attachTo(found_object)

		if self.indicator.createFlyParticleSystem then
			local particles, time = self.indicator:createFlyParticleSystem()
			time = time or 0
			particles:attachTo(found_object:getParent())
			particles:setOrder(found_object:getOrder()-0.1)
			particles.onUpdate:add(
				function(entity)
					particles:setPosition(found_object:getPosition())
					if found_object:getOpacity() == 0 and particles:getParticleAlive() == 0 then
						particles:destroy()
						particles = nil
					end
				end
			)
			playfield_timer:add(
				function()
					if particles then
						particles:fire()
					else
						WriteInfo("Particles destroyed before fire()")
					end
				end,  time >= 0 and time or (self.indicator:getFindAnimationDuration() + time) )
		end
		
		self.onTaskObjectFound:notify(found_object)

		if playfield.onObjectFound then
			playfield:onObjectFound(found_object)
		end

		self:playFlyingSound()

		self.task_objects_count = self.task_objects_count - 1
		if self.task_objects_count == 0 then
			playfield:getMouseBlocker():push()
		end
		
		playfield_timer:add(function() 
								self:onObjectReachPanel(object_name) 
								
								if self:isAllTaskObjectsFound() then
									playfield_timer:add(function()
															self:onLastItemFound()
														end, self.indicator:getStrikeOutDuration())
								end
							end, self.indicator:getFindAnimationDuration())
	end
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:onObjectReachPanel(object_name)
	HiddenBaseLogic.onObjectReachPanel(self, object_name)
	
	local task = self:getTaskByObjectName(object_name)
	task:onObjectFound(object_name)
	
	self.indicator:updateCaption(task, false)
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:getTask(task_name)
	return self.tasks[task_name]
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:getTaskByObjectName(object_name)
	local the_task = nil
	for task_name, task in pairs(self.tasks) do
		if task:hasObject(object_name) then
			the_task = task
			break
		end
	end
	
	assert(the_task)
	return the_task
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:addTask(task)
	local task_name = task:getName()
	if not self.tasks[task:getName()] then
		self.tasks[task:getName()] = task
	end
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:hasTask(task_name)
	for name, task in pairs(self.tasks) do
		if name == task_name then
			return true
		end
	end
	
	return false
end
--------------------------------------------------------------------------------

function HiddenObjectLogic:isTaskObject(object_name)
	for task_name, task in pairs(self.tasks) do
		if task:hasObject(object_name) then
			return true
		end
	end
	
	return false
end
-------------------------------------------------------------------------------

function HiddenObjectLogic:findAllObjects()
	self.using_cheat = true
	local playfield = self.game_scene:getPlayField()
	for task_name, task in pairs(self.tasks) do
		local task_objects = task:getObjects()
		for _, object_name in pairs(task_objects) do
			self:onClickObject(object_name) 
		end
	end
end
-------------------------------------------------------------------------------

function HiddenObjectLogic:getIndicator()
	return self.indicator
end
-------------------------------------------------------------------------------

function HiddenObjectLogic:getNotFoundTaskObjects()
	local sceneName = self.game_scene:getPlayField():getName()
	local result = {}

	for _, task in pairs(self.tasks) do
		local objects = task:getObjects()

		for _, objectName in pairs(objects) do

			if not IsTaskObjectFound(sceneName, objectName) then
				table.insert(result, objectName)
			end
		end
	end
	
	return result
end
-------------------------------------------------------------------------------


--------------------------------------------------------------------------------
-- PRIVATE METHODS -------------------------------------------------------------
--------------------------------------------------------------------------------

function HiddenObjectLogic:_getKey(object_name)
	return self.key_prefix..string.upper(object_name)
end

--------------------------------------------------------------------------------
function GetString(alias)
	return StringManager:getString(alias)
end

--------------------------------------------------------------------------------
function HiddenObjectLogic:_uniteObjectsToTaskTemplates(objects)
	local result      = {}
	local objectsType = type(objects)
	
	if objectsType == 'table' then
		for _, objectName in ipairs(objects) do
			local key    = self:_getKey(objectName)
			local value  = GetString(key)
			local bAdded = false
			
			for existValue, objs in pairs(result) do
				if value == existValue then
					table.insert(objs, objectName)
					bAdded = true
					break
				end
			end
			
			if not bAdded then
				result[value] = {}
				table.insert(result[value], objectName)
			end
		end
		
	elseif objectsType == 'string' then
		local key    = self:_getKey(objects)
		local value  = GetString(key)
		
		result[value] = {}
		table.insert(result[value], objects)
	end
	
	return result
end

--------------------------------------------------------------------------------
function HiddenObjectLogic:_moveIdenticalTaskTemplates(source, target)
	for tValue, tGroup in pairs(target) do
		for sValue, sGroup in pairs(source) do
			if tValue == sValue then
				for i, objectName in ipairs(sGroup) do
					table.insert(tGroup, objectName)
				end
				
				source[sValue] = nil
				break
			end
		end
	end
end

--------------------------------------------------------------------------------
function HiddenObjectLogic:_isTemplateContainObject(template, objectName)
	for _, tmplObjectName in ipairs(template) do
		if objectName == tmplObjectName then
			return true
		end
	end
	
	return false
end

--------------------------------------------------------------------------------
function HiddenObjectLogic:_setActionObjects(task, template, actionTasksObjects) 
	if actionTasksObjects == nil then
		return false
	end
	
	for _, objectName in ipairs(template) do
		if task:hasObject(objectName) and
		   self:_isTemplateContainObject(actionTasksObjects, objectName)
		then
			node = path_manager:addTaskNode(objectName)
			node:setState(NodeState.PENDING)
			self.nodes[task] = node
			task:setActionObject(objectName, true)
		end
	end
end

--------------------------------------------------------------------------------
function HiddenObjectLogic:_createTask(textKey, objects)
	local playfield = self.game_scene:getPlayField()
	local task  = Task(textKey)
	
	for i, objectName in ipairs(objects) do
		if playfield:hasEntityNamed(objectName) then
			if IsTaskObjectFound(playfield:getName(), objectName) then
				local obj = playfield:getEntityNamed(objectName)
				obj:hide()
			else
				node = path_manager:addTaskNode(objectName)
				node:setState(NodeState.PENDING)
				self.nodes[objectName] = node
				task:addObject(objectName)
			end
			
			table.insert(self.createdTaskObjects, objectName)
		end
	end

	return task
end

--------------------------------------------------------------------------------
function HiddenObjectLogic:_addTask(task)
	for _, name in ipairs(task:getObjects()) do
		self.nodes[name]:setState(NodeState.ENABLED)
	end
	self:addTask(task)
	SetActiveTaskState(self.game_scene:getPlayField():getName(), task)
end

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