local type = type
local kbdru = {
	["q"] = "й",
	["w"] = "ц",
	["e"] = "у",
	["r"] = "к",
	["t"] = "е",
	["y"] = "н",
	["u"] = "г",
	["i"] = "ш",
	["o"] = "щ",
	["p"] = "з",
	["["] = "х",
	["]"] = "ъ",
	["a"] = "ф",
	["s"] = "ы",
	["d"] = "в",
	["f"] = "а",
	["g"] = "п",
	["h"] = "р",
	["j"] = "о",
	["k"] = "л",
	["l"] = "д",
	[";"] = "ж",
	["'"] = "э",
	["z"] = "я",
	["x"] = "ч",
	["c"] = "с",
	["v"] = "м",
	["b"] = "и",
	["n"] = "т",
	["m"] = "ь",
	[","] = "б",
	["."] = "ю",
	["`"] = "ё",
	["/"] = ".",
	shifted = {
	["q"] = "Й",
	["w"] = "Ц",
	["e"] = "У",
	["r"] = "К",
	["t"] = "Е",
	["y"] = "Н",
	["u"] = "Г",
	["i"] = "Ш",
	["o"] = "Щ",
	["p"] = "З",
	["["] = "Х",
	["]"] = "Ъ",
	["a"] = "Ф",
	["s"] = "Ы",
	["d"] = "В",
	["f"] = "А",
	["g"] = "П",
	["h"] = "Р",
	["j"] = "О",
	["k"] = "Л",
	["l"] = "Д",
	[";"] = "Ж",
	["'"] = "Э",
	["z"] = "Я",
	["x"] = "Ч",
	["c"] = "С",
	["v"] = "М",
	["b"] = "И",
	["n"] = "Т",
	["m"] = "Ь",
	[","] = "Б",
	["."] = "Ю",
	["`"] = "Ё",
	["1"] = "!",
	["2"] = "@",
	["3"] = "#",
	["4"] = ";",
	["5"] = "%",
	["6"] = ":",
	["7"] = "?",
	["8"] = "*",
	["9"] = "(",
	["0"] = ")",
	["-"] = "_",
	["="] = "+",
	["/"] = ",",
	}
}

local toupper = {
	["й"] = "Й",
	["ц"] = "Ц",
	["у"] = "У",
	["к"] = "К",
	["е"] = "Е",
	["н"] = "Н",
	["г"] = "Г",
	["ш"] = "Ш",
	["щ"] = "Щ",
	["з"] = "З",
	["х"] = "Х",
	["ъ"] = "Ъ",
	["ф"] = "Ф",
	["ы"] = "Ы",
	["в"] = "В",
	["а"] = "А",
	["п"] = "П",
	["р"] = "Р",
	["о"] = "О",
	["л"] = "Л",
	["д"] = "Д",
	["ж"] = "Ж",
	["э"] = "Э",
	["я"] = "Я",
	["ч"] = "Ч",
	["с"] = "С",
	["м"] = "М",
	["и"] = "И",
	["т"] = "Т",
	["ь"] = "Ь",
	["б"] = "Б",
	["ю"] = "Ю",
	["ё"] = "Ё",
}

local function lower(str)
	if type(str) ~= 'string' then
		return str
	end
	str = str:lower()
	for k, v in pairs(toupper) do
		str = str:gsub(v, k)
	end
	return str
end

local function upper(str)
	if type(str) ~= 'string' then
		return str
	end
	str = str:upper();
	for k, v in pairs(toupper) do
		str = str:gsub(k, v)
	end
	return str
end

local function is_cap(str)
	if type(str) ~= 'string' then
		return false
	end
	local s, e
	for _, v in pairs(toupper) do
		if not s and str:find("^"..v) then
			s = true
		end
		if not e and str:find(v.."$") then
			e = true
		end
		if not s and str:find("^[A-Z]") then
			s = true
		end
		if not e and str:find("[A-Z]$") then
			e = true
		end
	end
	return s, e
end

local function cap(str)
	if type(str) ~= 'string' then
		return str
	end
	if str:find("^[a-z]") then
		str = str:gsub("^.", function(v) return v:upper() end)
		return str
	end
	for k, v in pairs(toupper) do
		local s = str:gsub("^"..k, v)
		if s ~= str then
			return s
		end
	end
	return str
end

local lang

local function norm(str)
	if type(str) ~= 'string' then
		return str
	end
	if not lang.yo then
		str = str:gsub("ё", "е"):gsub("Ё", "Е")
	end
	return str
end

local vowels = {
	["у"] = true,
	["е"] = true,
	["ы"] = true,
	["а"] = true,
	["о"] = true,
	["и"] = true,
	["ю"] = true,
	["ё"] = true,
	["э"] = true,
	["я"] = true,
}

local function is_vowel(l)
	l = lower(l);
	return vowels[l]
end

local gram_tt = {
	["ИНФИНИТИВ"] = true;
	["КР_ПРИЛ"] = true;
	["КР_ПРИЧАСТИЕ"] = true;
	["Г"] = true;
}

local function flex_filter(v)
	local an = v.an
	if an["им"] then
		return true
	end
	if an["рд"] or an["дт"] or an["тв"] or an["пр"] or an["вн"] then
		return false
	end
	if an["0"] then
		return true
	end
	return gram_tt[an.t]
end

local function gram_info(a)
	local t = { }
	if a['мр'] then
		t.gen = 'male'
	elseif a['жр'] then
		t.gen = 'female'
	elseif a['ср'] then
		t.gen = 'neuter'
	else
		t.gen = 'any'
	end

	if a['мн'] then
		t.num = 'singular'
	elseif a['ед'] then
		t.num = 'plural'
	else
		t.num = 'any'
	end

	if a['буд'] then
		t.time = 'future'
	elseif a['прш'] then
		t.time = 'past'
	elseif a['нст'] then
		t.time = 'present'
	else
		t.time = 'any'
	end

	if a['1л'] then
		t.face = 'first'
	elseif a['2л'] then
		t.face = 'second'
	elseif a['3л'] then
		t.face = 'third'
	else
		t.face = 'any'
	end

	return t
end

local function __gram_compat(g1, g2, time)
	if g1.gen ~= g2.gen and g1.gen ~= 'any' and g2.gen ~= 'any' then return false end
	if not time then
		if g1.num ~= g2.num and g1.num ~= 'any' and g2.num ~= 'any' then return false end
	end
	if g1.time ~= g2.time and g1.time ~= 'any' and g2.time ~= 'any' then return false end
	if g1.face ~= g2.face and g1.face ~= 'any' and g2.face ~= 'any' then return false end
	return true
end

local function gram_eq(a, b)
	if not a or not b then return true end
	if a == 'ИНФИНИТИВ' or b == 'ИНФИНИТИВ' then
		return b == a or b == 'Г' or a == 'Г'
	end
	if a == 'КР_ПРИЛ' or b == 'КР_ПРИЛ' then
		return b == a -- or b == 'П'
	end
	if a == 'КР_ПРИЧАСТИЕ' or b == 'КР_ПРИЧАСТИЕ' then
		return b == a
	end
	if a == 'ПРИЧАСТИЕ' or b == 'ПРИЧАСТИЕ' then
		return b == a
	end
	if a == 'Г' or b == 'Г' then return a == b end
	return true
end

local function gram_compat(base, aa, bb)
	if not gram_eq(base.t, aa.t) then
		return false
	end
--	local a, b = aa.t, bb.t
	local g1, g2 = gram_info(aa), gram_info(bb)
	if bb.noun then
		if not base['им'] then
			return false
		end
		local g0 = gram_info(base)
		if not __gram_compat(g0, g1, true) then return false end
		if not __gram_compat(g0, g2, true) then return false end
	end
	return __gram_compat(g1, g2)
end

local function gram_norm(an)
	local a = {}
	local g = {}
	for _, v in ipairs(an) do
		a[v] = true
		table.insert(g, v)
	end
	if not a['1л'] and not a['2л'] and not a['3л'] then
		table.insert(g, '3л')
	end
	return g
end

local function gram_score(an, g)
	local score = 0
	g = gram_norm(g)
	if an["фам"] then score = score - 0.1 end
	if an["арх"] then score = score - 0.1 end
	if not g["од"] and not g["но"] and an["од"] and not an["но"] then score = score - 0.1 end
	for _, vv in ipairs(g or {}) do
		if vv:sub(1, 1) == '~' then
			vv = vv:sub(2)
			if an[vv] then
				score = score - 1
			elseif an.t == vv then
				score = score - 10
			end
		else
			if an[vv] then
				score = score + 1
			elseif an.t == vv then
				score = score + 10
			end
		end
	end
	return score
end

lang = { yo = false,
	kbd = kbdru,
	norm = norm,
	upper = upper,
	lower = lower,
	cap = cap,
	is_cap = is_cap,
	is_vowel = is_vowel,
	flex_filter = flex_filter,
	gram_compat = gram_compat,
	gram_score = gram_score,
	gram_t = {
		noun = 'С',
		nom = 'им',
		live = 'од',
		nonlive = 'но',
		neuter = 'ср',
		male = 'мр',
		female = 'жр',
		plural = 'мн',
		proper = 'имя',
		surname = 'фам',
		first = '1л',
		second = '2л',
		third = '3л',
	};
	dict = {
		["ведро/ср,но,С"] = {
			"ведро/им", "вёдра/им,мн",
			"ведро/вн", "вёдра/вн,мн",
			"ведра/рд", "вёдер/рд,мн",
			"ведру/дт", "ведрам/дт,мн",
			"ведром/тв", "вёдрами/тв,мн",
			"ведре/пр", "вёдрах/пр,мн",
		},
		["деревья/ср,но,мн,С"] = {
			"деревья/им";
			"деревья/вн";
			"деревьев/рд";
			"деревьями/тв";
			"деревьях/пр";
			"деревьям/дт";
		};
		["листья/мр,но,мн,С"] = {
			"листья/им";
			"листья/вн";
			"листьев/рд";
			"листьями/тв";
			"листьях/пр";
			"листьям/дт";
		};
		["дерево/ср,но,С"] = {
			"дерево/им", "деревья/им,мн";
			"дерево/вн", "деревья/вн,мн";
			"дерева/рд", "деревьев/рд,мн";
			"деревом/тв", "деревьями/тв,мн";
			"дереве/пр", "деревьях/пр,мн";
			"дереву/пр", "деревьям/дт,мн";
		};
		["огонь/мр,но,С"] = {
			"огонь/им", "огни/им,мн",
			"огонь/вн", "огни/вн,мн",
			"огня/рд", "огней/рд,мн",
			"огню/дт", "огням/дт,мн",
			"огнём/тв", "огнями/тв,мн",
			"огне/пр", "огнях/пр,мн",
		};
		["цветы/мн,мр,но,С"] = { -- цветки
			"цветы/им",
			"цветы/вн",
			"цветов/рд",
			"цветам/дт",
			"цветами/тв",
			"цветах/пр",
		};
		["хлам/пр,2"] = "хламе";
		["клевер/пр"] = "клевере";
		["песок/пр,2"] = "песке";
	};
}

return lang
