class 'Glint'

-------------------------------------------------------------------------------
function Glint:__init(playfield)

	self.playfield = playfield
	self.additional_glint_objects = {}
	self.forced_glint_objects = {}
	self.object_glint_animations = {}
	self.ignore_on_highlight = {}

	
	self.search_functions =
	{
		bind(Glint.isForcedObject, self),
		bind(Glint.isForcedGlintObject, self),
		-- bind(Glint.isTaskObject, self),
		bind(Glint.isTaskHPTargetObject, self),
		{
			bind(Glint.isFoundTool, self),
			bind(Glint.isAppliedTool, self),
		},
		{
			bind(Glint.isAdditionalGlintObject, self),
			bind(Glint.isActiveObject, self),
			bind(Glint.isDragObject, self),
		},
		{
			bind(Glint.isActiveZoomZone, self),
			bind(Glint.isZoomZoneFirstVisit, self),
			bind(Glint.isMinigameFirstVisit, self),
			bind(Glint.isActiveMinigame, self),
			bind(Glint.isActiveLinkScene, self),
		},
		bind(Glint.isLinkHiddenObject, 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 Glint:hint()
	if getPlayModeParameters().sparklesEnabled then -- isCasualPlayMode() then
		local objects = self.playfield:getMainEntity():getChilds()
		
		local glint_objects = {}
		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
			return result
		end
		local child_objects = get_child_objects(objects)
		local flatten_childs = table.unpack(child_objects)
		glint_objects = self:getGlintObjects(flatten_childs, true)

		if #glint_objects > 0 then
			local ind = math.random(1, #glint_objects)
			obj_to_glint = glint_objects[ind]
			for _, v2 in pairs(obj_to_glint) do
				self:playGlintAnimation(v2)
			end
		end
--[[
		for k, v in pairs(glint_objects) do
			local obj_to_glint = table.unpack(v)
			for k, v2 in pairs(obj_to_glint) do
				local obj_name = v2:getName()
				local is_note_trigger = false
				if not self.object_glint_animations[obj_name] and not is_note_trigger then
					local delay = math.random()
					self.playfield:getTimer():add(	function()
														self:playglintAnimation(v2)	
													end, delay * 2)
				end
			end
		end
]]
	end
end


-------------------------------------------------------------------------------
--[[
function Glint:glint()
	local objects = self.playfield:getMainEntity():getChilds()
	
	local glint_objects = {}
	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
		return result
	end
	local child_objects = get_child_objects(objects)
	local flatten_childs = table.unpack(child_objects)
	glint_objects = self:getglintObjects(flatten_childs, true);

	if #glint_objects>0 then
		local obj_to_glint
		local obj_name
		
		if #glint_objects > 1 then
			if self.first_glinted_object ~= nil then
				obj_to_glint = self.first_glinted_object
				self.first_glinted_object = nil
				self.last_glinted_object = obj_to_glint:getName()
					-- PlaySound("glint")
				self:playglintAnimation(obj_to_glint)		        
				-- Console:pushText('glinted: '..obj:getName())    
			else
				local ind = math.random(1, #glint_objects)
				obj_to_glint = glint_objects[ind]
				
				if obj_to_glint[1]:getName() == self.last_glinted_object then
					table.remove(glint_objects, ind)
					obj_to_glint = table.random_value(glint_objects)
				end
				self.last_glinted_object = obj_to_glint[1]:getName()
				obj_to_glint = table.unpack(obj_to_glint) 
					-- PlaySound("glint")
				for i,obj in pairs(obj_to_glint) do
					self:playglintAnimation(obj)
					-- temp
					-- Console:pushText('glinted: '..obj:getName())
				end                            
			 end   
		else
			obj_to_glint = glint_objects[1];
			self.last_glinted_object = obj_to_glint[1]:getName()
			obj_to_glint = table.unpack(obj_to_glint)
				-- PlaySound("glint")
			for i,obj in pairs(obj_to_glint) do
				self:playglintAnimation(obj)
				-- Console:pushText('glinted: '..obj:getName())
			end            
		end
		return true;
	end
	
	return false;
end
]]

----------------------------------------------------------------------------------
--[[
function Glint:glintObject(object)
	-- local x = object:GetScrX()
	-- local y = object:GetScrY()
	-- local z = object:GetZ()
	
	-- local glint_composition = createGlintAnimation()
	
	-- if glint_composition then
	-- 	glint_composition:Detach()
	-- 	glint_composition:DestroyEffectors()
	-- 	glint_composition:SetScrXY(x, y)
		
	-- 	local x, y, w, h = Helpers.GetObjectScrRect(object)
	-- 	local glint_w, glint_h = 353, 205
		
	-- 	local corrected_scale_x, corrected_scale_y = (w / glint_w), (h / glint_h)
	-- 	local min_scale_xy = math.min(corrected_scale_x, corrected_scale_y)

	-- 	glint_composition:SetScrScaleX(min_scale_xy + .2)
	-- 	glint_composition:SetScrScaleY(min_scale_xy + .2)
	-- 	Helpers.AttachInPlace(glint_composition, object)
	-- 	glint_composition:SetZ(1)
	-- 	glint_composition:SetVisible(true)
	-- 	ChangeOpacity(glint_composition, 0, 1, .4)
	-- 	glint_composition:Play()
	-- end
end
]]

----------------------------------------------------------------------------------
function Glint:hideGlint()
	-- for k, v in pairs(self.used_glint_anims) do
	-- 	if self.glint_animations:GetAnimation(k):GetOpacity() == 1 then
	-- 		ChangeOpacity(self.glint_animations:GetAnimation(k), 1, 0, .4)
	-- 	end
		
	-- 	self.used_glint_anims[k] = false
	-- end
	-- self.object_glint_animations = {}
end

----------------------------------------------------------------------------------
function Glint:playGlintAnimation(target_object)
	if not self.object_glint_animations[target_object:getName()] then
		local dx = 0
		local dy = 0

		local glint_animation = createGlintAnimation()
		glint_animation:setName('glint_'..target_object:getName())
		glint_animation:attachTo(target_object)
		glint_animation:setScrXY(0, 0)
		glint_animation:setScrXY(target_object:getScrX() + dx, target_object:getScrY() + dy)
		--BopoH-->Возвращаем родной скейл глинту, изменный скеилом родителя при атаче. 
		local objScale = target_object:getScale()
		glint_animation:setScale(1/objScale.x, 1/objScale.y)
		--BopoH<--
		glint_animation:setVisible(true)
		--glint_animation:setCyclic(true)
		self.object_glint_animations[target_object:getName()] = glint_animation

		self.object_glint_animations[target_object:getName()]:play()
		self.playfield:getTimer():add(	function()
											if self.object_glint_animations[target_object:getName()] ~= nil  then -- hack
												self.object_glint_animations[target_object:getName()]:destroy() 
												self.object_glint_animations[target_object:getName()] = nil 
											end
										end, glint_animation:getDuration())
	end

	
end
----------------------------------------------------------------------------------

function Glint:addIgnoreOnHighlight(object_name)
	self.ignore_on_highlight[object_name] = true
end
----------------------------------------------------------------------------------
function Glint:removeIgnoreOnHighlight(object_name)
	self.ignore_on_highlight[object_name] = nil
end
----------------------------------------------------------------------------------

function Glint:IsAccessible(object)
	if  object and
		object:isGlobalRespondable()
	then
		local active_zone = self.playfield:getActiveZones():GetTop()
		if active_zone then
			if active_zone:hasObject(object) then
				return true
			end
		else
			return true
		end
	end
	
	return false
end

-------------------------------------------------------------------------------
function Glint: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 Glint:isForcedGlintObject(object)
	local table_objects = self.forced_glint_objects
	local result = table_objects and 
				   table_objects[object:getName()]~=nil
	
	return result
end

-------------------------------------------------------------------------------
function Glint: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

-------------------------------------------------------------------------------
function Glint: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.TS_SHOWED then
			return true
		end
	end
	
	return false
end

-------------------------------------------------------------------------------
function Glint:isAdditionalGlintObject(object)
	local table_objects = self.additional_glint_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 Glint: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 Glint:isAppliedTool(object)
	return self.playfield:isObjectTargetForToolWeHave(object:getName())
end

-------------------------------------------------------------------------------
function Glint: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 Glint: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

			if dragObject:isEnableDropTargetsOnDrag() then
				return target:isVisible() and
					   target:getOpacity() > 0.0 and
					   target:isRespondable()
			end
			
			return self:IsAccessible(target)
		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 Glint: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 Glint: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 Glint: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 Glint: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
				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 Glint: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 Glint: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 Glint: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 Glint:getGlintObjects(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 and not self.ignore_on_highlight[object:getName()] 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