dofile('Data/Scripts/Common/Hint/HintLogicBase.lua')
dofile('Data/Scripts/Common/Hint/WayManagerDummy.lua')
dofile('Data/Scripts/Common/Hint/WayPointDummy.lua')

class 'HintBase' (HintLogicBase)

-------------------------------------------------------------------------------
function HintBase:__init(playfield)
	HintLogicBase.__init(self, playfield)
	
	self.additional_hint_objects = {}
	self.forced_hint_objects = {}
	
	self.search_functions =
	{
		bind(HintBase.isForcedObject, self),
		bind(HintBase.isForcedHintObject, self),
		bind(HintBase.isTaskObject, self),
		bind(HintBase.isTaskHPTargetObject, self),
		{
			bind(HintBase.isFoundTool, self),
			bind(HintBase.isAppliedTool, self),
		},
		{
			bind(HintBase.isAdditionalHintObject, self),
			bind(HintBase.isActiveObject, self),
			-- bind(HintBase.isDragObject, self),
			function(obj) return self:isDragObject(obj) end
		},
		{
			bind(HintBase.isActiveZoomZone, self),
			bind(HintBase.isZoomZoneFirstVisit, self),
			bind(HintBase.isMinigameFirstVisit, self),
			bind(HintBase.isActiveMinigame, self),
			bind(HintBase.isActiveLinkScene, self),
		},
		{
    		bind(HintBase.isLinkHiddenObject, self),
	    	bind(HintBase.isLinkHiddenPicture, self),
	    }
	}
end

local function get_child_objects(root)
	local result = {}
	for i = 0, root:size() - 1 do
		table.insert(result, root:get(i))
		if root:get(i):getChilds():size() > 0 then
			table.insert(result, get_child_objects(root:get(i):getChilds()))
		end
	end
	local flatten_childs = table.unpack(result)
	return flatten_childs
end

-------------------------------------------------------------------------------
function HintBase:hint()
	local objects = self.playfield:getMainEntity():getChilds()
	
	local hint_objects = {}
	
	local flatten_childs = get_child_objects(objects)
	hint_objects = self:getHintObjects(flatten_childs, true);

	if #hint_objects>0 then
		local obj_to_hint
		local obj_name
		
		if #hint_objects > 1 then
			if self.first_hinted_object ~= nil then
				obj_to_hint = self.first_hinted_object
				self.first_hinted_object = nil
				self.last_hinted_object = obj_to_hint:getName()
					-- PlaySound("hint")
				self:playHintAnimation(obj_to_hint)		        
				-- Console:pushText('hinted: '..obj:getName())    
			else
				local ind = math.random(1, #hint_objects)
				obj_to_hint = hint_objects[ind]
				
				if obj_to_hint[1]:getName() == self.last_hinted_object then
					table.remove(hint_objects, ind)
					obj_to_hint = table.random_value(hint_objects)
				end
				self.last_hinted_object = obj_to_hint[1]:getName()
				obj_to_hint = table.unpack(obj_to_hint) 
					-- PlaySound("hint")
				for i,obj in pairs(obj_to_hint) do
					self:playHintAnimation(obj)
					-- temp
					-- Console:pushText('hinted: '..obj:getName())
				end
			 end
		else
			obj_to_hint = hint_objects[1];
			self.last_hinted_object = obj_to_hint[1]:getName()
			obj_to_hint = table.unpack(obj_to_hint)
				-- PlaySound("hint")
			for i,obj in pairs(obj_to_hint) do
				self:playHintAnimation(obj)
				-- Console:pushText('hinted: '..obj:getName())
			end
		end
		return HintResult.RESET;
	end
	
	return HintResult.NO_OBJECTS;
end

----------------------------------------------------------------------------------
function HintBase:IsAccessible(object)
	local hint_disabled = object.properties and object.properties.hint_disabled

	if  object and object:isGlobalRespondable() and not hint_disabled then
		local active_zone = self.playfield:getActiveZones():GetTop()
		if active_zone then
			if active_zone:hasObject(object) or self:isForcedObject(object) then
				return true
			end
		else
			return true
		end
	end
	
	return false
end

----------------------------------------------------------------------------------
function HintBase:setAdditionalHintObject(object_name, ...)
	local addobj = {...}
	if not addobj then
		addobj = true
	end
	
	self.additional_hint_objects[object_name] = addobj
end

-------------------------------------------------------------------------------
function HintBase:removeAdditionalHintObject(object_name)
	self.additional_hint_objects[object_name] = nil
end

-------------------------------------------------------------------------------
function HintBase:removeAllAdditionalHintObjects()
	self.additional_hint_objects = {}
end

----------------------------------------------------------------------------------
function HintBase:setForcedHintObject(object_name, ...)
	local forced_obj = {...}
	if not forced_obj then
		forced_obj = true
	end
	
	self.forced_hint_objects[object_name] = forced_obj
end

-------------------------------------------------------------------------------
function HintBase:removeForcedHintObject(object_name)
	self.forced_hint_objects[object_name] = nil
end

-------------------------------------------------------------------------------
function HintBase:removeAllForcedHintObjects(object_name)
	self.forced_hint_objects[object_name] = nil
end

-------------------------------------------------------------------------------
function HintBase:isForcedObject(object)
	local forced_object_data = self.playfield:getForcedObject()
	local result = forced_object_data and
				   forced_object_data.object:getName() == object:getName()
	
	return result
end

-------------------------------------------------------------------------------
function HintBase:isForcedHintObject(object)
	local table_objects = self.forced_hint_objects
	local result = table_objects and 
				   table_objects[object:getName()]~=nil
	
	return result
end

-------------------------------------------------------------------------------
function HintBase:isTaskObject(object)
	local result = false
	local ho_logic = self.playfield:getHOLogic() 
	if ho_logic then
		result = ho_logic:isTaskObject( object:getName() )
	end
	
	return result
end

-- TODO: test this ------------------------------------------------------------
-------------------------------------------------------------------------------
function HintBase:isTaskHPTargetObject(object)
	local logic     = self.playfield:getHOLogic()
	local panelMode = self.playfield:getGameScene():getPanel():getMode()
	
	if logic and panelMode == EPanelMode.HIDDENPICTURE then
		local info = logic:getTaskInfo(object:getName(), 'getObjectName')
		
		if info and info.state == logic.ETaskState.SHOWED then
			return true
		end
	end
	
	return false
end

-------------------------------------------------------------------------------
function HintBase:isAdditionalHintObject(object)
	local table_objects = self.additional_hint_objects
	local add_obj
	local result = table_objects and 
				   table_objects[object:getName()]
	
	if result and type(table_objects[object:getName()])=='table' then
		add_obj = {}
		for i, obj_name in pairs(table_objects[object:getName()]) do
			table.insert(add_obj, self.playfield:getEntityNamed(obj_name))
		end
	end
	
	return result, add_obj
end

-------------------------------------------------------------------------------
function HintBase:isFoundTool(object)
	local obj_name = object:getName()

	local result = false

	local find_tool_objects = self.playfield:getToolFindObjectNames()
	if find_tool_objects[obj_name] then
		local tool_name = find_tool_objects[obj_name]
		result = not IsToolFound(tool_name)
	end
	return result
end

-------------------------------------------------------------------------------
function HintBase:isAppliedTool(object)
	return self.playfield:isObjectTargetForToolWeHave(object:getName())
end

-------------------------------------------------------------------------------
function HintBase:isActiveObject(object)
	local obj_name = object:getName()
	local active_objects = self.playfield:getActiveObjects()
	local result = active_objects and
				   active_objects[obj_name] 
				   and (not active_objects[obj_name].multi_use)
	-- WriteInfo(obj_name..' active: '..tostring(result))
	return result
end

-------------------------------------------------------------------------------
function HintBase:isDragObject(object)
	local function searchCondition(dragObject)
		return dragObject:getTakeTarget():getName() == object:getName()
	end

	local dragManager = self.playfield:getDragManager()
	local dragObject  = dragManager:find( searchCondition )

	if dragObject then
		local function isTargetAccessible(target)
			if not target:hasParent( self.playfield:getMainEntity() ) then
				return false
			end

			local zone_of_object = getZoomZoneOfObject(target:getName())

			if zone_of_object and zone_of_object:isDisabled() then
				return false
			end

			return target:isVisible() and target:getOpacity() > 0 and target:isRespondable()
		end

		local dropTargets = dragObject:getDropTarget()
		local targets	  = table.filter(dropTargets, isTargetAccessible )

		if #targets > 0 then
			local additionalObject = (#targets == 1) and targets[1] or targets
			return true, additionalObject
		end
	end
	
	return false
end

-------------------------------------------------------------------------------
function HintBase:isZoomZoneFirstVisit(object)
	local obj_name = object:getName()
	local result = false
	local zones = self.playfield:getZones()
	
	if zones then
		for i, zone in pairs(zones) do
			if  zone:getTriggerName() == obj_name and
				not zone:isDisabled() and
				not isZoomZoneVisited(zone:getName(), self.playfield:getName())
			then
				result = true
			end
		end
	end
	
	return result
end

-------------------------------------------------------------------------------
function HintBase:isActiveZoomZone(object)
	local zoom_zone
	local result = false
	local zones = self.playfield:getZones()
	
	if zones then
		for i, zone in pairs(zones) do
			if  zone:getTriggerName() == object:getName() and not zone:isDisabled() then
				zoom_zone = zone
			end
		end
	end
	
	if not zoom_zone then
		return result
	end
	
	local flatten_childs = get_child_objects(zoom_zone:getObjects())
	for k, v in pairs(flatten_childs) do
		local zoom_object = v
		local is_proper 
		
		if  zoom_object.getOpacity and 
			zoom_object:getOpacity() > 0. and
			zoom_object:isVisible() and
			zoom_object:isRespondable()
		then
			is_proper = self:IsProperObject(zoom_object, self.search_functions, false)
		end
		
		if is_proper then
			result = true
			break
		end 
	end
	
	return result
end

-------------------------------------------------------------------------------
function HintBase:isMinigameFirstVisit(object)
	local obj_name   = object:getName()
	local link_scene = self.playfield:getLinkedScenes()[obj_name]
	
	local result =  link_scene and 
					isMinigameLevelType(link_scene.level_name) and
					not isSceneVisited(link_scene.level_name) and
					not isSceneClosed(link_scene.level_name) 
	
	return result	
end

-------------------------------------------------------------------------------
function HintBase:isActiveMinigame(object)
	local obj_name   = object:getName()
	local link_scene = self.playfield:getLinkedScenes()[obj_name]
	
	local result = false
	local is_minigame = link_scene and 
						isMinigameLevelType(link_scene.level_name) and
						not isSceneClosed(link_scene.level_name)
				   
	if is_minigame then
		local tools = GetToolsApply(link_scene.level_name)
		local inventory = self.playfield:getInventory()
		
		local have_tools = false
		for k, v in pairs(tools) do
			local tool_info = inventory:getToolInfoByName(v)
			if tool_info and inventory:isActiveTool(tool_info) then
				have_tools = true
			else
				have_tools = false
				break
			end
		end
		
		local tools_applied = false
		for k, v in pairs(tools) do
			if IsToolApplied(v) then
				tools_applied = true
			else
				tools_applied = false
				break
			end
		end
		
		result = have_tools or tools_applied
	end
	
	return result
end

-------------------------------------------------------------------------------
function HintBase:isActiveLinkScene(object)
	local obj_name   = object:getName()
	local link_scene = self.playfield:getLinkedScenes()[obj_name]
	
	local result = link_scene and 
				   not isMinigameLevelType(link_scene.level_name) and
				   not isSceneClosed(link_scene.level_name) and
				   self.playfield.isActiveLinkScene and
				   self.playfield:isActiveLinkScene(link_scene.level_name)
	
	return result
end

-------------------------------------------------------------------------------
function HintBase:isLinkHiddenObject(object)
	local obj_name   = object:getName()
	local link_scene = self.playfield:getLinkedScenes()[obj_name]
	
	local result = link_scene and 
				   isHiddenObjectLevel(link_scene.level_name) and
				   not isSceneClosed(link_scene.level_name)
	
	return result
end

-------------------------------------------------------------------------------
function HintBase:isLinkHiddenPicture(object)
	local obj_name   = object:getName()
	local link_scene = self.playfield:getLinkedScenes()[obj_name]
	
	local result = link_scene and 
				   isHiddenPictureLevel(link_scene.level_name) and
				   not isSceneClosed(link_scene.level_name)
	
	return result
end

----------------------------------------------------------------------------------
function HintBase:IsProperObject(object, SearchFunction, check_is_accessible)
	local result = false
	local priority 
	local additional_obj 
	
	check_is_accessible = check_is_accessible==nil and true or false
	
	if (not check_is_accessible) or self:IsAccessible(object) then
		for iPriority, Func in pairs(SearchFunction) do
			local res, add_obj 
			
			if type(Func)=='table' then
				local priority
				res, priority, add_obj = self:IsProperObject(object, Func, false)
			else
				res, add_obj = Func(object)
			end
			
			if res then
				result         = true
				priority       = iPriority
				additional_obj = add_obj
				break
			end
		end
	end
	
	return result, priority, additional_obj
end

-------------------------------------------------------------------------------
function HintBase:getHintObjects(objects, top_priority_only)
	local result_objects = {}
	local result_tables  = {}
	local min_priority
	
	-- if IsMouseBlocked() then
		-- return result_objects
	-- end
	
	-- for object in Objects do
	for k, v in pairs(objects) do
		-- Console:pushText('checking:')
		-- Console:pushText(tostring(v:getName()))
		local object = v
		local is_proper, priority, additional_obj = self.playfield:onHintObject(object, self:IsProperObject(object, self.search_functions) )
		-- Console:pushText('is proper:'..tostring(is_proper))
		if is_proper then
			if not result_tables[priority] then
				result_tables[priority] = {}
			end
			
			if additional_obj then
				table.insert(result_tables[priority], {object, additional_obj})
			else
				table.insert(result_tables[priority], {object})
			end
			
			if (not min_priority) or (min_priority>priority) then
				min_priority = priority
			end
		end
	end
	
	if top_priority_only and min_priority then
		result_tables = {result_tables[min_priority]}
	end
	
	result_objects = table.unpack(result_tables, 2)
	
	return result_objects
end