dofile('data/Scripts/HintComposition.lua')

-------------------------------------------------------------------------------
function Hint()
    local objects = play_field:GetObjects()
    
	-- Getting additional objects from script
	local hint_objects = {}
	hint_objects = GetHintObjects(objects, true);

	if #hint_objects>0 then
	    local obj_to_hint
	    local obj_name
	    
		if #hint_objects > 1 then
		    local ind = math.random(1, #hint_objects)
			obj_to_hint = hint_objects[ind]
			
			if obj_to_hint[1]:GetName() == play_field.last_hinted_object then
				table.remove(hint_objects, ind)
				obj_to_hint = table.random_value(hint_objects)
			end
		else
			obj_to_hint = hint_objects[1];
		end
		
		play_field.last_hinted_object = obj_to_hint[1]:GetName()
        PlaySound("hint")
        
        obj_to_hint = table.unpack(obj_to_hint)
        for i,obj in pairs(obj_to_hint) do
            PlayHintComposition( obj:GetScrX(), obj:GetScrY(), obj:GetScrZ())
--          Console:Log( 'Hint: '..obj:GetName())
        end

        -- counting hint amount in last 5 seconds -----------------------------------------
        --[[local level = GetCurrentLevel()
        if level.hint_counter then
             level.hint_counter = level.hint_counter+1
             if level.hint_counter  >=5 then
                PauseAmbientMusic(true)
                PlaySound('Hint_spam')
                level.timer:Add(function() ContinueAmbientMusic(true) end, 180)
                level.hint_counter =nil
             end
        else 
            level.hint_counter =1
        end
        
        level.timer:Add(function() 
                            if level.hint_counter then 
                                level.hint_counter = level.hint_counter -1
                                if level.hint_counter <=0 then level.hint_counter =nil end
                            end
                        end, 5)--]]
        -------------------------------------------------------------------------------------
        
        return true
    end
    
    return false;
end
----------------------------------------------------------------------------------

function IsAccessible(object)
    local level = GetCurrentLevel()
    
    if  object and
        object:GetScrOpacity() > 0. and
        object:IsVisible() and
		not play_field:IsIgnoredOnTestPoint( object:GetName() ) and
        not level.locked_objects[obj_name]
    then
        if level.active_zone then
            if level.active_zone:HasObject( object:GetName() ) then
                return true
            end
        else
            return true
        end
    end
    
    return false
end
----------------------------------------------------------------------------------
----------------------------------------------------------------------------------
local IsForcedObject         = nil
local IsForcedHintObject     = nil
local IsTaskObject           = nil
local IsTaskHPTargetObject   = nil
local IsFoundTool            = nil
local IsAppliedTool          = nil
local IsAdditionalHintObject = nil
local IsActiveObject         = nil
local IsDragObject           = nil
local IsZoomZoneFirstVisit   = nil
local IsActiveZoomZone       = nil
local IsMinigameFirstVisit   = nil
local IsActiveMinigame       = nil
local IsActiveLinkScene      = nil
local IsLinkHiddenObject     = nil
-------------------------

IsForcedObject = function(object)
    local forcedObject = GetCurrentLevel().forcedObject
    local result = forcedObject and
                   forcedObject == object:GetName()
    
	return result
end
-------------------------

IsForcedHintObject = function(object)
    local table_objects = GetCurrentLevel().forced_hint_objects
    local result = table_objects and 
                   table_objects[object:GetName()]~=nil
    
	return result
end
-------------------------

IsTaskObject = function(object)
    local result = play_field:GetLevelScreen():IsTaskObject( object:GetName() )
    
	return result
end
-------------------------

IsTaskHPTargetObject = function(object)
    local logic     = GetCurrentLevel():GetAdditionalLogic()
    local panelMode = GetCurrentLevel():GetPanelLuaObject():GetMode()
    
    if logic and panelMode == EPanelMode.PM_HIDDENPICTURE then
        local info = logic:GetTaskInfo(object:GetName(), 'getObjectName')
        
        if info and info.state == logic.ETaskState.TS_SHOWED then
            return true
        end
    end
    
    return false
end
-------------------------

IsAdditionalHintObject = function(object)
    local table_objects = GetCurrentLevel().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, play_field:GetObject(obj_name))
        end
    end
    
	return result, add_obj
end
-------------------------

IsFoundTool = function(object)
    local level = GetCurrentLevel()
    local obj_name = object:GetName()
    local result = false
    
    for name, tool in pairs(level.tools) do
        if  tool.trigger_name == obj_name and
            not IsToolFound(name)
        then
            result = true
            break
        end
    end
    
	return result
end
-------------------------

IsAppliedTool = function(object)
    local level = GetCurrentLevel()
    local obj_name = object:GetName()
    local result = false
    
    local function TargetCompare(target_name, obj_name)
        local result = false
        
        if     type(target_name)=='string' then
            result = target_name == obj_name
        elseif type(target_name)=='table' then
            for i,Name in pairs(target_name) do
                if Name == obj_name then
                    result = true
                    break
                end
            end
        end
        
        return result
    end
    
    for name, tool in pairs(level.tools) do
        if  TargetCompare(tool.target_name, obj_name) and
            IsToolFound(name) and
            not IsToolApplied(name)
        then
            result = true
            break
        end
    end
    
	return result
end
-------------------------

IsActiveObject = function(object)
    local level = GetCurrentLevel()
    local obj_name = object:GetName()
    local result = level.active_objects and
                   level.active_objects[obj_name] and 
                   (not level.active_objects[obj_name].multi_use--[[ or
                    not IsObjectClicked(obj_name)]])
    
	return result
end
-------------------------

IsDragObject = function(object)
    local level = GetCurrentLevel()
    local drag_object_name = GetDragObjectNameByGetArea(object:GetName())
    
    if drag_object_name then
        local info = level.drag_objects[drag_object_name]
        local result = false
        local add_obj
        
        -----------
        local isTargetAccessible = function(target)
            if info.bLockTargetArea then
                return target:IsVisible() and
                       target:GetOpacity() > 0.0
            end
            
            return IsAccessible(target)
        end
        -----------
        
        if info and info.target_area then
            if type(info.target_area) == 'string' then
                local target_area_object = play_field:GetObject(info.target_area);
                if isTargetAccessible(target_area_object) then
                    result = true
                    add_obj = target_area_object
                end
            else
                for _, target_area_name in pairs(info.target_area) do
                    local target_area_object = play_field:GetObject(target_area_name);
                    if isTargetAccessible(target_area_object) then
                        result = true
                        
                        if not add_obj then
                            add_obj = {}
                        end
                        table.insert(add_obj, target_area_object)
                    end
                end
            end
        end
        
        return result, add_obj
    end
    
    return false
end
-------------------------

IsZoomZoneFirstVisit = function(object)
    local level = GetCurrentLevel()
    local obj_name = object:GetName()
    local result = false
    
    if level.zones then
        for i, zone in pairs(level.zones) do
            if  zone.trigger_name == obj_name and
                not zone:IsDisabled() and
                not IsZoomZoneVisited(obj_name, level.name)
            then
                result = true
            end
        end
    end
    
	return result
end
-------------------------

IsActiveZoomZone = function(object)
    local level = GetCurrentLevel()
    local zoom_zone
    local result = false
    
    if level.zones then
        for i, zone in pairs(level.zones) do
            if  zone.trigger_name == object:GetName() and not zone:IsDisabled() then
                zoom_zone = zone
            end
        end
    end
    
    if not zoom_zone then
        return result
    end
    
    local SearchFunction = {
        IsForcedObject,
        IsForcedHintObject,
        IsTaskObject,
        IsTaskHPTargetObject,
        { 
          IsFoundTool,
          IsAppliedTool
        },
        {
          IsAdditionalHintObject,
          IsActiveObject,
          IsDragObject
        },
        {
          IsZoomZoneFirstVisit,
          IsActiveZoomZone,
          IsMinigameFirstVisit,
          IsActiveMinigame,
          IsActiveLinkScene
        },
        IsLinkHiddenObject
    }
    
    for object_name in pairs(zoom_zone.objects) do
        object = play_field:GetObject(object_name)
        local is_proper 
        
        if object:GetOpacity() > 0. and
           object:IsVisible() and
           not play_field:IsIgnoredOnTestPoint( object_name ) and
           not level.locked_objects[object_name]
        then
            is_proper = IsProperObject(object, SearchFunction, false)
        end
        
        if is_proper then
            result = true
            break
        end 
    end
    
	return result
end
-------------------------
IsMinigameFirstVisit = function(object)
    local level      = GetCurrentLevel()
    local obj_name   = object:GetName()
    local link_scene = level.linked_scenes[obj_name]
    
    local result = link_scene and 
				   IsLevelMiniGame(link_scene.level_name) and
				   not IsSceneVisited(link_scene.level_name) and
				   not IsSceneClosed(link_scene.level_name) 
    
	return result
end
-------------------------

IsActiveMinigame = function(object)
    local level      = GetCurrentLevel()
    local obj_name   = object:GetName()
    local link_scene = level.linked_scenes[obj_name]
    
    local result = false
    local is_minigame = link_scene and 
				        IsLevelMiniGame(link_scene.level_name) and
				        not IsSceneClosed(link_scene.level_name)
				   
	if is_minigame then
	    local tools = GetToolsApply(link_scene.level_name)
	    
	    result = IsToolsHave(tools, link_scene.level_name) or
	             IsToolsApplied(tools, link_scene.level_name)
	end
	             
    if is_minigame and level.IsActiveMinigame then
	    result = level:IsActiveMinigame(link_scene.level_name, result)
	end
    
	return result
end
-------------------------

IsActiveLinkScene = function(object)
    local level      = GetCurrentLevel()
    local obj_name   = object:GetName()
    local link_scene = level.linked_scenes[obj_name]
    
    local result = link_scene and 
				   not IsLevelMiniGame(link_scene.level_name) and
				   not IsSceneClosed(link_scene.level_name) and
				   level.IsActiveLinkScene and
				   level:IsActiveLinkScene(link_scene.level_name)
    
	return result
end
-------------------------

IsLinkHiddenObject = function(object)
    local level      = GetCurrentLevel()
    local obj_name   = object:GetName()
    local link_scene = level.linked_scenes[obj_name]
    
    local result = link_scene and 
				   IsLevelHiddenObject(link_scene.level_name) and
				   not IsSceneClosed(link_scene.level_name)
    
	return result
end
----------------------------------------------------------------------------------

function 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 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 = 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 GetHintObjects(Objects, TopPriorityOnly)
    local level = GetCurrentLevel()
    local result_objects = {}
    local result_tables  = {}
    local min_priority
    
    if IsMouseBlocked() and not level.forcedObject then
        return result_objects
    end
    
    local SearchFunction = {
        IsForcedObject,
        IsForcedHintObject,
        IsTaskObject,
        IsTaskHPTargetObject,
        { 
          IsFoundTool,
          IsAppliedTool
        },
        {
          IsAdditionalHintObject,
          IsActiveObject,
          IsDragObject
        },
        {
          IsZoomZoneFirstVisit,
          IsActiveZoomZone,
          IsMinigameFirstVisit,
          IsActiveMinigame,
          IsActiveLinkScene
        },
        IsLinkHiddenObject
    }
    
    for object in Objects do
        local is_proper, priority, additional_obj = level:OnDefaultHintObject(object, IsProperObject(object, SearchFunction) )
        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 TopPriorityOnly and min_priority then
        result_tables = {result_tables[min_priority]}
    end
    
    result_objects = table.unpack(result_tables, 2)
    
    return result_objects
end