local curdir = std.getinfo(1).source:gsub("^(.+[\\/])[^\\/]+$", "%1"):gsub("^@", "");
local table = std.table
require "fmt"
require "snapshots"

local tostring = std.tostr

--luacheck: no self
if std.ref'@theme' then
	std.ref'@theme'.set ('win.scroll.mode', 3)
end

local mrd = require "morph/mrd"

local input = std.ref '@input'

local function utf_bb(b, pos)
	if type(b) ~= 'string' or b:len() == 0 then
		return 0
	end
	local utf8 = (std.game.codepage == 'UTF-8' or std.game.codepage == 'utf-8')
	if not utf8 then return 1 end
	local i = pos or b:len()
	local l = 0
	while b:byte(i) >= 0x80 and b:byte(i) <= 0xbf do
		i = i - 1
		l = l + 1
		if i <= 1 then
			break
		end
	end
	return l + 1
end

local function utf_ff(b, pos)
	if type(b) ~= 'string' or b:len() == 0 then
		return 0
	end
	local utf8 = (std.game.codepage == 'UTF-8' or std.game.codepage == 'utf-8')
	if not utf8 then return 1 end
	local i = pos or 1
	local l = 0
	if b:byte(i) < 0x80 then
		return 1
	end
	i = i + 1
	l = l + 1
	while b:byte(i) and b:byte(i) >= 0x80 and b:byte(i) <= 0xbf do
		i = i + 1
		l = l + 1
		if i > b:len() then
			break
		end
	end
	return l
end

local function utf_len(b)
	local i = 1
	local n = 0
	if not b or b:len() == 0 then
		return 0
	end
	while i <= b:len() do
		i = i + utf_ff(b, i)
		n = n + 1
	end
	return n
end

local function utf_char(b, c)
	local i = 1
	local n = 0
	local s
	while i <= b:len() do
		s = i
		i = i + utf_ff(b, i)
		n = n + 1
		if n == c then
			return b:sub(s, i - 1)
		end
	end
	return
end

local function utf_chars(b)
	local i = 1
	local s
	local res = {}
	local ff = std.rawget(_G, 'utf8_next') or utf_ff
	while i <= b:len() do
		s = i
		i = i + ff(b, i)
		table.insert(res,  b:sub(s, i - 1))
	end
	return res
end

local function utf_similar(str1, str2, lev)
	local chars1 = utf_chars(str1)
	local chars2 = utf_chars(str2)
	local len1 = #chars1
	local len2 = #chars2
	if len1 < lev or len2 < lev then
		return false
	end

	for i = 0, len2 - lev do
		local ok = true
		for k = 1, lev do
			if chars1[k] ~= chars2[i + k] then
				ok = false
				break
			end
		end
		if ok then return true end
	end

	for i = 0, len1 - lev do
		local ok = true
		for k = 1, lev do
			if chars2[k] ~= chars1[i + k] then
				ok = false
				break
			end
		end
		if ok then return true end
	end
	return false
end
--- Returns the Levenshtein distance between the two given strings.
-- https://gist.github.com/Badgerati/3261142

-- @param str1 string1
-- @param str2 string2
local function utf_lev(str1, str2)
	str1 = str1 or ''
	str2 = str2 or ''
	local chars1 = utf_chars(str1)
	local chars2 = utf_chars(str2)
	local len1 = #chars1
	local len2 = #chars2
	local matrix = {}
	local cost

        -- quick cut-offs to save time
	if (len1 == 0) then
		return len2
	elseif (len2 == 0) then
		return len1
	elseif (str1 == str2) then
		return 0
	end

        -- initialise the base matrix values
	for i = 0, len1, 1 do
		matrix[i] = {}
		matrix[i][0] = i
	end
	for j = 0, len2, 1 do
		matrix[0][j] = j
	end

        -- actual Levenshtein algorithm
	for i = 1, len1, 1 do
		for j = 1, len2, 1 do
			if (chars1[i] == chars2[j]) then
				cost = 0
			else
				cost = 1
			end

			matrix[i][j] = math.min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, matrix[i-1][j-1] + cost)
		end
	end

        -- return the last value - this is the Levenshtein distance
	return matrix[len1][len2]
end
local okey = input.key
local mp

local function use_text_event()
	return instead.text_input and instead.text_input()
end

if PLATFORM == "ANDROID" or PLATFORM == "IOS" or PLATFORM == "SFOS" then
local oclick = input.click

function input:click(press, mb, x, y, ...)
	local theme = std.ref'@theme'

	if not instead.text_input or not press or mb ~= 1 or not theme or mp.autohelp then
		if oclick then
			return oclick(self, press, mb, x, y, ...)
		end
		return false
	end

	local xx = std.tonum(theme.get 'inv.x')
	local yy = std.tonum(theme.get 'inv.y')
	local ww = std.tonum(theme.get 'inv.w')
	local hh = std.tonum(theme.get 'inv.h')
	if x >= xx and y >= yy and x < xx + ww and y < yy + hh then
		instead.text_input(not instead.text_input())
	end
	if oclick then
		return oclick(self, press, mb, x, y, ...)
	end
	return false
end
end

function input:text(sym)
	if sym == " " then -- in old key logic
		return false
	end
	if iface:raw_mode() then
		return false
	end
	mp:inp_insert(sym)
	mp:post_inp()
	return '@mp_key '..tostring(sym)
end

function input:key(press, key)
	local mod
	if mp:noparser() then
		return false
	end
	if key:find("alt") then
		mp.alt = press
		mod = true
	elseif key:find("ctrl") then
		mp.ctrl = press
		mod = true
	elseif key:find("shift") then
		mp.shift = press
		mod = true
	end
	if key:find("enter") or key:find("return") then key = 'enter' end

	if press and not mod and not (mp.ctrl or mp.alt) then
		if mp:key(key) then
			mp:post_inp()
			if key == 'f6' and mp.autoplay then key = 'enter' end
			return '@mp_key '..tostring(key)
		end
	end
	if okey then
		return okey(self, press, key)
	end
end

mp = std.obj {
	nam = '@metaparser';
	started = false;
	score = false;
	maxscore = false;
	expert_mode = true;
	strict_mode = false;
	autohelp = false;
	autohelp_limit = 1000;
	autohelp_noverbs = false;
	togglehelp = true;
	errhints = true;
	autocompl = true;
	undo = 0;
	compl_thresh = 0;
	compare_len = 3;
	detailed_inv = false;
	last_gfx = false;
	inp_delim = " :.,!?-";
	daemons = std.list {};
	{
		version = "2.4";
		cache = { tokens = {}, nouns = false };
		scope = std.list {};
		logfile = false;
		lognum = 0;
		clear_on_move = true;
		auto_animate = true;
		autoplay = false;
		pushed = {};
		autoplay_command = false;
		inp = '';
		cur = 1;
		utf = {
			bb = std.rawget(_G, 'utf8_prev') or utf_bb;
			ff = std.rawget(_G, 'utf8_next') or utf_ff;
			len = std.rawget(_G, 'utf8_len') or utf_len;
			char = std.rawget(_G, 'utf8_char') or utf_char;
			chars = utf_chars;
		};
		lev_thresh = 3;
		lev_ratio = 0.20;
		history = {};
		persistent = std.list {};
		winsize = 16 * 1024;
		history_len = 100;
		history_pos = 0;
		cursor = fmt.b("|");
		prompt = "> ";
		ctrl = false;
		shift = false;
		alt = false;
		words = {};
		parsed = {};
		hints = {};
		unknown = {};
		multi = {};
		token = {};
		shortcut = {};
		snapshot = false;
		reaction = false;
		redirect = false;
		msg = {};
		mrd = mrd;
		args = {};
		vargs = {};
		debug = { trace_action = false };
		completions = {};
		event = false;
		xevent = false;
		aliases = {};
		first = false;
		first_it = false;
		second_it = false;
		second = false;
		first_hint = '';
		second_hint = '';
		hint = {
			live = 'live',
			neuter = 'neuter',
			male = 'male',
			female = 'female',
			plural = 'plural',
		};
		shorten = {};
		shorten_expert = {};
		_pager_mode = false;
	};
	text = '';
	-- dict = {};
}

function mp:post_inp()
	if mp.autohelp then
		mp:compl_fill(mp:compl(mp.inp))
	elseif mp.autocompl then
		mp:compl(mp.inp)
	end
end

function mp:onedit()
end

function mp:trim()
	if self.winsize == 0 then
		self.text = ""
		return
	end
	local text = self.text
	while text:len() > self.winsize do
		local text2 = text:gsub("^%^?[^%^]*%^%^", "")
		if text2 == text then
			break
		end
		text = text2
	end
	self.text = text
end

function mp:key(key)
	if key == 'f1' and (self.togglehelp or DEBUG) then
		self.autohelp = not self.autohelp
		return true
	end
	if key == 'f6' and DEBUG then
		self:autoscript()
		return true
	end
	if key == 'home' or key == '[7]' then
		self.cur = 1
		return true
	end
	if key == 'end' or key == '[1]' then
		self.cur = self.inp:len() + 1
		return true
	end
	if key == 'left' then
		return self:inp_left()
	end
	if key == 'right' then
		return self:inp_right()
	end
	if key == 'up' then
		return self:key_history_prev()
	end
	if key == 'down' then
		return self:key_history_next()
	end
	if key == 'space' then
--		local inp = mp:docompl(self.inp)
--		if inp == self.inp then
			mp:inp_insert(' ')
--		else
--			self.inp = inp
--			self.cur = self.inp:len() + 1
--		end
		return true
	end
	if key == 'tab' then
		self.inp = mp:docompl(self.inp)
		self.cur = self.inp:len() + 1
		return true
	end
	if key == 'backspace' then
		if self:inp_remove() then
			return true
		end
		return true -- avoid scrolling
	end
	if key == 'enter' then
		return true
	end
	if use_text_event() then
		return false
	end
	if key:len() > 1 then
		return false
	end
	key = mp.shift and mrd.lang.kbd.shifted[key] or mrd.lang.kbd[key] or key
	if key then
		mp:inp_insert(key)
		return true
	end
	return false
end

function mp:inp_left()
	if self.cur > 1 then
		local i = mp.utf.bb(self.inp, self.cur - 1)
		self.cur = self.cur - i
		return true
	end
end

function mp:inp_right()
	if self.cur <= self.inp:len() then
		local i = mp.utf.ff(self.inp, self.cur)
		self.cur = self.cur + i
		return true
	end
end

function mp:inp_split()
	local pre = self.inp:sub(1, self.cur - 1);
	local post = self.inp:sub(self.cur);
	return pre, post
end

function mp:inp_insert(k)
	local pre, post = self:inp_split()
	self.cur = self.cur + k:len()
	self.inp = pre .. k .. post
end

function mp:inp_remove()
	local pre, post = self:inp_split()
	if not pre or pre == '' then
		return false
	end
	local i = mp.utf.bb(pre)
	self.inp = self.inp:sub(1, pre:len() - i) .. post
	self.cur = self.cur - i
	return true
end

function mp:esc(s)
	local rep = function(str)
		return fmt.nb(str)
	end
	if not s then return end
	local r = s:gsub("[<>]+", rep):gsub("[ \t]", rep);
	return r
end

local keys_en = {
	"A", "B", "C", "D", "E", "F",
	"G", "H", "I", "J", "K", "L",
	"M", "N", "O", "P", "Q", "R",
	"S", "T", "U", "V", "W", "X",
	"Y", "Z"
}

local function str_strip(str)
	return std.strip(str)
end

local function str_split(str, delim)
	local a = std.split(str, delim)
	for k, _ in ipairs(a) do
		a[k] = str_strip(a[k])
	end
	return a
end

--- Returns true if parser mode is disabled
function mp:noparser()
	return std.here().noparser or game.noparser
end

instead.get_inv = std.cacheable('inv', function(horiz)
	if mp:noparser() then
		return
	end
	local delim = instead.hinv_delim
	if not horiz then
		delim = instead.inv_delim
	end
	local pre, post = mp:inp_split()
	local ret = iface:bold(mp.prompt) .. mp:esc(pre)..mp.cursor..mp:esc(post) .. '\n'
	if mp.autohelp then
		ret = iface:xref(ret, mp, "<clear>")
	end
	if not mp.autohelp and not std.here().forcehelp then
		local r = std.call(std.here(), 'help')
		return ret .. (r or '')
	end

	if mp.autohelp_noverbs and mp.inp:find("^[ \t]*$") then
		return ret
	end

	delim = delim or ' | '

	for _, v in ipairs(mp.completions) do
		local t = iface:xref(std.fmt(v.word), mp, v.word)
		if v.ob and have(v.ob) then t = iface:em(t) end
		if _ >= mp.autohelp_limit then
			ret = ret .. t .. ' ...' .. delim
			break
		end
		ret = ret .. t .. delim
	end
	if #mp.completions == 0 or mp.completions.eol then
		ret = ret .. iface:xref(mp.msg.enter or "<enter>", mp, "<enter>") .. delim
		if mp.completions.vargs then
			ret = ret .. iface:xref(mp.keyboard_space or "<space>", mp, "<space>") .. delim
			ret = ret .. iface:xref(mp.keyboard_backspace or "<backspace>", mp, "<backspace>") .. delim
			for _, v in ipairs(mp.keyboard or keys_en) do
				ret = ret .. iface:xref(v, mp, v, 'letter') .. delim
			end
		end
	end

	ret = ret:gsub(delim .."$", "")
	return ret
end)

--- Returns (add to) table with scope objects
-- @param wh where to start scope
-- @param oo table
-- @param recurs recursive flag
-- @param scope scope list (optional)

function mp:objects(wh, oo, recurs, scope)
	wh:for_each(function(v)
		if v:disabled() then return nil, false end
		if v:visible() or (scope and scope:lookup(v)) then
			table.insert(oo, v)
			if scope and v.scope then
				if std.is_obj(v.scope, 'list') then
					scope:cat(v.scope)
				elseif type(v.scope) == 'function' then
					v:scope(scope)
				end
			end
		end
		if recurs == false or v:closed() then
			if std.is_obj(wh, 'list') then
				return -- just skip
			end
			return nil, false
		end
		if std.is_obj(wh, 'list') then
			self:objects(v, oo, recurs, scope)
		end
	end)
end

local darkness = std.obj {
	nam = '@darkness';
}

function mp:nouns()
	local scope = self.scope
	scope:zap()
	if type(std.here().nouns) == 'function' then
		return std.here():nouns()
	end
	local oo = {}
	self:objects(std.me():inroom(), oo, true, scope)
	self:objects(std.list { std.me() }, oo, true, scope)
	self:objects(self.persistent, oo, true, scope)
	local ob = std.list {}
	if std.here().word then
		ob:add(std.here())
	end
	if self:thedark() then
		ob:add(darkness)
	end
	self:objects(ob, oo, false, scope)
	local dups = {}
	scope:for_each(function(v)
		if not dups[v] then
			table.insert(oo, v)
			dups[v] = true
		end
	end)
	return oo
end

function mp.token.noun_obj(w)
	return mp.token.noun(w)
end

function mp.token.noun(w)
	local attr = w.morph or ''
	local oo
	local ww = {}
	if w.pat == '{noun_obj}' then
		local hint = str_split(w.morph, ",")
		local o = std.ref(hint[1])
		oo = {}
		if o:visible() and not o:disabled() then
			table.insert(oo, o)
			mp:objects(o, oo)
		end
	else
		oo = mp.cache.nouns or {}
	end

	local sm_dup = {
	}
	local syms = {}
	for _, o in ipairs(oo) do
		local d = {}
		local r = o:noun(attr, d)
		if o == std.me() and mp.myself then
			for _, vm in ipairs(mp:myself(o, w.morph) or {}) do
				table.insert(ww, { optional = w.optional, word = vm, morph = attr, ob = o, alias = o.alias,
					hidden = w.hidden or _ ~= 1 })
			end
		end
		if o ~= std.me() or (not o:hint'first' and not o:hint'second') then
			for k, v in ipairs(d) do
				local hidden = (k ~= 1) or w.hidden
				if o:has 'multi' then
					hidden = w.hidden or (v.idx ~= 1)
				end
				table.insert(ww,
					{ optional = w.optional,
						word = r[k], ob = o,
						morph = attr, alias = v.alias,
						hidden = hidden })
			end
			if o == mp.first_it then
				table.insert(syms, 1, o)
			elseif o == mp.second_it then
				table.insert(syms, o)
			end
		end
	end

--	for k = 1, #syms do
--		table.insert(oo, k, syms[k])
--	end

	oo = syms

	for _, o in ipairs(oo) do
		for _, v in ipairs(mp:synonyms(o, w.morph)) do
			if not sm_dup[v] then
				table.insert(ww, { optional = w.optional, word = v, ob = o, morph = attr,
					alias = o.alias, hidden = true, synonym = true })
				sm_dup[v] = true
			end
		end
	end

	return ww
end

function mp.token.select(w)
	return mp.token.noun(w)
end

local norm_cache = { hash = {}, list = {}}

function mp:norm(t)
	local key = t
	local cc = norm_cache.hash[t]
	if cc then return cc end
	t = mrd.lang.lower(mrd.lang.norm(t)):gsub("[ \t]+", " ")
	table.insert(norm_cache.list, 1, t)
	norm_cache.hash[key] = t
	local len = #norm_cache.list
	if len > 512 then
		cc = norm_cache.list[len]
		table.remove(norm_cache.list, len)
		norm_cache.hash[cc] = nil
	end
	return t
end

--- Check if two strings are equal, using two possible check modes.
-- If lev is set, use it as Levenstein equality threshold.
-- If not, normalize the strings and check equality.
--
-- @see word_search
-- @see mp:lookup_noun
-- @param t1 first string
-- @param t2 second string
-- @param lev use levenstein or just normalize and compare
function mp:eq(t1, t2, lev)
	if t1:find("%*$") then
		local t = t1:gsub("%*$", "")
		t = self:norm(t)
		return self:__startswith(t2, t)
	end
	if lev then
		t1 = self:norm(t1)
		t2 = self:norm(t2)
		if not utf_similar(t1, t2, 3) then -- 3 is hardcoded
			return false
		end
		local l = utf_lev(t1, t2)
		if l < lev and l / (utf_len(t1) + utf_len(t2)) <= self.lev_ratio then
			return l
		end
		return false
	end
	return self:norm(t1) == self:norm(t2)
end

local function starteq(t1, t2)
	if t2:len() >= t1:len() or mp.utf.len(t2) < mp.compare_len then
		return mp:eq(t1, t2)
	end
	t1 = t1:sub(1, t2:len())
	return mp:norm(t1) == mp:norm(t2)
end

function mp:pattern(t, delim)
	local words = {}
	local pat = str_split(t, delim or "|")
	for _, v in ipairs(pat) do
		local w = { }
		local ov = v
		if v:sub(1, 1) == '~' then
			v = v:sub(2)
			v = str_strip(v)
			w.hidden = true
		end
		if v:sub(1, 1) == '+' then
			v = v:sub(2)
			v = str_strip(v)
			w.optional = true
			w.default = true
		end
		if v:sub(1, 1) == '?' then
			v = v:sub(2)
			v = str_strip(v)
			w.optional = true
		end
		v = v:gsub("%+", " ") -- spaces
		if v:find("[^/]+/[^/]*$") then
			local s, e = v:find("/[^/]*$")
			w.morph = v:sub(s + 1, e)
			v = v:sub(1, s - 1)
			v = str_strip(v)
		end
		w.pat = v
		if v:find("^{[^}]+}$") then -- completion function
			v = v:gsub("^{", ""):gsub("}$", "")
			if type(self.token[v]) ~= 'function' then
				std.err("Wrong subst function: ".. v, 2);
			end
			local key = ov --  .. '/' .. (w.morph or '')
			local tok = self.cache.tokens[key]
			if not tok then
				tok = self.token[v](w)
				self.cache.tokens[key] = tok
			end
			while type(tok) == 'string' do
				tok = self:pattern(tok)
			end
			if type(tok) == 'table' then
				for _, xw in ipairs(tok) do
					table.insert(words, xw)
				end
			end
		else
			local vv = {}
			for _, w in ipairs(mp:pref_pattern(v)) do
				for _, w in ipairs(mp:suff_pattern(w)) do
					table.insert(vv, w)
				end
			end
			if #vv == 1 then
				w.word = v
				table.insert(words, w)
			else
				for _, exv in ipairs(vv) do
					local ww = std.clone(w)
					ww.word = exv
					table.insert(words, ww)
				end
			end
		end
	end
	return words
end

function mp:verb_find(tag, w)
	w = w or game
	for k, v in ipairs(w.__Verbs or {}) do
		if v.tag == tag then
			return v, k
		end
	end
end

function mp:verb_remove(tag, w)
	local v, k = self:verb_find(tag, w)
	if v then
		table.remove((w or game).__Verbs, k)
	end
	return v
end

function mp:pref_pattern(v)
	if not v:find("^%[[^%]]+%]") then
		return { v }
	end
	local _, e = v:find("]", 1, true)
	local pre = v:sub(2, e - 1)
	local post = v:sub(e + 1)
	pre = pre:gsub("^|", " |"):gsub("|$", "| "):gsub("||", "| |");
	pre = str_split(pre, "|")
	local ret = {}
	for _, pref in ipairs(pre) do
		table.insert(ret, pref .. post)
	end
	return ret
end

function mp:suff_pattern(v)
	if not v:find("%[[^%]]+%]$") then
		return { v }
	end
	local s, _ = v:find("[", 1, true)
	local e, _ = v:find("]", 1, true)
	local suff = v:sub(s + 1, e - 1)
	local pref = v:sub(1, s - 1)
	suff = suff:gsub("^|", " |"):gsub("|$", "| "):gsub("||", "| |");
	suff = str_split(suff, "|")
	local ret = {}
	for _, suff in ipairs(suff) do
		table.insert(ret, pref .. suff)
	end
	return ret
end

function mp:verb(t, w, extend, extend_words)
	local rem
	w = w or game
	if type(t) ~= 'table' then
		std.err("Wrong 1-arg to mp:verb()", 2)
	end
	if type(w) ~= 'table' then
		std.err("Wrong 2-arg to mp:verb()", 2)
	end
	if not w.__Verbs then
		std.rawset(w, '__Verbs', {})
	end
	local verb = {}
	local n = 1
	if std.is_tag(t[1]) then
		verb.tag = t[1]
		rem = self:verb_remove(verb.tag, w)
		n = 2
	end
	if extend and (not rem or not verb.tag) then
		std.err("Extending non existing verb "..verb.tag or '#Undefined', 2)
	end
	if extend then
		verb.verb = rem.verb
		verb.dsc = rem.dsc
	end
	if not extend or extend_words then
		if type(t[n]) ~= 'string' then
			std.err("Wrong verb pattern in mp:verb()", 2)
		end
		local verbs = self:pattern(mp.fmt(t[n]), ",")
		if extend then
			for _, v in ipairs(verbs) do
				table.insert(verb.verb, v)
			end
		else
			verb.verb = verbs
		end
		n = n + 1
		if not extend then
			verb.dsc = {}
		end
	end
	if type(t[n]) ~= 'string' then
		if t[n] or not extend_words then
			std.err("Wrong verb descriptor mp:verb()", 2)
		end
	end
	while type(t[n]) == 'string' do
		local dsc = str_split(t[n], ":")
		local pat
		if #dsc == 1 then
			table.insert(verb.dsc, { pat = {}, ev = dsc[1] })
		elseif #dsc == 2 then
			pat = str_split(dsc[1], ' ')
			if pat[1] == '~' then
				table.remove(pat, 1)
				for k, v in ipairs(pat) do
					pat[k] = v:gsub("[^ |]+", function(s) return "~" .. s end)
				end
			end
			table.insert(verb.dsc, { pat = pat, ev = dsc[2] })
		else
			std.err("Wrong verb descriptor: " .. t[n])
		end
		n = n + 1
	end
	verb.hint = t.hint
	table.insert(w.__Verbs, 1, verb)
	return verb
end

function mp:verbs()
	local w = std.here().__Verbs or std.me().__Verbs or game.__Verbs or {}
	local t = {}
	for _, v in ipairs(w) do
		table.insert(t, v)
	end
	for _, v in ipairs(mp.__Verbs or {}) do
		table.insert(t, v)
	end
	return t
end

local function word_search(t, w, lev)
	local rlev
	w = str_split(w, mp.inp_delim)
	for k = 1, #t - #w + 1 do
		local found = true
		for i = 1, #w do
			local found2 = false
			for ii = k, k + #w - 1 do
				if type(lev) == 'function' then
					rlev = lev(w[i], t[ii])
				else
					rlev = mp:eq(w[i], t[ii], lev)
				end
				if rlev then
					found2 = true
					break
				end
			end
			if not found2 then
				found = false
				break
			end
		end
		if found then
			return k, #w, rlev
		end
	end
end

function mp:lookup_short(words, w)
	local ret = { }
	for _,v in ipairs(words) do
		if self:__startswith(v, w) then
			table.insert(ret, { i = _, w = v, pos = #ret })
		end
	end
	table.sort(ret, function(a, b)
		if a.w:len() == b.w:len() then return a.pos < b.pos end
		return a.w:len() < b.w:len()
	end)
	if #ret == 0 then return end
	return ret[1].i, 1
end

--- Return false if unparsed word is critical in parse sense
-- @param w table with skipped words
function mp:skip_filter()
	return true
end

function mp:ignore_filter(w)
	return false
end

function mp:verb_filter(words)
	return true
end

function mp:lookup_verb(words, lev)
	local ret = {}
	local w = self:verbs()
	for _, v in ipairs(w) do -- verbs
		local lev_v = {}
		for _, vv in ipairs(v.verb) do
			local verb = vv.word .. (vv.morph or "")
			local i, len, rlev
			local vwords = mp.strict_mode and { words[1] } or words
			i, len, rlev = word_search(vwords, verb, lev and self.lev_thresh)
			if not i and not lev and vv.morph then
				i, len = self:lookup_short(vwords, vv.word)
				if i then
					local v = {}
					for k = i, i + len - 1 do
						table.insert(v, words[k])
					end
					if verb:find(table.concat(v, ' '), 1, true) ~= 1 and
						not self:verb_filter(v) then
						i = false
					end
				end
			end
			if i and i > 1 and not self:skip_filter({words[i - 1]}) then
				i = nil
			end
			if i then
				if lev then
					table.insert(lev_v, { lev = rlev, verb = v, verb_nr = i, verb_len = len, word_nr = _ } )
				else
					local vc = std.clone(v)
					vc.verb_nr = i
					vc.verb_len = len
					vc.word_nr = _
					table.insert(ret, vc)
				end
			end
		end
		if lev and #lev_v > 0 then
			table.sort(lev_v, function(a, b)
					   if a.lev == b.lev then return a.word_nr < b.word_nr end
					   return a.lev < b.lev
			end)
			lev_v[1].verb.verb_nr = lev_v[1].verb_nr
			lev_v[1].verb.verb_len = lev_v[1].verb_len
			lev_v[1].verb.word_nr = lev_v[1].word_nr
			lev_v[1].verb.lev = lev_v[1].lev
			table.insert(ret, lev_v[1].verb)
		end
	end
	if lev then
		table.sort(ret, function(a, b)
				   if a.lev == b.lev then return a.word_nr < b.word_nr end
				   return a.lev < b.lev
		end)
	elseif #ret > 0 then
		table.sort(ret, function(a, b)
			if a.verb_nr == b.verb_nr then return a.word_nr < b.word_nr end
			return a.verb_nr < b.verb_nr
		end)
		local vlev = ret[1].verb_nr
		local ret2 = {}
		for _, v in ipairs(ret) do
			if v.verb_nr == vlev then
				table.insert(ret2, v)
			else
				break
			end
		end
		ret = ret2
	end
	return ret
end

local function tab_sub(t, s, e)
	local r = {}
	e = e or #t
	for i = s, e do
		table.insert(r, t[i])
	end
	return r
end

--[[
local function tab_exclude(t, s, e)
	local r = {}
	e = e or #t
	for i = 1, #t do
		if i < s or i > e then
			table.insert(r, t[i])
		end
	end
	return r
end
]]--

function mp:docompl(str, maxw)
	local full
	local force = maxw
	local inp, pre = self:compl_ctx()
	if mp.utf.len(pre) < self.compl_thresh then
		return str
	end
	if not maxw then
		full = false
		local compl = self:compl(str)
		for _, v in ipairs(compl) do
			if not maxw then
				full = true
				maxw = v.word
			else
				local maxw2 = ''
				local utf_word = utf_chars(v.word)
				local utf_maxw = utf_chars(maxw)
				for k = 1, #utf_maxw do
					if mrd.lang.norm(utf_maxw[k]) == mrd.lang.norm(utf_word[k]) then
						maxw2 = maxw2 .. utf_maxw[k]
					else
						full = false
						break
					end
				end
				maxw = maxw2
			end
		end
		if full then
			if #compl > 1 then full = false end
		end
	else
		full = true
	end
	if maxw and maxw ~= '' then
		str = inp .. maxw
	end
	if force or full then
		str = str .. ' '
	end
	return str
end

function mp:__startswith(w, v)
	return w:find(v, 1, true) == 1
end

function mp:startswith(w, v)
	return (self:norm(w)):find(self:norm(v), 1, true) == 1
end

function mp:hint_verbs(v)
	if not v.tag then return true end
	if type(v.hint) == 'function' then
		return v:hint()
	end
	local r = true

	if std.here().hint_verbs_only then
		r = false
		for _, vv in ipairs(std.here().hint_verbs_only) do
			if v.tag == vv then
				r = true
				break
			end
		end
		return r
	end

	if game.hint_verbs then
		r = false
		for _, vv in ipairs(game.hint_verbs) do
			if v.tag == vv then
				r = true
				break
			end
		end
	end
	if r then return r end
	if std.here().hint_verbs then
		for _, vv in ipairs(std.here().hint_verbs) do
			if v.tag == vv then
				r = true
				break
			end
		end
	end
	return r
end
function mp:compl_verb(_)
	local poss = {}
	for _, v in ipairs(self:verbs()) do
		local filter = not self:hint_verbs(v)
		for _, vv in ipairs(v.verb) do
			local verb = vv.word .. (vv.morph or "")
			table.insert(poss, { word = verb, hidden = (_ ~= 1) or vv.hidden or filter})
		end
	end
	return poss
end

function mp:animate(w)
	if w:has'animate' == false then
		return false
	end
	return w:has'animate' or (self.auto_animate and w:hint'live')
end

local function holded_by(ob, holder)
	if not holder then
		return true
	end
	local h = holder.ob
	if not h then return false end
	if ob:where() == h then return true end
	for _, hm in ipairs(holder.multi or {}) do
		if ob:where() == hm then
			return true
		end
	end
	return false
end

local function multi_held(ob, attrs)
	if not attrs.held and not attrs.scene then
		return true
	end
	if ((attrs.held and have(ob)) or
		(attrs.scene and not have(ob))) then
		return true
	end
	return false
end

local function multi_select(vv, attrs, holder)
	local res = {}
	local ob = vv.ob
	if holded_by(ob, holder) and multi_held(ob, attrs) then
		table.insert(res, ob)
	end
	for _, h in ipairs(vv.multi or {}) do
		if holded_by(h, holder) then
			ob = h
			if multi_held(ob, attrs) then
				table.insert(res, ob)
			end
		end
	end
	local dist = -1;
	ob = res[1]
	for _, v in ipairs(res) do
		local d = mp:distance(v)
		if d < dist or dist == -1 then
			ob = v
			dist = d
		end
	end
	return ob or vv.ob, res
end

function mp:compl_filter(v)
	local hidden = v.hidden
	if not hidden and v.ob and v.ob.hint_noun ~= nil then
		if type(v.ob.hint_noun) == 'function' then
			hidden = not v.ob:hint_noun(v)
		else
			hidden = not v.ob.hint_noun
		end
	end
	local _, pre = self:compl_ctx()
	local nsym = mp.utf.len(pre)
	if hidden and self.compl_thresh == 0 and nsym == 0 then
		return false
	end
	if nsym < self.compl_thresh then
		return false
	end
	if not v.ob or not v.morph then
		return true
	end
	local attrs = {
		held = false;
		scene = false;
		container = false;
		inside = false;
		enterable = false;
		edible = false;
		supporter = false;
		live = false,
		compass = false,
	}
	for _, h in ipairs(str_split(v.morph, ",")) do
		if attrs[h] ~= nil then attrs[h] = h end
	end
	for _, a in ipairs { 'container', 'enterable', 'supporter', 'edible' } do
		if attrs[a] and not v.ob:has(a) then return false end
	end
	if v.ob and mp:compass_dir(v.ob) and not attrs.compass then
		return false
	end
	if attrs.live and not self:animate(v.ob) then return false end
	if attrs.inside and not v.ob:has'container' and not v.ob:has'supporter' then return false end
	if not attrs.held and not attrs.scene then return true end
	if attrs.held and have(multi_select(v, attrs)) then return true end
	if attrs.scene and (not have(multi_select(v, attrs)) and v.ob ~= std.me()) then return true end
	return false
end

function mp:compl_fill(compl, eol, vargs)
	local ctx = self.completions.ctx
	self.completions = {}
	self.completions.ctx = ctx
	self.completions.eol = eol
	self.completions.vargs = vargs
	for _, v in ipairs(compl) do
		if self:compl_filter(v) then
			table.insert(self.completions, v)
		end
	end
end

function mp:compl_reset()
	self.completions = { ctx = {} }
end

function mp:compl_ctx_current()
	local ctx = self.completions.ctx
	local new = {}
	for _, v in ipairs(ctx) do
		if v.inp == '' and self.inp == '' then break end
		if self:startswith(self.inp, v.inp) then
			table.insert(new, v)
		else
			break
		end
	end
	self.completions.ctx = new
end

function mp:compl_ctx_push(poss)
	if #poss == 0 then
		return
	end
	local ctx = self.completions.ctx
	table.insert(ctx, poss)
	local top = #ctx
	ctx[top].inp = self.inp
end

function mp:compl_ctx()
	local ctx = self.completions.ctx
	local top = #ctx
	if top == 0 then
		return self.inp, ''
	end
	local inp = self.inp
	local ctx_inp = ctx[top].inp
	local _, e = inp:find(ctx_inp, 1, true)
	local pre = ''
	if e then
		pre = inp:sub(e + 1)
	end
	return ctx[top].inp, pre
end

function mp:compl_ctx_poss()
	local ctx = self.completions.ctx
	local top = #ctx
	local res = {}
	if top == 0 then
		return res
	end
	ctx = ctx[top]
	local _, pre = self:compl_ctx()
	for _, v in ipairs(ctx) do
		if self:__startswith(v.word, pre) or
			self:__startswith(self:norm(v.word), pre) then
			table.insert(res, v)
		end
	end
	res.eol = ctx.eol
	return res
end

function mp:compl(str)
	local words = str_split(self:norm(str), mp.inp_delim)
	local poss
	local ret = {}
	local dups = {}
	local eol
	local e = str:find(" $")
	local vargs
	collectgarbage("stop")
	self:compl_ctx_current();
	poss = self:compl_ctx_poss()
	eol = poss.eol
	if (#poss == 0 and e) or #words == 0 then -- no context
		if #words == 0 or (#words == 1 and not e) then -- verb?
			poss, eol = self:compl_verb(words)
			local oo = self:nouns() -- and hidden nouns
			for _, o in ipairs(oo) do
				local ww = {}
				o:noun(ww)
				for _, n in ipairs(ww) do
					local hidden = true
					if o.raw_word then hidden = n.alias ~= 1 and not o:has'multi' end
					table.insert(poss, { word = n.word, hidden = hidden })
				end
			end
		else -- matches
			self.cache.nouns = self:nouns()
			poss, eol = self:compl_match(words)
		end
		poss.eol = eol
		self:compl_ctx_push(poss)
	end
	local _, pre = self:compl_ctx()
	for _, v in ipairs(poss) do
		if v.word == '*' and not v.hidden then
			vargs = true
		end
		if self:startswith(v.word, pre) and not v.word:find("%*$") then
			if not dups[v.word] then
				dups[v.word] = v
				table.insert(ret, v)
			else
				local o = dups[v.word]
				if o.hidden then
					o.hidden = v.hidden
				end
				if v.ob then
					if o.ob then
						o.multi = o.multi or {}
						table.insert(o.multi, v.ob)
					else
						o.ob = v.ob
					end
				end
			end
		end
	end
	table.sort(ret, function(a, b)
			   return a.word < b.word
	end)
	collectgarbage("restart")
	return ret, eol, vargs
end

local function lev_sort(t)
	for _, v in ipairs(t) do v.i = _ end
	table.sort(t, function(a, b)
			if a.lev == b.lev then
				return a.i < b.i
			end
			return a.lev > b.lev
	end)
	local lev = t[1] and t[1].lev
--	local fuzzy = t[1] and t[1].fuzzy

	local fuzzy = {}
	for _, v in ipairs(t) do if v.lev ~= lev then break end if v.fuzzy then table.insert(fuzzy, v) end end
	if #fuzzy > 0 then
		t = fuzzy
		t.fuzzy = true
	end

	local res = { lev = 0, match = #t > 0 and t[1].match }
	local dup = {}

	for _, v in ipairs(t) do
		if v.lev ~= lev then
			break
		end
		res.lev = lev
		if v.word then
			if not dup[v.word] then
				table.insert(res, v.word)
				dup[v.word] = true
			end
		else
			for _, vv in ipairs(v) do
				if not dup[vv] then
					table.insert(res, vv)
					dup[vv] = true
				end
			end
		end
	end
	return res
end

function mp:compl_match(words)
--	local verb = { words[1] }
	local verbs = self:lookup_verb(words)
--	table.remove(words, 1) -- remove verb
	local matches = {}
	local hints = {}
	local res = {}
	local multi
	collectgarbage("stop")
	for _, v in ipairs(verbs) do
		local m, h, _, mu = self:match(v, words, true)
		if #m > 0 then
			table.insert(matches, { verb = v, match = m[1] })
		end
		if #h > 0 then
			table.insert(hints, h)
		end
		multi = multi or (#mu > 0)
	end
	collectgarbage("restart")
	hints = lev_sort(hints)
	if multi then -- #matches > 0 or #hints == 0 or multi then
		return res
	end
	for _, v in ipairs(hints) do
		if #matches > 0 and #matches[1].match > hints.lev then
			return res, false, not not matches[1].match.vargs
		end
		local pat = self:pattern(v)
		for _, p in ipairs(pat) do
			table.insert(res, p)
		end
	end
	if #hints == 0 and #matches > 0 then
		return res, true, not not matches[1].match.vargs
	end
	return res, #matches > 0
end


function mp:match(verb, w, compl)
	local matches = {}
	local found
	local hints = {}
	local unknown = {}
	local multi = {}
	local parsed_verb = {}
	local fixed_verb = verb.verb[verb.word_nr]
	fixed_verb = fixed_verb.word .. (fixed_verb.morph or '')
	table.insert(parsed_verb, fixed_verb)
	for _, d in ipairs(verb.dsc) do -- verb variants
--		local was_noun = false
		local match = { args = {}, vargs = {}, skip = 0, ev = d.ev, wildcards = 0, verb = parsed_verb, defaults = 0 }
		local a = {}
		found = (#d.pat == 0)
		for k, v in ipairs(w) do
			if k < verb.verb_nr or k >= verb.verb_nr + verb.verb_len then
				table.insert(a, v)
			end
		end
		local skip = {}
		local all_optional = true
		local rlev = 1
		local need_required = false
		local default = false
		local vargs
		for lev, v in ipairs(d.pat) do -- pattern arguments
			if v == '*' or v == '~*' then
				vargs = v -- found
				v = '*'
			end
			local noun = not not v:find("^~?{noun}")
			local pat = self:pattern(v) -- pat -- possible words
			local best = #a + 1
			local best_len = 1
			local word
			local required = false
			found = false
			local wildcard = false
			for _, pp in ipairs(pat) do -- single argument
				if v == '*' then break end
				required = not pp.optional
				if not pp.optional then
					need_required = true
					all_optional = false
				end
				default = pp.default
				if default then
					word = pp.word
				end
				local new_wildcard
				local k, len = word_search(a, pp.word)
				if not k and mp.compare_len > 0 and not pp.synonym then
					k, len = word_search(a, pp.word, starteq)
					new_wildcard = true
				else
					new_wildcard = false
				end
				if (not required or mp.strict_mode) and k ~= 1 then k = false end -- ?word is only in 1st pos
				if k and ((k < best or (k == best and len > best_len)) or
					(not new_wildcard and wildcard and k <= best and len >= best_len)) then
					wildcard = new_wildcard
					best = k
					word = pp.word
					found = pp
					best_len = len
					if pp.synonym or word:find("%*$") then -- subst
						word = found.ob:noun(found.morph, found.alias)
						wildcard = true
					end
				end
			end
			if found then
				need_required = false
				if found.ob then
					local exact
					for _, pp in ipairs(pat) do
						if pp.ob and pp.ob ~= found.ob and self:eq(found.word, pp.word) then
							if not found.multi then
								found.multi = {}
							end
							if not exact and pp.ob:noun(found.morph, pp.alias) == pp.word then -- excactly match
								exact = pp.ob
								table.insert(found.multi, found.ob)
							else
								table.insert(found.multi, pp.ob)
							end
							if found.ob:noun(found.alias) ~= pp.ob:noun(pp.alias) then
								table.insert(multi, { word = pp.ob:noun(pp.alias), lev = rlev })
							end
						end
					end
					if exact then
						found = std.clone(found)
						found.ob = exact
						multi = {}
					end
					if #multi > 0 and found.multi then
						table.insert(multi, 1, { word = found.ob:noun(found.alias), lev = rlev })
						found = false
						break
					end
				end
				if vargs then
					for i = 1, best - 1 do
						table.insert(match.vargs, a[i])
						table.insert(match, a[i])
					end
					if #match.vargs == 0 then -- * in the pattern center
						found = false
						break
					end
					rlev = rlev + 1
				end
				if (wildcard or match.wildcards > 0) and best > 1 then -- do not skip words if wildcard used
					found = false
					vargs = false
					break
				end
--				if false then
--					a = tab_exclude(a, best, best + best_len - 1)
--				else
--				if not was_noun then
				if not vargs then
					match.skip = match.skip + (best - 1)
					for i = 1, best - 1 do
						table.insert(skip, a[i])
					end
				end
--				end
					a = tab_sub(a, best + best_len)
--					table.remove(a, 1)
--				end
				vargs = false
				table.insert(match, word)
				table.insert(match.args, found)
				if wildcard then
					match.wildcards = match.wildcards + 1
				end
				rlev = rlev + 1
--				was_noun = not not found.ob
			elseif vargs then
				if lev == #d.pat then -- last?
					if #a == 0 then
						need_required = true
					end
					while #a > 0 do
						table.insert(match.vargs, a[1])
						table.insert(match, a[1])
						table.remove(a, 1)
					end
				else
					need_required = need_required or required
				end
				if not need_required then
					found = true
				else
					found = false
					if #a > 0 or #match.vargs > 0 then
						while #a > 0 do
							table.insert(match.vargs, a[1])
							table.insert(match, a[1])
							table.remove(a, 1)
						end
						table.insert(hints, { word = v, lev = rlev, match = match })
					else
						table.insert(hints, { word = vargs, lev = rlev, match = match })
					end
				end
				if not found then
					break
				end
			elseif required then
				for i = 1, best - 1 do
					table.insert(unknown, { word = a[i], lev = rlev, noun = noun })
				end
				if best <= 1 and #skip > 0 then
					for i = 1, #skip do
						table.insert(unknown, { word = skip[i], lev = rlev, skip = true })
					end
				end
				if not compl and mp.errhints then
					local objs = {}
					for _, pp in ipairs(pat) do -- single argument
						if not pp.synonym and not objs[pp.ob or 0] then
							local k, _ = word_search(a, pp.word, self.lev_thresh)
							if k then
								table.insert(hints, { word = pp.word, lev = rlev, fuzzy = true, match = match })
								objs[pp.ob or 1] = true
							end
						end
					end
				end
				table.insert(hints, { word = v, lev = rlev, match = match })
				break
			else
				if word then
					table.insert(match, word)
					if default then
						match.defaults = match.defaults + 1
					end
				end
				if default then
					table.insert(match.args, { word = word, default = true } )
				else
					table.insert(match.args, { word = false, optional = true } )
				end
--				table.insert(hints, { word = v, lev = rlev })
				found = true
			end
		end
--		if #multi > 0 then
--			matches = {}
--			break
--		end
		if found or all_optional then
			match.extra = (#a ~= 0)
			if not match.extra or match.wildcards == 0 then
				table.insert(match, 1, fixed_verb) -- w[verb.verb_nr])
				if self:skip_filter(skip) then
					table.insert(matches, match)
				end
				if #match.vargs == 0 and not vargs then
					match.vargs = false
				end
			end
		end
	end

	for k, v in ipairs(matches) do
		v.nr = k
--[[
if false then
		print("-----------", k)
		for kk, vv in ipairs(v) do
			print(vv)
		end
end
]]--
	end

	table.sort(matches,
		function(a, b)
			local na, nb = #a - a.defaults, #b - b.defaults
			if not a.extra and a.skip == 0 then
				na = na + 100
			end
			if not b.extra and b.skip == 0 then
				nb = nb + 100
			end
			if na == nb and a.wildcards == b.wildcards then
				return a.nr < b.nr
			end
			if na == nb then
				return a.wildcards < b.wildcards
			end
			return na > nb
		end)

	if #matches > 0 and matches[1].extra then
		local lev = #matches[1]
		if #unknown > 0 then
			for _, v in ipairs(unknown) do
				if v.lev >= lev and not v.skip then -- and v.noun then
					matches = {}
					break
				end
			end
		end
		if #multi > 0 and #matches > 0 then
			for _, v in ipairs(multi) do
				if v.lev >= lev and not v.skip then
					matches = {}
					break
				end
			end
		end
	end
--[[
	if #unknown > 0 then
		local nmatches = {}
		for _, v in ipairs(matches) do
			if not v.extra then
				table.insert(nmatches, v)
			end
		end
		matches = nmatches
	end
end
if false then
	print "MATCHES: "
	for _, v in ipairs(matches) do
		for _, vv in pairs(v) do
			print(_, vv)
		end
	end

--	for _, v in ipairs(hints) do
--		for _, vv in ipairs(hints) do
--			print(vv.word, vv.fuzzy, vv.lev)
--		end
--	end
end
]]--
	hints = lev_sort(hints)
	unknown = lev_sort(unknown)
	multi = lev_sort(multi)
	if #hints > 0 and #unknown > 0 then
		if hints.lev > unknown.lev then
			unknown = {}
		elseif hints.lev < unknown.lev then
			hints = {}
		end
	end
	return matches, hints, unknown, multi
end

local function get_events(self, ev)
	local events = {}
	self.aliases = {}
--	self.first_it = false
--	self.second_it = false
	self.multi = {}
	for _, v in ipairs(ev) do
		local ea = str_split(v)
		local e = ea[1]
		local args = {}
		table.remove(ea, 1)
		local reverse = false
		for _, vv in ipairs(ea) do
			if vv == 'reverse' then
				reverse = true
			end
		end
		local attrs = {}
		local holder
		for k, vv in ipairs(self.args) do
			if vv and std.is_obj(vv.ob) then
				attrs[k] = {}
				for _, h in ipairs(str_split(vv.morph, ",")) do
					attrs[k][h] = true
					if h == 'holder' then
						holder = vv
					end
				end
			end
		end

		for k, vv in ipairs(self.args) do
			if vv and std.is_obj(vv.ob) then
				local ob, m = multi_select(vv, attrs[k], holder)
				self.multi[ob] = m
				if reverse then
					table.insert(args, 1, ob)
				else
					table.insert(args, ob)
				end
				self.aliases[ob] = vv.alias
			end
		end

		if self.vargs and #self.vargs > 0 then
			local varg = ''
			for _, vv in ipairs(self.vargs) do
				if varg ~= '' then varg = varg .. ' ' end
				varg = varg .. vv
			end
			table.insert(args, varg)
		end
		if std.is_obj(args[1]) then
			self.first_it = args[1]
		end
		if std.is_obj(args[2]) then
			self.second_it = args[2]
		end
		table.insert(events, { ev = e, args = args })
	end
	return events
end

--- Take a value or run the function
--- returns two valuses. retval and true if method was called
-- @param wh what
-- @param fn function
function mp:runorval(wh, fn, ...)
	if wh[fn] == nil then
		return nil, false
	end
	if type(wh[fn]) == 'function' then
		local v
		local r = wh[fn](wh, ...)
		if r == false then v = false else v = true end
		return r, v
	end
	return wh[fn], true
end

function mp:call(ob, ev, ...)
--	self.event = ev
	for _, v in ipairs({ob, ...}) do
		if self.aliases[v] then
			std.rawset(v, '__word_alias', self.aliases[v])
		end
	end

--	self.reaction = std.pget() or false
	local r, v = std.call(ob, ev, ...)
--	std.cctx().txt = self.reaction
	self.reaction = self.reaction or v or false

	if self.debug.trace_action and v then
		dprint("mp:call ", ob, ev, ...)
		p("mp:call ", ob, " ", ev, " ")
		for _, t in ipairs {...} do
			pr (tostring(t), " ")
		end
		pn()
	end
	for _, a in ipairs(self.aliases) do
		std.rawset(a, '__word_alias', nil)
	end
	return r, v
end

function mp:events_call(events, oo, t)
	if not t then t = '' else t = t .. '_' end
	for _, o in ipairs(oo) do
		for _, e in ipairs(events) do
			self.event = e.ev
			local meta = self.event and self.event:find("Meta", 1, true)
			local ename = t .. e.ev
			local eany = t .. 'Any'
			local edef = t .. 'Default'
			local ob = o
			self.first = std.is_obj(e.args[1]) and e.args[1]
			self.second = std.is_obj(e.args[2]) and e.args[2]
			self.first_hint = self.first and self.first:gram().hint
			self.second_hint = self.second and self.second:gram().hint
			if o == 'obj' then
				ob = e.args[1]
				table.remove(e.args, 1)
			end
			local r, v
			if (meta and o == mp) or (not meta and std.is_obj(ob) and (o ~= 'obj' or ob ~= std.here())) then
				r, v = self:call(ob, eany, e.ev, std.unpack(e.args))
				if r then std.pn(r) end
				if not v then
					if ob[ename] then
						r, v = self:call(ob, ename, std.unpack(e.args))
						if r then std.pn(r) end
					else
						r, v = self:call(ob, edef, e.ev, std.unpack(e.args))
						if r then std.pn(r) end
					end
				end
			end
			if r then
				pn()
			end
			if o == 'obj' then
				table.insert(e.args, 1, ob)
			end
			if v and t ~= 'post_' then return v end
		end
	end
	return false
end

function mp:__action(events)
	local r
	self.reaction = false
	self.redirect = false
	r = self:events_call(events, { mp, game, std.here(), 'obj' }, 'before')
	if not r then
		r = self:events_call(events, { 'obj', std.here(), game, mp })
		if not r then
			self:events_call(events, { 'obj', std.here(), game, mp }, 'after')
		end
	end
	if not self.redirect then
		self:events_call(events, { 'obj', std.here(), game, mp }, 'post')
	end
end

function mp:save_ctx()
	return {
		first = self.first,
		second = self.second,
		first_hint = self.first_hint,
		second_hint = self.second_hint,
		event = self.event;
		xevent = self.xevent
	}
end

function mp:restore_ctx(ctx)
	self.first, self.second = ctx.first, ctx.second
	self.first_hint, self.second_hint = ctx.first_hint, ctx.second_hint
	self.event = ctx.event
	self.xevent = ctx.xevent
end

--- Execute a method
-- @usage mp:runmethods('before', 'LetGo', wh, w)
function mp:runmethods(t, verb, ...)
	local events = { {ev = verb, args = { ... }}}
	local ctx = self:save_ctx()
	self.xevent = self.event
	local r, v = self:events_call(events, { 'obj' }, t)
	self:restore_ctx(ctx)
	self.reaction = false
	return r, v
end

--- Execute a new sequence without terminating the current one
function mp:subaction(verb, ...)
	local events = { {ev = verb, args = { ... }}}
	local ctx = self:save_ctx()
	self.xevent = self.event
	local r, v = self:__action(events)
	self:restore_ctx(ctx)
	self.reaction = false
	return r, v
end

--- Switch the sequence to a new event
function mp:xaction(verb, ...)
	local events = { {ev = verb, args = { ... }}}
	local r, v = self:__action(events)
	self.redirect = true
	return r, v
end

function mp:action()
	local parsed = self.parsed
	local ev = str_split(parsed.ev, "|")
	local events = get_events(self, ev)
	self:__action(events)
end

function mp:correct(inp)
	if self:comment() then
		return
	end

	local rinp = ''

	for _, v in ipairs(self.parsed) do
		if rinp ~= '' then rinp = rinp .. ' ' end
		rinp = rinp .. v
	end
	local strip_inp = str_split(inp, mp.inp_delim)
	inp = ''
	for _, v in ipairs(strip_inp) do
		if not mp:ignore_filter(v) then
			if inp ~= '' then inp = inp .. ' ' end
			inp = inp .. v
		end
	end
	local cmprinp = rinp:gsub("["..mp.inp_delim.."]+", " ")
	if not self:eq(cmprinp, inp) then
		pn(fmt.em("("..rinp..")"))
	end
end

function mp:log(t)
	if mp.logfile then
		t = std.fmt(t)
		local f = io.open(mp.logfile, "a+b")
		if not f then return end
		f:write((t or '').."\n\n")
		f:close()
	end
end

function mp:show_prompt(inp)
	if std.cmd[1] == 'look' then
		return false
	end
	if std.here():has 'cutscene' or std.here():has 'noprompt' then
		return false
	end
	if self.prompt then
		pn(fmt.b(self.prompt .. inp))
	end
	return true
end

function mp:comment()
	if self.inp:find("^[ \t]*%*") then return true end
end

--- Main parse function. Input goes here
function mp:parse(inp)
	inp = std.strip(inp)

	if self.expert_mode and not self:comment() and not std.here():type'dlg' then
		local multi_inp = str_split(inp, ".\r\n") or {}

		inp = multi_inp[1] or inp

		for i = 2, #multi_inp do
			self:push(multi_inp[i])
		end
	end

	mp:log("> "..inp)

	local prompt = mp:show_prompt(inp)

	inp = inp:gsub("[ ]+", " "):gsub("["..mp.inp_delim.."]+", " "):gsub("[ \t]+$", "")

	local r, v

	if self:comment() then
		r = false
		v = nil
	else
		self.cache.nouns = self:nouns()
		r, v = self:input(self:norm(inp))
	end
	self.cache = { tokens = {} }; -- to completion
	if not r then
		if v then
--			pn()
			self:err(v)
			local s = std.game
			s:reaction(std.pget())
			r = s:display(false)
			s:lastdisp(r)
			std.abort_cmd = true
			return r
		end
	else
		if std.cmd[1] ~= 'look' and prompt ~= false then
			self:correct(inp)
		end
		-- here we do action
		mp:action()
	end
	mp:post_action()
end

std.world.display = function(s, state)
	local l, av, pv, first
	if not mp.started and mp.text == '' and game:time() == 1 and state ~= false then
		local r = std.call(game, 'dsc')
		if type(r) == 'string' then
			first = true
			if mp._pager_mode then
				mp.text = fmt.anchor() .. r .. '^^' -- .. fmt.anchor()
			else
				mp.text = r .. '^^'
			end
		end
		mp.started = true
	end
	if mp.clear_on_move and game:time() ~= 1 then
		if player_moved() then mp:clear() end
	end
--	mp:trim()
	local reaction = s:reaction() or nil
	if reaction == "" then reaction = nil end
	if state then
--		reaction = iface:em(reaction)
		av, pv = s:events()
		av = iface:em(av)
		pv = iface:em(pv)
		if s.player:need_scene() then
--			t = iface:bold(std.titleof(stead.here()))
			l = s.player:look() -- objects [and scene]
		end
	end
	l = std.par(std.scene_delim, reaction or false,
		    av or false, l or false,
		    pv or false) or ''
	if l ~= '' then
		mp:log(l)
	end
	if mp._pager_mode and not first then
		mp.text = mp.text ..  fmt.anchor() .. l .. '^^' -- .. fmt.anchor()
	else
		mp.text = mp.text ..  l .. '^^' -- .. fmt.anchor()
	end
	return mp.text
end

function mp:pager_mode(mode)
	self._pager_mode = not not mode
	if std.ref'@theme' then
		std.ref'@theme'.set ('win.scroll.mode', mode and 1 or 3)
	end
end

function mp:completion(word)
	self.inp = self:docompl(self.inp, word)
	self.cur = self.inp:len() + 1
	self:compl_fill(self:compl(self.inp))
end

function mp:key_history_prev()
	if #self.history == 0 then
		return
	end
	self.history_pos = self.history_pos + 1
	if self.history_pos > #self.history then
		self.history_pos = #self.history
	end
	self.inp = self.history[self.history_pos]
	self.cur = self.inp:len() + 1
	return true
end

function mp:key_history_next()
	if #self.history == 0 then
		return
	end
	self.history_pos = self.history_pos - 1
	if self.history_pos < 1 then
		self.history_pos = 0
		self.inp = ''
		self.cur = 1
		return true
	end
	self.inp = self.history[self.history_pos]
	self.cur = self.inp:len() + 1
	return true
end

--- Add input string to auto execution mode
-- @param cmd input string
function mp:push(cmd)
	if not cmd then
		return false
	end
	table.insert(self.pushed, cmd)
end

--- Returns true if some auto command is pending
function mp:autoplay_pending()
	return self.autoplay or #self.pushed > 0 or self.autoplay_command
end

--- Internal function that gets commands from script or pushed commands
-- @see mp:push
function mp:autoplay_inp()
	local auto_inp = false
	self.autoplay_command = false
	if #self.pushed > 0 then
		auto_inp = table.remove(self.pushed, 1)
	elseif not self.autoplay then
		return false
	end
	self.inp = auto_inp or self.autoplay:read("*line") or false
	if not self.inp then
		self.inp = ''
		self.autoplay:close()
		self.autoplay = false
	else
		if not auto_inp then
			dprint("> ", self.inp)
		end
		self.autoplay_command = true
	end
	return true
end

function mp:key_enter()
	if self:noparser() then
		return
	end
	if not mp:autoplay_pending() and
		(#self.history == 0 or self.history[1] ~= self.inp) and std.strip(self.inp) ~= '' then
		table.insert(self.history, 1, self.inp)
	end
	self.history_pos = 0
	if #self.history > self.history_len then
		table.remove(self.history, #self.history)
	end
	self:compl_reset();
	local r, v = std.call(mp, 'parse', self.inp)
	self.inp = '';
--[[
	if std.here():has'cutscene' then
		self.inp = mp.cutscene.default_Verb or ''
		if self.inp ~= '' then
			self.inp = self.inp .. ' '
		end
	end
]]--
	self:autoplay_inp()

	self.cur = self.inp:len() + 1;
	mp:post_inp()
--	self:completion()
	return r, v
end

function mp:lookup_noun(w, lev)
	local k, len
	local res = {}
	local oo = self.cache.nouns
	for _, o in ipairs(oo) do
		local ww = {}
		o:noun(ww)
		for _, d in ipairs(ww) do
			k, len = word_search(w, d.word, lev)
			if k and len == #w then
				d.ob = o
				table.insert(res, d)
				break
			end
		end
	end
	if #res == 0 then
		return res
	end
	local uniq = {}
	local same
	for _, v in ipairs(res) do
		local t = v.ob:noun(v.alias)
		if not uniq[t] then
			uniq[t] = v
		else
			same = true
		end
	end
	res = {}
	for _, v in pairs(uniq) do
		table.insert(res, v)
	end
	table.sort(res, function(a, b)
		return a.word:len() > b.word:len()
	end)
	if same then
		res = { res[1] }
	end
	self.aliases = {}
	for _, o in ipairs(res) do
		self.aliases[o.ob] = o.alias
	end
	return res
end

function mp:shorten_input(w)
	if #w < 1 or std.here():type 'dlg' then
		return
	end
	local str
	if #w == 1 and self.shorten[w[1]] then
		str = self.shorten[w[1]]
	end
	if self.expert_mode and not str and
		self.shorten_expert[w[1]] then
		str = self.shorten_expert[w[1]]
	end
	if not str then
		return
	end
	local t = str_split(str, mp.inp_delim)
	table.remove(w, 1)
	for i, v in ipairs(t) do
		table.insert(w, i, v)
	end
end

function mp:strip_input(w)
	local i = 1
	local len = #w
	while i < len do
		if mp:ignore_filter(w[i]) then
			table.remove(w, i)
			len = len - 1
		else
			i = i + 1
		end
	end
end

function mp:input(str)
--	self.cache = { tokens = {} };
	local hints = {}
	local unknown = {}
	local multi = {}
	self.hints = hints
	self.unknown = unknown
	self.multi = multi

	if (self.default_Verb or std.here().default_Verb) and str == "" then
		str = std.here().default_Verb or self.default_Verb
	end
	if type(mp.pre_input) == 'function' then
		str = mp:pre_input(str)
		if not str then return false end
	end
	local w = str_split(str, mp.inp_delim)
	mp:strip_input(w)
	mp:shorten_input(w)
	self.words = w
	if #w == 0 then
		return false, "EMPTY_INPUT"
	end
	local ob = self:lookup_noun(w)
	local verbs = {}
	if #ob == 0 then
		verbs = self:lookup_verb(w)
	end
	if #verbs == 0 then
		-- match object?
		if #ob > 1 then
			self.multi = {}
			for _, v in ipairs(ob) do
				table.insert(self.multi, v.ob:noun(v.alias))
			end
			return false, "MULTIPLE"
		end

		if #ob == 0 then -- try fuzzy
			if not mp.errhints then
				return false, "UNKNOWN_VERB"
			end
			ob = self:lookup_noun(w, self.lev_thresh)
			if #ob >= 1 then
				for _, v in ipairs(ob) do
					table.insert(self.hints, v.word)
				end
				return false, "UNKNOWN_WORD"
			end
			return false, "UNKNOWN_VERB"
		end

		-- it is the object!
		if ob[1].ob.default_Event then
			w = std.call(ob[1].ob, 'default_Event')
		else
			w = self.default_Event or "Exam"
		end
--		pn()
		mp.first_it = ob[1].ob
		self:xaction(w, ob[1].ob)
--		verbs = self:lookup_verb(w)
--		if #verbs == 0 then
--			return false, "UNKNOWN_VERB"
--		end
		return
	end
	local matches = {}
	for _, v in ipairs(verbs) do
		local m, h, u, mu = self:match(v, w)
		if #m > 0 then
			table.insert(matches, { verb = v, match = m[1] })
		end
		if #h > 0 then
			table.insert(hints, h)
		end
		if #u > 0 then
			table.insert(unknown, u)
		end
		if #mu > 0 then
			table.insert(multi, mu)
		end
	end
	table.sort(matches, function(a, b) return #a.match > #b.match end)
	hints = lev_sort(hints)
	unknown = lev_sort(unknown)
	multi = lev_sort(multi)
	local mlev = #matches > 0 and #matches[1].match or 0
	if (hints.lev or 0)> mlev or (unknown.lev or 0) > mlev then
		matches = {}
	end

	if #matches == 0 then
		self.hints = hints
		self.unknown = unknown
		self.multi = multi
		if #multi > 0 then
			self.multi = multi
			return false, "MULTIPLE"
		end
		return false, "INCOMPLETE"
	end
	self.parsed = matches[1].match
	self.args = self.parsed.args
	self.vargs = self.parsed.vargs or {}
	return true
end

function MetaVerb(t)
	return mp:verb(t, mp)
end

function Verb(t, w)
	return mp:verb(t, w)
end

function VerbExtend(t, w)
	return mp:verb(t, w or false, true)
end

function VerbExtendWord(t, w)
	return mp:verb(t, w or false, true, true)
end

function VerbRemove(t, w)
	return mp:verb_remove(t, w)
end

function VerbHint(t, fn, w)
	local v = mp:verb_find(t, w)
	if not v then
		dprint("Unknown verb: " .. tostring(t))
		return
	end
	if v then v.hint = fn end
end
std.rawset(_G, 'mp', mp)
std.mod_cmd(
function(cmd)
	if mp.snapshot then
		snapshots:write(mp.snapshot)
		mp.snapshot = false
	end
	if cmd[1] == '@metaparser' then
		mp.inp = cmd[2] or ''
		cmd[1] = '@mp_key'
		cmd[2] = 'enter'
	end
	if cmd[2] == '@metaparser' then
		if cmd[3] == '<clear>' then
			mp.inp = '';
			mp.cur = 1;
			mp:onedit()
			mp:compl_fill(mp:compl(mp.inp))
			return true, false
		elseif cmd[3] == '<enter>' then
			return mp:key_enter()
		end
		if cmd[3] == '<space>' then
			mp:inp_insert(' ')
			mp:onedit()
			mp:compl_fill(mp:compl(mp.inp))
			return true, false
		end
		if cmd[3] == '<backspace>' then
			mp:inp_remove()
			mp:onedit()
			mp:compl_fill(mp:compl(mp.inp))
			return true, false
		end
		if cmd[4] == 'letter' then
			mp.inp = mp.inp .. cmd[3]
			mp.cur = mp.inp:len() + 1
		else
			mp:completion(cmd[3])
		end
		mp:onedit()
		return true, false
	end
	if (cmd[1] == '@mp_key' and cmd[2] == 'enter') or cmd[1] == 'look' then
		mp:trim()
		if not std.game.__started and cmd[1] == 'look' then
			std.game:__start()
		end
		if mp:noparser() then
			return
		end
--		mp.inp = mp:docompl(mp.inp)
		local r, v, n
		repeat
			if n then
				std.busy(true)
				std.abort_cmd = false
				std.me():moved(false)
				std.me():need_scene(false)
			end
			r, v = mp:key_enter(cmd[1] == 'look')
			n = true
		until not mp:autoplay_pending() or mp:noparser()
		std.busy(false)
		mp:onedit()
		return r, v
	end
	if cmd[1] ~= '@mp_key' then
		return
	end
	mp:onedit()
	return true, false
end)

function mp:autoscript(w)
	if self.autoplay then
		self.autoplay:close()
	end
	self.autoplay = io.open(w or 'autoscript') or false
	if self.autoplay then
		-- self:MetaTranscriptOn();
		std.cmd = { 'autoscript' }
		return true
	end
	return false
end

std.mod_init(
function()
	if DEBUG and mp.undo == 0 then mp.undo = 5 end
	mp:pager_mode(true)
	_'game'.__daemons = std.list {}
end)

function mp:init(lang)
	if type(std.SOURCES_DIRS) == 'table' then
		mrd.dirs = std.SOURCES_DIRS
	end
	if type(std.DICT_FILE) == 'string' then
		mrd.dict_file = std.DICT_FILE
	end
	table.insert(mrd.dirs, curdir)
	mrd:init(lang)
	cutscene = mp.cutscene
	gameover = mp.gameover
	door = mp.door
end
std.mod_start(function()
	mp:compl_reset()
	mp:compl_fill(mp:compl(""))
	mp.last_gfx = false
--	if instead.text_input then instead.text_input(true) end
end, 2)
instead.mouse_filter(0)
-- speedup undo
local obusy = std.busy
local busy_count = 0
function std.busy(b)
	busy_count = busy_count + 1
	if not b or (busy_count % 100) == 0 then
		obusy(b)
	end
end
function instead.fading()
	return instead.need_fading() or player_moved()
end

instead.notitle = true

instead.noways = true

local opr = std.pr

local function shortcut(ob, hint)
	return ob:noun(hint)
end

function mp.shortcut.where(hint)
	return shortcut(std.me():where(), hint)
end

function mp.shortcut.firstwhere(hint)
	return shortcut(mp.first:where(), hint)
end

function mp.shortcut.secondwhere(hint)
	return shortcut(mp.second:where(), hint)
end

function mp.shortcut.here(hint)
	return shortcut(std.here(), hint)
end

function mp.shortcut.first(hint)
	return shortcut(mp.first, hint)
end

function mp.shortcut.firstit(hint)
	return mp.first:it(hint)
end

function mp.shortcut.second(hint)
	return shortcut(mp.second, hint)
end

function mp.shortcut.me(hint)
	return shortcut(std.me(), hint)
end

mp.msg.verbs = {}

function mp.shortcut.verb(hint)
	local verb = mp.msg.verbs[hint]
	if not verb then
		return hint
	end
	return mp.shortcut.word(verb)
end

function mp:shortcut_obj(ob)
	if ob == '#first' then
		ob = mp.first
	elseif ob == '#second' then
		ob = mp.second
	elseif ob == '#firstwhere' then
		ob = mp.first:where()
	elseif ob == '#secondwhere' then
		ob = mp.second:where()
	elseif ob == '#me' then
		ob = std.me()
	elseif ob == '#where' then
		ob = std.me():where()
	elseif ob == '#here' then
		ob = std.here()
	else
		ob = false
	end
	return ob
end

local function hint_append(hint, h)
	if h == "" or not h then return hint end
	if hint == "" or not hint then return h end
	return hint .. ',' .. h
end

function mp.shortcut.word(hint)
	local w = str_split(hint, ",")
	if #w == 0 then
		return hint
	end
	local verb = w[1]
	table.remove(w, 1)
	hint = ''
	for _, k in ipairs(w) do
		if k == '#first' then
			hint = hint_append(hint, mp.first_hint)
		elseif k == '#second' then
			hint = hint_append(hint, mp.second_hint)
		elseif k:find("#", 1, true) == 1 then
			local ob = mp:shortcut_obj(k)
			if not ob then
				std.err("Wrong shortcut word: "..k, 2)
			end
			hint = hint_append(hint, ob:gram().hint)
		else
			hint = hint_append(hint, k)
		end
	end
	local t = mp.mrd:noun(verb .. '/' .. hint)
	return t
end

function mp.shortcut.if_hint(hint)
	local w = str_split(hint, ",")
	if #w < 3 then
		return hint
	end
	local attr = w[2]
	local ob = w[1]
	ob = mp:shortcut_obj(ob)
	if not ob then
		std.err("Wrong object in if_has shortcut: "..hint, 2)
	end
	if not ob:hint(attr) then
		return w[4] or ''
	end
	return w[3] or ''
end

function mp.shortcut.if_has(hint)
	local w = str_split(hint, ",")
	if #w < 3 then
		return hint
	end
	local attr = w[2]
	local ob = w[1]
	ob = mp:shortcut_obj(ob)
	if not ob then
		std.err("Wrong object in if_has shortcut: "..hint, 2)
	end
	if not ob:has(attr) then
		return w[4] or ''
	end
	return w[3] or ''
end

function mp.fmt(...)
	local args = {}
	for _, v in ipairs({...}) do
		local finish
		if type(v) == 'string' then
		repeat
			finish = true
			v = v:gsub("{#[^{}]*}", function(w)
				local ww = w
				w = w:gsub("^{#", ""):gsub("}$", "")
				local hint = w:gsub("^[^/]*/?", "")
				w = w:gsub("/[^/]*$", "")
				local cap = mp.mrd.lang.is_cap(w)
				w = w:lower()
				if mp.shortcut[w] then
					w = mp.shortcut[w](hint)
					if cap then
						w = mp.mrd.lang.cap(w)
					end
				else
					std.err("Wrong shortcut: ".. ww, 2)
				end
				finish = false
				return w
			end)
		until finish
		end
		table.insert(args, v)
	end
	local ret
	for i = 1, #args do
		ret = std.par('', ret or false, std.tostr(args[i]));
	end
	return ret
end

function std.pr(...)
	local ctx = std.cctx()
	if not ctx or not ctx.self then
		return opr(...)
	end
	return opr(mp.fmt(...))
end

function std.obj:persist()
	self:attr 'persist'
	mp.persistent:add(self)
	return self
end

function std.obj:hint(hint)
	return self:gram()[mrd.lang.gram_t[hint] or hint]
end

--- Return a pronoun for the object (capitalized).
-- @see mp:it
function mp:It(w, hint)
	local t = self:it(w, hint)
	return mp.mrd.lang.cap(t)
end

--- Return a pronoun for the object.
-- This is language-dependent.
-- @param hint pronoun case
-- @see mp:it
function std.obj:it(hint)
	if mp.it then
		return mp:it(self, hint)
	else
		if self:hint'plural' then
			return 'they'
		elseif self:hint'female' then
			return 'she'
		elseif self:hint 'male' then
			return 'he'
		else
			return "it"
		end
	end
end

function std.obj:It(hint)
	local t = self:it(hint)
	return mp.mrd.lang.cap(t)
end

function mp:traceinside(w, fn)
	local ww = w and w.obj or std.here().obj
	while #ww > 0 do
		local nww = {}
		for _, o in ipairs(ww) do
			local r, v = fn(o)
			if r ~= nil then
				return r
			end
			if v ~= false then
				for _, vv in ipairs(o.obj) do
					table.insert(nww, vv)
				end
			end
		end
		ww = nww
	end
end

--- Trace an object upwards (check every parent)
-- @param w where
-- @param fn function
function mp:trace(w, fn)
	local ww = {}
	w:where(ww)
	while #ww > 0 do
		local nww = {}
		for _, o in ipairs(ww) do
			local r, v = fn(o)
			if r ~= nil then
				return r
			end
			if v ~= false then
				o:where(nww)
			end
		end
		ww = nww
	end
end

function std.obj:attr(str)
	local a = str_split(str, ", ")
	for _, v in ipairs(a) do
		local val =  (v:find("~", 1, true) ~= 1)
		v = v:gsub("^~", "")
		self['__attr__' .. v] = val
	end
	return self
end

function std.obj:hasnt(attr)
	return not self:has(attr)
end

function std.obj:has(attr)
	attr = std.strip(attr)
	local val =  (attr:find("~", 1, true) ~= 1)
	attr = attr:gsub("^~", "")
	if val then
		return self['__attr__' .. attr]
	else
		return not self['__attr__' .. attr]
	end
end

function iface:title(t)
	return(iface:bold(mrd.lang.cap(t)))
end

std.getmt("").__pow = function(a, b)
	if b then
		if std.is_obj(b) then
			return b ^ a
		end
		return std.rawequal(a, b)
	end
	return false
end
