function GetToolGlobalTargets(toolName)
    return nil
end
--------------------------------------------------------------------------------

function RegisterTools()
    local level = GetCurrentLevel()
    
	local auxRegisterTool = function(toolName, inv_path, window_path)
        assert(toolName and "All parameters haven't been specified!");
        
        local globalTargets = GetToolGlobalTargets(toolName)
        
        local function create_tool()
            local anim = CreateAnimationByTextureName(inv_path)
            return level:GetInventory():CreateObject(toolName..TOOL_INVENTORY_SUFFIX,anim,512,384,300,0,1,1)
        end
        
        local function create_icon()
            local anim = CreateAnimationByTextureName(inv_path);
            local obj  = level:GetInventory():CreateObject(toolName..TOOL_SHADOW_SUFFIX,anim,512,384,300,0,1,1)
            obj:GetAnimation():SetColor('0x88FFFFFF')
            return obj
        end
        
        local function create_wnd_icon()
            local anim = CreateAnimationByTextureName(window_path);
            return play_field:CreateObject(toolName..TOOL_WINDOW_SUFFIX,anim,512,384,300,0,1,1)
        end
        
        RegisterTool(toolName, globalTargets, create_tool, create_icon, create_wnd_icon)                      
    end
    
    local toolFolder = (IsLevelCollectorsEdition(level.name)) and TOOL_FOLDER_EXTRA or TOOL_FOLDER
		  
    local toolInfoVec = ParseToolsDirectory(toolFolder, TOOL_INVENTORY_SUFFIX, TOOL_WINDOW_SUFFIX)
    for i = 0, toolInfoVec:size() - 1 do
        local name_tool     = toolInfoVec:get(i).toolName
        local inv_path      = toolInfoVec:get(i).toolInvPath
        local window_path   = toolInfoVec:get(i).toolWindowPath
        
        auxRegisterTool(name_tool, inv_path, window_path)
    end
    
    for tool_name, _ in pairs(level.tools) do
        CreateFoundButNotShownTool(tool_name)
    end
end
--------------------------------------------------------------------------------

function RegisterTool(name, global_targets, create_tool, create_icon, create_info_icon)
    local level = GetCurrentLevel()
    
    local info            = {}
    info.name             = name
	info.global_targets   = global_targets
    info.create_tool      = create_tool
    info.create_icon      = create_icon
    info.create_info_icon = create_info_icon
    info.info_text        = 'TOOL_INFO_'..name:upper()
    info.hint_text        = 'TOOL_HINT_'..name:upper()
    
    info.apply_request    = function(tool_name, obj_name)
                                return true
                            end
    
    info.getLevelTargets  = function()
                                if info.global_targets == nil then
                                    return nil
                                end
                                
                                local localTargets = {}
                                
                                for _, globalTarget in ipairs(info.global_targets) do
                                    if globalTarget.level_name == level.name then
                                        table.insert(localTargets, globalTarget.target_name)
                                    end
                                end
                                
                                return localTargets
                            end
    
    level.tools[name] = info

    if level.mode == 'quest'
        and     IsToolFound(name)
        and not IsToolApplied(name)
    then
        assert(info.create_tool ~= nil)
        
        info.tool = info.create_tool()
        info.icon = info.create_icon()

        if HasGameState(TOOL_INFO_SHOWN, name) then
            local inv = level:GetInventoryLuaObject()
            inv:AddTool(name, info)
            inv:RotateTargetCell(name, nil)
	    end
    end
end
--------------------------------------------------------------------------------

function RegisterToolApply(ToolName)
    local level_name = GetCurrentLevel().name
    local state_name = 'RegisterToolApply_'..level_name
    
    if not HasGameState(state_name, ToolName) then
        AddGameState(state_name, ToolName)
    end
end
--------------------------------------------------------------------------------

function GetToolsApply(level_name)
    local level = GetCurrentLevel()
    level_name = level_name or level.name
    local state_name = 'RegisterToolApply_'..level_name
    local result = {}
    
    if HasGameState(state_name) then
        for i=0, GetGameState(state_name):GetSize()-1 do
            local toolName = GetGameState(state_name):Get(i)
            table.insert(result, toolName)
        end
    end
    
    return result
end
--------------------------------------------------------------------------------

function CreateFoundButNotShownTool(tool_name)
    local level = GetCurrentLevel()
    
    if level.mode == 'quest'
		and     IsToolFound(tool_name)
		and not IsToolApplied(tool_name)
		and not HasGameState(TOOL_INFO_SHOWN, tool_name)
    then
        local info = level.tools[tool_name]
        local inv  = level:GetInventoryLuaObject()
        
        inv:AddTool(tool_name, info)
        inv:LockTool(tool_name)

        local toolWindowInfo = CreateToolWindow(info)
        toolWindowInfo:Open()
    end
end
--------------------------------------------------------------------------------



-----------------------------------------------------------------------------------
-- GLOBAL TOOL TARGET -------------------------------------------------------------
-----------------------------------------------------------------------------------

class 'GlobalToolTarget'

function GlobalToolTarget:__init(level_name, tool_name, target_name)
    self.level_name  = level_name
    self.tool_name   = tool_name
    self.target_name = target_name
end
-----------------------------------------------------------------------------------

function GlobalToolTarget:IsUsed()
    local state, value = GenerateToolTargedUsed(self.tool_name, self.target_name, self.level_name)
    return HasGameState(state, value)
end
-----------------------------------------------------------------------------------

function GlobalToolTarget:IsUsedInLocation(location_name)
    if location_name == self.level_name then
        return IsUsed()
    end
    
    return false
end
-----------------------------------------------------------------------------------

function IsToolAllGlobalTargetsUsed(tool_name)
    local level  = GetCurrentLevel()
    local info   = level.tools[tool_name]
    
    assert(info)
    
    if info.global_targets ~= nil then
        for _, globalTarget in ipairs(info.global_targets) do
            if not globalTarget:IsUsed() then
                return false
            end
        end
    end
    
    return true
end
-----------------------------------------------------------------------------------


--------------------------------------------------------------------------------
-- TOOLS  HELPERS --------------------------------------------------------------
--------------------------------------------------------------------------------

function RegisterToolFindInfo(  name,
                                trigger_name,
                                on_find,
                                on_find_disable_zoom_request,
                                do_not_show_window)
    
    local level = GetCurrentLevel()
    local info  = level.tools[name]
    
    assert(info)
    
   -- info.text_alias         = text_alias
    info.trigger_name       = trigger_name
    info.on_find            = on_find
    info.find_scene         = GetCurrentLevel().name
    info.do_not_show_window = do_not_show_window
     
    if on_find_disable_zoom_request == nil then
        on_find_disable_zoom_request = true
    end
     
    if type(on_find_disable_zoom_request) == 'function' then
        info.disable_zoom_request = on_find_disable_zoom_request
    else
        info.disable_zoom_request = function()
                                        return on_find_disable_zoom_request
                                    end
    end

    if IsToolFound(name) then
        if trigger_name then
            play_field:GetObject(trigger_name):SetVisible(false)
        end
        
		if info.disable_zoom_request() then
			local zone = GetObjectsZone(trigger_name)
			if zone then	
				zone:Disable()
			end
		end
		
		if info.on_find then
		    local event     = {}
		    event.save      = true
		    event.target    = trigger_name
		    event.toolName  = name
		    
			info.on_find(event)
		end
    else
        if  level.mode ~= 'hidden_object' and
            level.mode ~= 'hidden_picture'
        then
            level.custom_cursors[trigger_name] = 'CursorActive'
            level:UnlockObject(trigger_name)
        end
    end
end
--------------------------------------------------------------------------------

function RegisterToolApplyInfo(name,
							   target_name,
							   on_apply,
							   text_alias,
							   apply_request, 
                               once_apply)
                               
    local info            = GetCurrentLevel().tools[name]
    info.target_name      = (target_name ~= nil) and target_name or info.getLevelTargets()
    info.on_apply         = on_apply
    info.on_get           = nil
    info.on_drop          = nil
	info.once_apply       = once_apply
	info.apply_text_alias = text_alias
	
	assert(info.target_name)
	
	info.local_targets_count   = (type(info.target_name) == 'table') and #info.target_name or 1
	info.local_targets_applied = 0
	
	if apply_request then
		info.apply_request = apply_request
	end
    
    local targetCursor = 'CursorGlass'
    local bToolApplied = IsToolApplied(name)
    
    if type(info.target_name) == 'string' then
        local state, value = GenerateToolTargedUsed(name, info.target_name)
        
        if HasGameState(state, value) then
            info.local_targets_applied = info.local_targets_applied + 1
            
            if on_apply then
                local event                  = {}
                event.save                   = true
                event.target                 = info.target_name
                event.toolName               = name
                event.applied                = bToolApplied
                event.allLocalTargetsApplied = (info.local_targets_applied == info.local_targets_count)
                
                on_apply(event)
            end
        else
            GetCurrentLevel().custom_cursors[info.target_name] = targetCursor
        end
    else
        if not info.global_targets then
			info.global_targets = {}
		end
		do -- добавить зоны применения в global_targets, если их там нет.
			for _, target in ipairs(info.target_name) do
				local not_in_global_targets = true
				for i, v in ipairs(info.global_targets) do
					if v.level_name == GetCurrentLevel().name and 
					   v.target_name == target and
					   v.tool_name == info.name
					then
						not_in_global_targets = false
						break
					end
				end
				if not_in_global_targets then
					table.insert(info.global_targets, GlobalToolTarget(GetCurrentLevel().name, info.name, target))
				end
			end
			GetCurrentLevel().tools[name].global_targets = info.global_targets
		end
    
        local i = 1
        while info.target_name[i] do
            local v = info.target_name[i]
            local state, value = GenerateToolTargedUsed(name, v)
            
            if HasGameState(state, value) then
                info.local_targets_applied = info.local_targets_applied + 1
                
                if on_apply then
                    local event                  = {}
                    event.save                   = true
                    event.target                 = v
                    event.toolName               = name
                    event.applied                = (bToolApplied and #info.target_name == 1)
                    event.allLocalTargetsApplied = (info.local_targets_applied == info.local_targets_count)
                    
                    on_apply(event)
                end
                if once_apply then
                    info.target_name = nil
                    break
                end
                table.remove(info.target_name, i)
            else
                GetCurrentLevel().custom_cursors[v] = targetCursor
                i = i + 1
            end
        end
    end
    
    RegisterToolApply(name)
    
    return info
end
--------------------------------------------------------------------------------

function RegisterToolApplyOnDogInfo(name, on_apply, dogEffectTarget, bDontMarkDog)
    local info           = GetCurrentLevel().tools[name]
    info.target_name     = '<DOG_ON_PANEL>'
    info.on_apply        = on_apply
    info.on_get          = nil
    info.on_drop         = nil
    info.dogEffectTarget = dogEffectTarget
    info.bDogTarget      = true
    
    local bToolApplied = IsToolApplied(name)
    
    if type(info.target_name) == 'string' then
        local state, value = GenerateToolTargedUsed(name, info.target_name)
        
        if HasGameState(state, value) then
            if on_apply then
                local event    = {}
                event.save     = true
                event.toolName = name
                
                on_apply(event)
            end
        else
            if IsToolFound(name) and not bDontMarkDog then
                local level = GetCurrentLevel()
                local inv   = level:GetInventoryLuaObject()
                local panel = level:GetPanelLuaObject()
                
                inv:MarkDogCell(name)
                panel:SetActiveDogState(true)
            end
        end
    end
    
    RegisterToolApply(name)
    
    return info
end
--------------------------------------------------------------------------------

function FindTool(tool_name, do_not_show_window)
    if IsToolFound(tool_name) then
        return
    end
    
    do_not_show_window = do_not_show_window or false
        
    local level = GetCurrentLevel()

    GenerateTool(tool_name)
    AddGameState(TOOL_FOUND, tool_name)

	local info = level.tools[tool_name]
    local wnd

    if level.mode == 'quest'
		and not do_not_show_window
		and not HasGameState(TOOL_INFO_SHOWN, tool_name)
    then
        wnd = CreateToolWindow(info)
    else
        local inv = level:GetInventoryLuaObject()
        inv:OnToolFound(tool_name)
    end
	
    if info.on_find ~= nil then
        local event     = {}
	    event.save      = false
		event.target    = info.trigger_name
		event.toolName  = tool_name
		event.ToolWnd   = wnd
	    
        info.on_find(event)
    end
    
    if wnd then
        wnd:Open()
    end
end
--------------------------------------------------------------------------------

function GenerateTool(tool_name)
    local level = GetCurrentLevel()
    local inv   = level:GetInventoryLuaObject()
    local info  = level.tools[tool_name]

    info.tool = info.create_tool()
    info.icon = info.create_icon()
    
	inv:AddTool(tool_name, info)
    play_field:OnToolFound(tool_name, info.tool)
end
--------------------------------------------------------------------------------

function GenerateToolTargedUsed(tool_name, target_name, level_name)
    if not level_name then
        level_name = GetCurrentLevel().name
    end
    
    local value = level_name..': tool_name('..tool_name..') target_name('..target_name..')'
    return TOOL_TARGET_USED, value
end
--------------------------------------------------------------------------------

function IsToolFound(tool_name)
    return HasGameState(TOOL_FOUND, tool_name)
end
--------------------------------------------------------------------------------

function IsToolApplied(tool_name)
    return HasGameState(TOOL_APPLIED, tool_name)
end
--------------------------------------------------------------------------------

function IsToolsFound(tools_names)
    local result = true
    
    for i, tool_name in pairs(tools_names) do
        if not IsToolFound(tool_name) then
            result = false
            break
        end
    end
    
    return result
end
--------------------------------------------------------------------------------

function IsToolsHave(tools_names, level_name)
    local level  = GetCurrentLevel()
    level_name = level_name or level.name
    local result = false
    
    for i, tool_name in pairs(tools_names) do
        local tool = level.tools[tool_name]
        if tool.global_targets then
            for i, target in pairs(tool.global_targets) do
                if IsToolFound(tool_name) and (target.level_name == level_name and not target:IsUsed()()) then
                    result = true
                    break
                end
            end
            
            if result then
                break
            end
        else
            if IsToolFound(tool_name) and not IsToolApplied(tool_name) then
                result = true
                break
            end
        end
    end
    
    return result
end
--------------------------------------------------------------------------------

function IsToolsApplied(tools_names, level_name)
    local level  = GetCurrentLevel()
    level_name = level_name or level.name
    local result = true
    
    for i, tool_name in pairs(tools_names) do
        local tool = level.tools[tool_name]
        if tool.global_targets then
            for i, target in pairs(tool.global_targets) do
                if target.level_name == level_name and not target:IsUsed()() then
                    result = false
                    break
                end
            end
            
            if not result then
                break
            end
        else
            if not IsToolApplied(tool_name) then
                result = false
                break
            end
        end
    end
    
    return result
end
--------------------------------------------------------------------------------

function IsToolTargetUsed(tool_name, target_name, level_name)
    local state, value = GenerateToolTargedUsed(tool_name, target_name, level_name)
    return HasGameState(state, value)
end
--------------------------------------------------------------------------------

function IsToolTarget(tool_name, obj_name)
    local info = GetCurrentLevel().tools[tool_name]
    
    if info and info.target_name then
        if type(info.target_name) == 'string' then
            return info.target_name == obj_name
        else
            for i, target_name in pairs(info.target_name) do
                if target_name == obj_name then
                    return true, i
                end
            end
        end
    end
    
    return false
end
--------------------------------------------------------------------------------

function GetToolNameByTargetName(target_name, bNotUsed)
    bNotUsed = bNotUsed or false
    
    for tool_name, info in pairs(GetCurrentLevel().tools) do
        if info.target_name
            and not (bNotUsed and IsToolApplied(tool_name))
        then
            if type(info.target_name) == 'string' then
                if info.target_name == target_name then
                    return tool_name, nil
                end
                
            else
                for i, tmp_target_name in pairs(info.target_name) do
                    if tmp_target_name == target_name then
                        return tool_name, i
                    end
                end
            end
        end
    end
    
    return nil
end
--------------------------------------------------------------------------------

function CreateInfoWindow(...)
    local arg  = {...}
    local zone = CreateZoomZone('',
                                ZOOM_TYPE_INFO_WINDOW,
                                'ToolFoundWindow')
    zone.fading_enabled = false
    zone:SetOffsetZ(1000)
    
    -- create close button
    if not play_field:HasObject('Button_OK_Tools') then
        anim = CreateAnimation('data/Texture/GUI/Buttons/bttn_ok.png',0,0,162,104,3,0)
	    anim:SetHotSpot(81,52)
	    anim:SetBlendMode(2)
	    anim:SetColor('0xFFFFFFFF')
	    play_field:CreateObject('Button_OK_Tools',anim,512,525,120,0,1,1)
    end
    
    zone.close_button = ScriptCreateButton( Object('Button_OK_Tools'),
                                            zone.close_button_message,
                                            play_field:GetLevelScreen(),
                                            false)
    Helpers.AttachInPlace(zone.close_button, Object('ToolFoundWindow'))
    zone.close_button:SetZ(1)
    ----------------------
    
    for i, v in ipairs(arg) do
        if play_field:HasObject(v) then
            play_field:GetObject(v):GetAnimation():SetColor('0xFFFFFFFF')
            zone:AddObject(v)
        end
    end
    return zone
end
-----------------------------------------------------------------------------------

function CreateToolWindow(toolInfo)
    local inv = GetCurrentLevel():GetInventoryLuaObject()
    local tool = toolInfo.tool
    local text = toolInfo.info_text
    local CellInfo = inv:GetCellInfoByToolName(toolInfo.name)
    local wndIconMaxH = 145
    local wndIconY    = -90
    
    inv:LockTool(toolInfo.name)
    
    if not play_field:HasObject('ToolFoundWindow') then
        local anim = CreateAnimationByTextureName('data/Texture/GUI/OK/WindowOK.png')
        play_field:CreateObject('ToolFoundWindow',anim,512,326,300,0,1,1)
	end

	local toolFoundWindow = play_field:GetObject('ToolFoundWindow')
	toolFoundWindow:DestroyChildren()

    local wndIcon = toolInfo.create_info_icon()
    local zone    = CreateInfoWindow(wndIcon:GetName())
    zone.empty_obj:SetLayer( play_field:GetLevelScreen():GetInventory():GetLayer() + 2 )
    
    local scale = 1--math.min(1, wndIconMaxH / wndIcon:GetAnimation():GetHeight())
    wndIcon:SetScale(scale)
    wndIcon:SetXY(0, wndIconY)
    wndIcon:SetScrZ(toolFoundWindow:GetScrZ() + 2)

    local title = CreateTextEntity(MENU_FONT, 'CONGRATULATIONS')
    title:AttachTo(toolFoundWindow)
    title:SetBorderSize(500, 0)
    title:SetScale(1)
    title:SetXY(0, -170)
    title:SetZ(0.1)

    -------------------------------------------------------------------------------
    local params = ExplodeString("\\t", GetString(toolInfo.info_text));
    local textScale    = 0.8
    local textTracking = -2
    local textY        = 95
    local deltaY       = 40
   
    local tool_title_1 = CreateTextEntityWOLocalization(MENU_FONT, params:get(0))
    tool_title_1:SetScale(textScale)
    tool_title_1:SetXY(0, textY)
    tool_title_1:SetTracking(textTracking)
    tool_title_1:SetSpacing(0.6)
    tool_title_1:SetColor('0xFFFFFFFF')
    tool_title_1:AttachTo(toolFoundWindow)
    tool_title_1:SetZ(1)
    
    tool_title_2 = CreateTextEntityWOLocalization(MENU_FONT, (params:size() > 1) and params:get(1) or '\n$ NO STRING $')
    tool_title_2:SetScale(textScale)
    tool_title_2:SetXY(-5, textY + deltaY)
    tool_title_2:SetTracking(textTracking)
    tool_title_2:SetSpacing(0.6)
    --tool_title_2:SetColor('0xFFAA9999')
    tool_title_2:SetColor('0xFFFFFFDD')
    tool_title_2:AttachTo(toolFoundWindow)
    tool_title_2:SetZ(1)

    local textWidth = 440
    if tool_title_1:GetWidth() + tool_title_2:GetWidth() < textWidth then  
        local y = tool_title_1:GetY() + deltaY/2
        
        tool_title_1:SetXY(tool_title_1:GetX() - tool_title_2:GetWidth() / 2, y)
        tool_title_2:SetXY(tool_title_1:GetX() + tool_title_1:GetWidth() / 2 + tool_title_2:GetWidth() / 2, y)
    end    
    -------------------------------------------------------------------------------
    
    HideZoomZones()
    
    tool:Hide()
    toolInfo.icon:Hide()
    toolInfo.notInInventoryYet = true
    CellInfo.notInInventoryYet = true
    
    GetCurrentLevel().toolWindow = zone
    
    zone.on_close = function(zone)  
        GetCurrentLevel().toolWindow = nil
        
        if zone.on_click_ok then
            zone.on_click_ok(zone)
        end
        
        local tool_name = inv:GetToolName(tool)
        local cellIndex = inv:GetCellIndexByToolName(tool_name)
        local currIndex = inv:GetIndex()
        
        zone:RemoveObject(wndIcon:GetName())
        
        local invY = inv:GetY()
        if inv:GetVisibleCellsCount()<cellIndex then
            inv:SetIndex(cellIndex - inv:GetVisibleCellsCount())
        end
        inv:SlideUp(0)
        
        wndIcon:SetLayer(INVENTORY_LAYER);
		wndIcon:SetZ(TOOL_IN_INVENTORY_Z);
		wndIcon:AddParticleSystem('data/ParticleSystem/ObjectMoveTail.psi', 'ZoneHighlightParticle', true)
			
        local tool_fly_time = 0.9	
        local ef = CreateAnimationEffector()
        ef:SetDestroysOnEnd(true)
        ef:AttachTo(wndIcon)
        ef:SetKey(_TRACK_X, 0., wndIcon:GetX())
        ef:SetKey(_TRACK_Y, 0., wndIcon:GetY())
        ef:SetKey(_TRACK_S, 0., wndIcon:GetScale())
        ef:SetKey(_TRACK_O, 0.89, 1)
        ef:SetKey(_TRACK_X, tool_fly_time, tool:GetScrX())
        ef:SetKey(_TRACK_Y, tool_fly_time, tool:GetScrY())
        ef:SetKey(_TRACK_S, tool_fly_time, wndIcon:GetScale() * 0.2)
        ef:SetKey(_TRACK_O, tool_fly_time, 0)
        ef:Play()
        
        tool:Show()
        tool:SetOpacity(0)
        AddGameState(TOOL_INFO_SHOWN, tool_name)
        
        if GetCurrentLevel().OnDefaultToolWindowClose then
            GetCurrentLevel():OnDefaultToolWindowClose(tool_name)
        end
        
        if inv:GetVisibleCellsCount()<cellIndex then
            inv:SetIndex(currIndex)
        end
        inv:SetXY(nil, invY)
        
        --inv:SlideUp(0.5)
        inv:OnObjectMouseEnter(nil)
        
        if inv:GetVisibleCellsCount()<cellIndex then
            inv:SlideInventoryTo(cellIndex - inv:GetVisibleCellsCount())
        end
        
        inv:RotateTargetCell(toolInfo.name, true)
        GetCurrentLevel().timer:Add(function()
                                        CellInfo.notInInventoryYet = nil
                                        inv:OnToolFound(tool_name)
                                        inv:UnlockTool(tool_name)
                                        tool:SetOpacity(1)
                                        toolInfo.icon:Show()
                                        
                                        --inv:SlideDown(0.5)
                                        inv:OnObjectMouseLeave(nil)
                                        
                                        if GetCurrentLevel().OnDefaultToolAddToInventory then
											GetCurrentLevel():OnDefaultToolAddToInventory(tool_name)
                                        end
                                    end,
                                    tool_fly_time)
        
        ShowHiddenZoomZones()
        --[[
        local soundTrack = SelectRandom("object_fly_1", "object_fly_1")
        MakeSoundEntity(soundTrack):PlaySound(false)
        ]]
    end
    
    PlaySound("WindowOpen")
    return zone
end
-----------------------------------------------------------------------------------
