-------------------------------------------------------------------------------
class 'HiddenPictureLogic' (HiddenBaseLogic)
-------------------------------------------------------------------------------

TASK_OBJECT_DROPPED = 'TaskObjectDropped'

function HiddenPictureLogic:__init(game_scene)
	HiddenBaseLogic.__init(self, game_scene)
	
	self.indicator = HiddenPictureIndicator(self.game_scene)
	self.tasksInfo = {}
	
	self.ETaskState = {TS_NONE      = -1,
					   TS_RESERVED  = 0,
					   TS_SHOWED    = 1,
					   TS_DRAGGABLE = 2,
					   TS_HIDED     = 3}
	
	self.reservedTasks      = {}
	-- self.createdTaskObjects = {}
	self.itemTargets        = {}
	self.tasks				= {}
	self.task_objects_count = 0
end

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

-------------------------------------------------------------------------------
function HiddenPictureLogic:update(dt)
	HiddenBaseLogic.update(self, dt)
	self.indicator:update(dt)
end

--------------------------------------------------------------------------------
-- INTERFACE METHODS -----------------------------------------------------------
--------------------------------------------------------------------------------
function HiddenPictureLogic:registerTask(	dealId, 
											objectName, 
											getObjectName, 
											targetObjectName, 
											onDropTarget, 
											onFind, 
											bCustomObjectPos, 
											targetTextAlias)
	local playfield = self.game_scene:getPlayField()
	local task = self:_createTask(objectName)
	self:_createSubstrate(objectName)
	
	local info            = {}
	info.taskName         = task:getName()
	info.dealId           = dealId
	info.objectName       = objectName
	info.getObjectName    = getObjectName
	info.targetObjectName = targetObjectName
	info.dropTarget       = targetObjectName and playfield:getEntityNamed(targetObjectName) or nil
	info.onDropTarget     = onDropTarget
	info.onFind           = onFind
	info.targetTextAlias  = targetTextAlias or self:_getTargetAlias(objectName)
	info.state            = self.ETaskState.TS_NONE
	info.inv_scale 		  = 1
	
	table.insert(self.tasksInfo, info)
	
	info.node = path_manager:addTaskNode(getObjectName)
	if info.node then
		info.node:setState(NodeState.PENDING)
	end
	
	if targetObjectName and dealId == self.dealIndex then
		if type(targetObjectName) == 'table' then
			for area in each(targetObjectName) do
				playfield:registerBBT(area, info.targetTextAlias, EBBTAliasOrder.RANDOM, nil, true)
			end
		else
			playfield:registerBBT(targetObjectName, info.targetTextAlias, EBBTAliasOrder.RANDOM, nil, true)
		end
	end
	
	if IsTaskObjectFound(playfield:getName(), objectName) then
		self:_hideGetObject(info.getObjectName, 0)

		local bShowTask = info.targetObjectName
							and not IsDragObjectUsed( info.objectName, playfield:getName() )
							and (self.dealIndex == dealId)

		if bShowTask then
			self:_showTask(task)
			self:_activateTask(task)
		   
		   -- подгонка слоя и позици объекта для реализации сохранки
			do 
				local object = playfield:getEntityNamed(objectName)
				local anchor = self.indicator:getAnchor()
				local pos    = self.indicator:getTaskScrPosition(task)
				local scale  = self.indicator:getTaskScale(task)
				
				object:attachTo(anchor)
				object:setScrXY(pos.x, pos.y)
				object:setScale(scale)
			end
		end
		
		if info.onFind then
			local event = {}
			event.save = true
			event.info = info
			
			info.onFind(event)
		end
		
		self:_registerDrag(task)
	else
		-- объект ставится в позицию предмета для нахождения
		if not bCustomObjectPos then
			local target_object = playfield:getEntityNamed(info.objectName)
			local get_object = playfield:getEntityNamed(info.getObjectName)
			target_object:setScrPosition(get_object:getScrPosition())
		end
	
		if self.dealIndex == dealId then
			self:addTask(task)
			if self.indicator:FreeSlotsExist() then
				self:_showTask(task)
			else
				self:_reserveTask(task)
			end
		else
			playfield:getEntityNamed(objectName):setRespondable(false)
			playfield:getEntityNamed(getObjectName):setRespondable(false)
		end
	end
	
	local playfield = self.game_scene:getPlayField()
	-- local task_objects = task:getObjects()
	-- for _, object_name in pairs(task_objects) do
	-- playfield:getTimer():add(function()
		self.game_scene:getEntityNamed(getObjectName).onClickBegin:add(	function() 
																			self:onClickObject(objectName, getObjectName) 
																		end)
	-- end, 2)
	-- end

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

--------------------------------------------------------------------------------
function HiddenPictureLogic:start()
	self.task_objects_count = self:getTaskObjectsCount()
	if self.task_objects_count <= 0 then
		self:onLastItemFound()
	end
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:onClickObject(object_name, getObjectName)
	local playfield = self.game_scene:getPlayField()
	-- local info = self:getTaskInfo(object_name)
	local info = self:getTaskInfo(getObjectName, 'getObjectName')
	
	self.num_wrong_clicks = self.num_wrong_clicks - 1
	if info and info.state == self.ETaskState.TS_SHOWED and not self:isPunishing() then
		local object = self.game_scene:getEntityNamed(info.objectName)
		
		SetTaskObjectFound(playfield:getName(), info.objectName)
		self:onObjectFound(object_name)
		
		local node = path_manager:getConnectedNode(NodeType.TASK, getObjectName)
		assert(node and 'No node for task')
		node:trigger()
		
		if info.onFind then
			local event = {}
			event.save = false
			event.info = info
			
			info.onFind(event)
		end
	end
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:onObjectFound(object_name)
	local playfield = self.game_scene:getPlayField()
	local playfield_timer = self.game_scene:getPlayField():getTimer()
	local info = self:getTaskInfo(object_name, 'objectName')
	local get_object = self.game_scene:getEntityNamed(info.getObjectName)  
	get_object:hide()  

	local found_object = self.game_scene:getEntityNamed(object_name)
	found_object:show()
	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.task_objects_count = self.task_objects_count - 1
	if self.task_objects_count <= 0 then
		playfield:getMouseBlocker():push()
	end
	
	SetTaskObjectFound(playfield:getName(), object_name)
	local task_info = self:getTaskInfo(task:getName())
	if not task_info.targetObjectName or self.task_objects_count <= 0 then
		self:_hideTask(task, self.indicator:getFindAnimationDuration())
	end	
		
	playfield_timer:add(function() 
							self:onObjectReachPanel(object_name) 

							if self:isAllTaskObjectsFound() then
								playfield_timer:add(function()
														if playfield:getMouseBlocker():isHold() then
															playfield:getMouseBlocker():pop()
														end
														self:onLastItemFound()
													end, 0.4)
							end
						end, self.indicator:getFindAnimationDuration())

	self:playFlyingSound()
end

-------------------------------------------------------------------------------
function HiddenPictureLogic:isAllTaskObjectsFound()
	if #self.reservedTasks == 0 then
		return HiddenBaseLogic.isAllTaskObjectsFound(self)
	end
	
	return false
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:onLastItemFound()
	HiddenBaseLogic.onLastItemFound(self)
	
	self.indicator:ClearSlotStates()
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:onObjectReachPanel(object_name)
	HiddenBaseLogic.onObjectReachPanel(self, object_name)
	
	local task = self:getTaskByObjectName(object_name)
	task:onObjectFound(object_name)
	
	local task_info = self:getTaskInfo(task:getName())
	if task_info.targetObjectName or self.task_objects_count <= 0 then
		self:_activateTask(task)
		self:_registerDrag(task)
	else
		self:_showNextTask()
	end
end

-------------------------------------------------------------------------------
function HiddenPictureLogic:getTaskInfo(searchingValue, key)
	key = key or 'taskName'
	
	for i, info in ipairs(self.tasksInfo) do
		if info[key] == searchingValue then
			return info
		end
	end
	
	return nil
end

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

-- move method up ---------------------------------------------------------------
function HiddenPictureLogic: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 HiddenPictureLogic:isTaskObject(object_name)
	for task_name, task in pairs(self.tasks) do
		local get_object_name = self:getTaskInfo(object_name, 'getObjectName')
		if task:hasObject(object_name) or get_object_name then
			return true
		end
	end
	
	return false
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:hasTaskObjectDropped(object_name)
	local level_name = self.game_scene:getPlayField():getName()

	if HasGameState(TASK_OBJECT_DROPPED, level_name .. '_' .. object_name) then
		return true
	end

	return false
end


--------------------------------------------------------------------------------
-- PRIVATE METHODS -------------------------------------------------------------
--------------------------------------------------------------------------------
function HiddenPictureLogic:_getTaskName(objectName)
	local level_name  = self.game_scene:getPlayField():getName()
	local prefix = 'HO_'..string.sub(level_name, 1, 2)..'_'
	
    if IsLevelCollectorsEdition(current_scene:getPlayField().name) then
        prefix = prefix .. 'CE_'
	end    
	
	return prefix..string.upper(objectName)
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_getTargetAlias(objectName)
	local level  = self.game_scene:getPlayField():getName()
	local prefix = 'BBT_'..string.sub(level, 1, 2)..'_'
	
	return prefix..string.upper(objectName)
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_getStartDragAreaName(objectName)
	return 'back_'..objectName
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_createTask(objectName)
	local playfield = self.game_scene:getPlayField()
	local textName = self:_getTaskName(objectName)
	local task     = Task(textName)
	
	if not IsTaskObjectFound(playfield:getName(), objectName) then
		task:addObject(objectName)
	end
	
	table.insert(self.createdTaskObjects, objectName)
	
	return task
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_createSubstrate(objectName)
	local back = CreateSpriteEntityInstant('Data/Textures/Common/Area.png')
	back:setName(self:_getStartDragAreaName(objectName))
	back:setColor(0.0, 0.0, 0.0, 0.01)
	back:setRespondable(false)
	back:attachTo(self.game_scene:getPlayField():getMainEntity())
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_showTask(task, delay)
	local playfield = self.game_scene:getPlayField()
	SetActiveTaskState(playfield:getName(), task)
	
	local taskName = task:getName()
	local info     = self:getTaskInfo(taskName)
	
	info.state = self.ETaskState.TS_SHOWED
	if info.node then
		info.node:setState(NodeState.ENABLED)
	end
	
	self.indicator:CreateIndicator(taskName, playfield:getEntityNamed(info.objectName))
	self.indicator:setIndicatorColor(taskName, self.indicator.COLOR_SHADOW)
	self.indicator:ShowIndicator(taskName, delay)
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_showNextTask(delay)
	if #self.reservedTasks > 0 then
		local task = table.remove(self.reservedTasks, 1)
		
		self:_showTask(task, delay)
	end
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_activateTask(task)
	local taskName = task:getName()
	local info     = self:getTaskInfo(taskName)
	
	self.indicator:setIndicatorColor(taskName, self.indicator.COLOR_DEFAULT)
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_hideTask(task, delay)
	local taskName = task:getName()
	local info     = self:getTaskInfo(taskName)
	
	info.state = self.ETaskState.TS_HIDED
	self.indicator:HideIndicator(taskName, delay)
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_reserveTask(task)
	local taskName = task:getName()
	local info     = self:getTaskInfo(taskName)
	
	info.state = self.ETaskState.TS_RESERVED
	
	table.insert(self.reservedTasks, task)
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_registerDrag(task)
	local playfield = self.game_scene:getPlayField()
	local taskName = task:getName()
	local info     = self:getTaskInfo(taskName)
	
	if info.targetObjectName then
		local function on_get(e)
			self.game_scene:getEntityNamed(info.objectName):setScale(1, 1)
			self.game_scene:getEntityNamed(info.objectName):setOpacity(1)
			self.game_scene:getEntityNamed(info.objectName):show()
			self.indicator:setIndicatorColor(taskName, self.indicator.COLOR_DRAGGING)
			self.indicator:SetIndicatorIgnoreTestPoint(taskName, true)
			for k, v in pairs(self.createdTaskObjects) do
				self.game_scene:getEntityNamed(self:_getStartDragAreaName(v)):setRespondable(false)
			end

			if info.on_get then
				info:on_get()
			end
		end
		
		local function on_drop(e)
			Helpers.ChangeScale(self.game_scene:getEntityNamed(info.objectName), 1, self.indicator:GetIconScale(taskName).x, e.time)
			for k, v in pairs(self.createdTaskObjects) do
				self.game_scene:getEntityNamed(self:_getStartDragAreaName(v)):setRespondable(true)
			end			
			playfield:getTimer():add(function()
											-- self.game_scene:getEntityNamed(info.objectName):setOpacity(0)
											-- self.game_scene:getEntityNamed(info.objectName):setRespondable(false)
											self.game_scene:getEntityNamed(info.objectName):hide()
											self.indicator:setIndicatorColor(taskName, self.indicator.COLOR_DEFAULT)
											self.indicator:SetIndicatorIgnoreTestPoint(taskName, false)											
										end, e.time)
		end
		
		local function on_drop_target(e)
			if not e.save then
				AddUniqueGameState(TASK_OBJECT_DROPPED, playfield:getName() .. '_' .. info.objectName)
				
				if self:_isAllTargetsUsed(info) then
					-- self.game_scene:getEntityNamed(info.objectName):setOpacity(0)
					-- self.game_scene:getEntityNamed(info.objectName):setRespondable(false)
					self.game_scene:getEntityNamed(info.objectName):hide()
					self.indicator:setIndicatorColor(taskName, self.indicator.COLOR_DEFAULT)
					
					self:_hideTask(task)
					self:_showNextTask()
				else
					e.time = FOUND_TOOL_DISAPPEARING_TIME
					
					-- Uncomment if item should be dropped when used on one of a multiple targets
					--DropDragingObject(info.objectName, e.time)
					--on_drop(e)
				end
			end
			
			if info.onDropTarget then
				local event  = {}
				event.save   = e.save
				event.info   = info
				event.target = e.target
				event.taskName = taskName
				event.targetName = e.targetName
			
				info.onDropTarget(event)
			end
			
			for k, v in pairs(self.createdTaskObjects) do
				local start_drag_area = self:_getStartDragAreaName(v)
				if self.game_scene:hasEntityNamed(start_drag_area) then
					self.game_scene:getEntityNamed(start_drag_area):setRespondable(true)
				end
			end
		end
		
		info.state = self.ETaskState.TS_DRAGGABLE
		playfield:registerDragObject(info.objectName, self:_getStartDragAreaName(info.objectName), info.dropTarget, on_get, on_drop, on_drop_target, nil, true)
		playfield:getHint():setAdditionalHintObject( info.targetObjectName )
	end
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_isAllTargetsUsed(info)
	local function predicate(dragObject)
		return dragObject:getDraggedEntity():getName() == info.objectName
	end

	local dragManager = self.game_scene:getPlayField():getDragManager()
	local dragObject  = dragManager:find( predicate )

	assert(dragObject)
	return dragObject:isUsed()
end

--------------------------------------------------------------------------------
function HiddenPictureLogic:_hideGetObject(objectName, time)
	local playfield = self.game_scene:getPlayField()
	if objectName ~= nil then
		time = time or 0
		
		local object = playfield:getEntityNamed(objectName)
		object:setRespondable(false)
		
		if time == 0 then
			object:setOpacity(0)
		else
			object:ChangeOpacity(object:getOpacity(), 0, time)
		end
	end
end
--------------------------------------------------------------------------------