local export = {}

local KarachayBalkar = require "Module:languages".getByCode("krc")

local harmonic_types = {
	["и"] = "front_unrounded",
	["е"] = "front_unrounded", ["э"] = "front_unrounded",
	["ы"] = "back_unrounded",
	
	["у"] = "back_rounded", ["о"] = "back_rounded",
	["ю"] = "rounded", -- ü, yu,
	["ё"] = "rounded", -- ö,
	
	["а"] = "back_unrounded", ["я"] = "back_unrounded",
}

local function last_harmonic_type_and_vowel(word)
	local harmonic_type, last_vowel
	for pos, vowel_digraph, vowel_letter in mw.ustring.gmatch(mw.ustring.lower(word), "()(([иеэәуыоаяёю])ь?)") do
		if harmonic_types[vowel_digraph] then
			last_vowel = vowel_digraph
			harmonic_type = harmonic_types[vowel_digraph]
		else
			last_vowel = vowel_letter
			harmonic_type = harmonic_types[vowel_letter]
		end
	end
	return harmonic_type, last_vowel
end

local function split_vowel_dimensions(harmonic_type)
	local parts = mw.text.split(harmonic_type, "_")
	assert(#parts == 1 or #parts == 2)
	local roundedness = parts[#parts]
	local backness = #parts == 2 and parts[1] or nil
	return backness, roundedness
end

-- Assumes that the only vowels in the ending are е or и.
local function harmonize(stem, ending, is_back)
	local harmonic_type, last_vowel = last_harmonic_type_and_vowel(stem)
	if not harmonic_type then
		error("No last harmonic type for " .. tostring(stem))
	end
	if (last_vowel == "ю" or last_vowel == "ё") and is_back then
		harmonic_type = harmonic_types["у"]
	end
	local backness, roundedness = split_vowel_dimensions(harmonic_type)
	
	return (mw.ustring.gsub(ending, "[геи]",
		function(letter)
			local result
			if letter == "е" then
				if not backness or backness == "front" then
					result = "е"
				else result = "а" end
			elseif letter == "и" then
				if not backness or backness == "front" then
					if not roundedness or roundedness == "unrounded" then
						result = "и"
					else result = "ю" end
				else
					if not roundedness or roundedness == "unrounded" then
						result = "ы"
					else result = "у" end
				end
			elseif letter == "г" then
				if not backness or backness == "front" then
					result = "г"
				else
					result = "гъ"
				end
			end
			
			local harmonic_type = last_harmonic_type_and_vowel(result)
			if harmonic_type then
				backness, roundedness = split_vowel_dimensions(harmonic_type)
			end
			
			return result
		end))
end

function export.generate_forms(stem, stem_translit, is_back)
	assert(type(stem) == "string")
	assert(stem_translit == nil or type(stem_translit) == "string")
	
	local forms = {}
	
	local function make_form(key, ending)
		ending = harmonize(stem, ending, is_back)
		forms[key] = stem .. ending
		if stem_translit then
			-- Transliterating a transliterated stem plus Cyrillic ending does
			-- not work in all cases, but should work with these noun endings.
			forms[key .. "_tr"] = (KarachayBalkar:transliterate(stem_translit .. ending))
		end
	end
	
	-- vowels given in front unrounded forms
	make_form("nom_sg", "")
	make_form("acc_sg", "ни")
	make_form("dat_sg", "ге")
	make_form("loc_sg", "де")
	make_form("abl_sg", "ден")
	make_form("gen_sg", "ни")
	
	make_form("nom_pl", "ле")
	make_form("acc_pl", "лени")
	make_form("dat_pl", "леге")
	make_form("loc_pl", "леде")
	make_form("abl_pl", "леден")
	make_form("gen_pl", "лени")
	
	return forms
end

local full_link = require "Module:links".full_link
local function link(term, tr)
	return full_link({ term = term, lang = KarachayBalkar, tr = tr })
end

local function add_forms(text, forms)
	return (text
		:gsub(
			"{{{(.-)}}}",
			function(code)
				local form = forms[code]
				local tr = forms[code .. "_tr"]
				if not form then return end
				if code == "testcase" then
					return form
				else
					return link(form, tr)
				end
			end))
end

local function make_testcase(forms)
	local number_abbrs = { "sg", "pl" }
	local case_abbrs = { "nom", "acc", "dat", "loc", "abl", "gen" }
	local rows = {}
	for _, number in ipairs(number_abbrs) do
		local row = {}
		for _, case in ipairs(case_abbrs) do
			table.insert(row, forms[case .. "_" .. number])
		end
		table.insert(rows, table.concat(row, " "))
	end
	return table.concat(rows, "\n")
end

function export.noun(frame)
	local args = frame:getParent().args
	local stem = args[1] or frame.args[1]
	local tr = args.tr or frame.args.tr
	local is_back = require "Module:yesno"(args.back or frame.args.back, false)
	if stem then
		stem = mw.text.trim(stem)
	end
	if stem == "" then
		stem = nil
	end
	if not stem then
		local title = mw.title.getCurrentTitle()
		local namespace = title.nsText
		if namespace == "" then
			stem = title.text
		else
			error("Parameter 1 (stem) is required.")
		end
	end
	
	local template = [[
{| class="wikitable"
! 
! Singular
! Plural
|-
! nominative
| {{{nom_sg}}}
| {{{nom_pl}}}
|-
! accusative
| {{{acc_sg}}}
| {{{acc_pl}}}
|-
! dative
| {{{dat_sg}}}
| {{{dat_pl}}}
|-
! locative
| {{{loc_sg}}}
| {{{loc_pl}}}
|-
! ablative
| {{{abl_sg}}}
| {{{abl_pl}}}
|-
! genitive
| {{{gen_sg}}}
| {{{gen_pl}}}
|}]]
	local forms = export.generate_forms(stem, tr, is_back)
	mw.log(make_testcase(forms))
	return add_forms(template, forms)
end

return export