-- особый механизм хинтов для s10
s10_level_hints = {}

-- поисания хинтов для всех уровней должны находиться в scripts\game\hints\
s10_level_hints.levels = {}
load_lua_scripts('scripts\\game\\hints\\')

function s10_level_hints.Init()
	local level_id = tostring(quest.global_played_level.GetId())
	s10_level_hints.hints_table = s10_level_hints.levels[level_id]
	if not s10_level_hints.hints_table then 
		s10_level_hints._StandardInit()
		return
	end
	
	if level_hints._generate_hints then
		s10_level_hints.hints_table = nil
		quest.global_quest_window.EndModal(2)
		return
	end
	
	local last_globals, env = {}, getfenv(0)
	for name,func in pairs(s10_level_hints.funcs) do
		last_globals[name] = env[name]
		env[name] = func
	end
	s10_level_hints.hints_table = s10_level_hints.hints_table()
	for name,func in pairs(last_globals) do
		env[name] = last_globals[name]
	end
	return true
end

function s10_level_hints.ShowHint()
	if not s10_level_hints.hints_table then 
		s10_level_hints._StandardShowHint()
		return
	end

	local context = {}
	context.hint = true
	local obj = s10_level_hints._WalkHintTable(context)
	if obj then hint_to_object(obj) end
end

function s10_level_hints.GetPossibleTakeObjects()
	if not s10_level_hints.hints_table then 
		return s10_level_hints._StandardGetPossibleTakeObjects()
	end

	local context = {}
	context.take_objects = {}
	s10_level_hints._WalkHintTable(context)

	local result = {}
	for _,id in ipairs(context.take_objects) do
		local obj = quest.find_object_by_id(id, false, false)
		assert(obj)
		table.insert(result, obj)
	end
	return result
end

function s10_level_hints.GetCursorsTable()
	if not s10_level_hints.hints_table then 
		return s10_level_hints._StandardGetCursorsTable()
	end

	local context = {}
	context.action_objects = {}
	context.action_objects[qe.CLevelHints.ActionCanApply] = {}
	context.action_objects[qe.CLevelHints.ActionMinigame] = {}
	context.action_objects[qe.CLevelHints.ActionMiniscene] = {}
	context.action_objects[qe.CLevelHints.ActionCompleteLevel] = {}

	s10_level_hints._WalkHintTable(context)
	
	local cursors = {}
	
	local obj_in_cursor = quest.global_cursor.GetCursorObject()
	if obj_in_cursor then
		local obj_id = obj_in_cursor.GetIdRef().c_str()
		--__message(obj_id, "in cursor")
		local cursor = level_hints._cursors[qe.CLevelHints.ActionCanApply]
		cursors[cursor] = {}
		for _,pair in ipairs(context.action_objects[qe.CLevelHints.ActionCanApply]) do
			if pair[1] == obj_id then
				--__message("can click on "..pair[2])
				cursors[cursor][pair[2]] = true
			end
		end
	else
		local cursor = level_hints._cursors[qe.CLevelHints.ActionMinigame]
		cursors[cursor] = {}
		for _,pair in ipairs(context.action_objects[qe.CLevelHints.ActionMinigame]) do
			--__message("can click on "..pair[2])
			cursors[cursor][pair[1]] = true
		end
		
		local cursor = level_hints._cursors[qe.CLevelHints.ActionMiniscene]
		cursors[cursor] = cursors[cursor] or {}
		for _,pair in ipairs(context.action_objects[qe.CLevelHints.ActionMiniscene]) do
			--__message("can click on "..pair[2])
			cursors[cursor][pair[1]] = true
		end
		
		local cursor = level_hints._cursors[qe.CLevelHints.ActionCompleteLevel]
		cursors[cursor] = cursors[cursor] or {}
		for _,pair in ipairs(context.action_objects[qe.CLevelHints.ActionCompleteLevel]) do
			--__message("can click on "..pair[2])
			cursors[cursor][pair[1]] = true
		end
	end
	return cursors
end

function s10_level_hints._WalkHintTable(_context)
	for _,action in ipairs(s10_level_hints.hints_table) do
		local result = action:Apply(_context)
		if result then
			assert(type(result)=="string")
			return result
		end
	end
end

function s10_level_hints._IsObjectEnabled(_id)
	local obj = quest.find_object_by_id(_id, false, false)
	if not obj or sf.misc.BitwiseAnd(obj.GetObjectFlags(), __or(qe.CBaseSceneObject.ObjectFlagHidden, qe.CBaseSceneObject.ObjectFlagDisable))~=0 then
		--[[if obj then
			local hidden = __and(obj.GetObjectFlags(), qe.CBaseSceneObject.ObjectFlagHidden)
			local disabled = __and(obj.GetObjectFlags(), qe.CBaseSceneObject.ObjectFlagDisable)
			__message("object "..(hidden and "hidden" or "").." "..(disabled and "disabled" or ""))
		end]]
		return false
	end
	return true
end

function s10_level_hints._ApplyActions(_actions, _context)
	if _actions.Apply then
		return _actions:Apply(_context)
	else
		for _,a in ipairs(_actions) do
			local result = a:Apply(_context)
			if result then return result end
		end					
	end
end

-- заменяем стандартный вариант хинтов на s10
for _,func in ipairs({"Init", "ShowHint", "GetPossibleTakeObjects", "GetCursorsTable"}) do
	s10_level_hints["_Standard"..func] = level_hints[func]
	level_hints[func] = s10_level_hints[func]
end

-- функции для описания действий на уровне
s10_level_hints.funcs = {}

-- IF
function s10_level_hints.funcs.IF(_condition, _action)
	return { condition = _condition, action = _action, 
		Apply = function(self, _context) 
			local cond_result = false
			if self.condition.Apply then
				cond_result = self.condition:Apply(_context) 
			else
				cond_result = true
				for _,c in ipairs(self.condition) do
					if not c:Apply(_context) then 
						cond_result = false
						break
					end
				end
			end
				
			if cond_result then 
				assert(type(self.action)=="table")
				return s10_level_hints._ApplyActions(self.action, _context)
			end
		end
	}
end

-- scene
function s10_level_hints.funcs.scene(_id)
	return { scene = _id,
		Apply = function(self)
			local widget = quest.get_scene_widget(self.scene)
			return widget~=nil and sf.misc.BitwiseAnd(widget.GetFlags(), widget.FlagDead)==0
		end
	}
end

-- state
function s10_level_hints.funcs.state(_id, ...)
	return { object = _id, states = {(...)},
		Apply = function(self)
			for _,s in ipairs(self.states) do
				if state(self.object, s) then return true end
			end
		end 
	}
end

-- state_in_inv
function s10_level_hints.funcs.state_in_inv(_id, ...)
	return { object = _id, states = {(...)},
		Apply = function(self)
			for _,s in ipairs(self.states) do
				if is_object_in_inventory(_id, s) then return true end
			end
		end 
	}
end

-- on_level
function s10_level_hints.funcs.on_level(_id)
	return { object = _id,
		Apply = function(self)
			return is_object_on_level(self.object)
		end 
	}
end

-- in_inv
function s10_level_hints.funcs.in_inv(_id)
	return { object = _id,
		Apply = function(self)
			return is_object_in_inventory(self.object)
		end
	}
end

-- removed
function s10_level_hints.funcs.removed(_id)
	return { object = _id,
		Apply = function(self)
			local obj = quest.find_object_by_id(self.object, false, true)
			return obj and obj.ReadObjectFlag(obj.ObjectFlagRemoved) and not is_object_in_inventory(self.object)
		end
	}
end

function s10_level_hints.funcs.task_completed(_id)
	return { task = _id,
		Apply = function(self)
			return is_task_completed(self.task)
		end
	}
end

function s10_level_hints.funcs.NOT(_action)
	return { action = _action,
		Apply = function(self, _context)
			return not self.action:Apply(_context)
		end
	}
end

function s10_level_hints.funcs.miniscenes(_scenes)
	local scenes = {}
	for id,actions in pairs(_scenes) do
		scenes[id] = 
		{
			action = actions,
			
			Apply = function(self, _context)
				return s10_level_hints._ApplyActions(self.action, _context)
			end
		}
	end
	
	return { scenes = scenes, 
		Apply = function(self, _context) 		
			assert(type(self.scenes)=="table")			
			_context.miniscenes = self.scenes
			
			if _context.action_objects then
				for _,actions in pairs(self.scenes) do
					actions:Apply(_context)
				end
			end
		end
	}
end

function s10_level_hints.funcs.on_scene(_scene, _action)
	return { scene = _scene, action = _action,
		Apply = function(self, _context)
			local widget = quest.get_scene_widget(self.scene)
    
			local scene_open = widget~=nil and sf.misc.BitwiseAnd(widget.GetFlags(), widget.FlagDead)==0

            if widget~=nil then
                if widget.IsDead() then
					scene_open = false
				end
            end
			
			local result = nil
			if _context.hint or scene_open then 
				result = s10_level_hints._ApplyActions(self.action, _context)
			end
			
			if not scene_open and (not _context.hint or result) then
				assert(_context.miniscenes)
				return _context.miniscenes[self.scene] and _context.miniscenes[self.scene]:Apply(_context)
			end
			return result
		end
	}
end

-- take
function s10_level_hints.funcs.take(_id, _what)
	return { object = _id, what = _what,
		Apply = function(self, _context) 
			if not is_object_on_level(self.object) or not self.what and not s10_level_hints._IsObjectEnabled(self.object)
			or self.what and (not is_object_on_level(self.what) or not s10_level_hints._IsObjectEnabled(self.what)) then return end
			
			if _context.take_objects then table.insert(_context.take_objects, self.object) 
			elseif _context.hint then return self.object end
		end
	}
end

-- click, minigame, miniscene, end_level
local function click_base(_what, _with, _action_type)
	return { object = _what, with = _with, action_type = _action_type,
		Apply = function(self, _context)
		
			if not s10_level_hints._IsObjectEnabled(self.object)
			or self.with and not is_object_in_inventory(self.with) then return end
			
			if _context.action_objects then
				table.insert(
					_context.action_objects[self.action_type], 
					self.with and {self.with, self.object} or {self.object})
			elseif _context.hint then
				return self.object
			end
		end
	}
end

local object_actions = 
{ 
	click = qe.CLevelHints.ActionCanApply, 
	minigame = qe.CLevelHints.ActionMinigame, 
	miniscene = qe.CLevelHints.ActionMiniscene,
	end_level = qe.CLevelHints.ActionCompleteLevel
}

for func,atype in pairs(object_actions) do
	s10_level_hints.funcs[func] = function(_what, _with) return click_base(_what, _with, atype) end
end
--[[
-- click
function s10_level_hints.funcs.click(_what, _with)
	return { object = _what, with = _with,
		Apply = function(self, _context)
			if not s10_level_hints._IsObjectEnabled(self.object)
			or self.with and not is_object_in_inventory(self.with) then return end
			
			if _context then
				if _context.action_objects then
					--__message("click "..self.object.." with "..(self.with or "nothing"), "action")
					table.insert(_context.action_objects[qe.CLevelHints.ActionCanApply], {self.with, self.object})
				end
			else
				return self.object
			end
		end
	}
end

-- minigame
function s10_level_hints.funcs.minigame(_what, _with)
	return { object = _what, with = _with,
		Apply = function(self, _context)
			if _context then
				if _context.action_objects then
					table.insert(_context.action_objects[qe.CLevelHints.ActionMinigame], {self.object})
				end
			else
				return self.object
			end
		end
	}
end

-- miniscene
function s10_level_hints.funcs.miniscene(_what, _with)
	return { object = _what, with = _with,
		Apply = function(self, _context)
			if _context then
				if _context.action_objects then
					table.insert(_context.action_objects[qe.CLevelHints.ActionMiniscene], {self.object})
				end
			else
				return self.object
			end
		end
	}
end

-- click
function s10_level_hints.funcs.end_level(_what, _with)
	return { object = _what, with = _with,
		Apply = function(self, _context)
			if _context then
				if _context.action_objects then
					table.insert(_context.action_objects[qe.CLevelHints.ActionCompleteLevel], {self.object})
				end
			else
				return self.object
			end
		end
	}
end]]
