local export = {}

local m_conj = require("Module:fr-conj")
local m_links = require("Module:links")
local lang = require("Module:languages").getByCode("fr")
local IPA = function(str)
	return require("Module:IPA").format_IPA(nil,str)
end

local rmatch = mw.ustring.match
local rsubn = mw.ustring.gsub

local pref_sufs = {}
export.pref_sufs = pref_sufs

-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
	local retval = rsubn(term, foo, bar)
	return retval
end

local function verb_slot_to_accel(slot)
	return rsub(slot, "^([a-z_]+)_([123])([sp])$",
		function(mood_tense, person, number)
			local mood_tense_to_infl = {
				["ind_p"] = "pres|indc",
				["ind_i"] = "impf|indc",
				["ind_ps"] = "phis",
				["ind_f"]  = "futr",
				["cond_p"] = "cond",
				["sub_p"] = "pres|subj",
				["sub_pa"] = "impf|subj",
				["imp_p"] = "impr"
			}
			return person .. "|" .. number .. "|" .. mood_tense_to_infl[mood_tense]
		end
	)
end

local function map(seq, fun)
	if type(seq) == "table" then
		local ret = {}
		for _, s in ipairs(seq) do
			-- Handle stem of the form {"STEM", RESPELLING="RESPELLING"}
			if type(s) == "table" then
				s = s[1]
			end
			-- store in separate var in case fun() has multiple retvals
			local retval = fun(s)
			table.insert(ret, retval)
		end
		return ret
	else
		-- store in separate var in case fun() has multiple retvals
		local retval = fun(seq)
		return retval
	end
end

local function add(source, appendix)
	return map(source, function(s) return s .. appendix end)
end

function export.make_ind_p_e(data, stem, stem2, stem3)
	stem2 = stem2 or stem
	stem3 = stem3 or stem

	data.forms.ind_p_1s = add(stem,"e")
	data.forms.ind_p_2s = add(stem,"es")
	data.forms.ind_p_3s = add(stem,"e")
	data.forms.ind_p_3p = add(stem,"ent")
	data.forms.ind_p_1p = add(stem2,"ons")
	-- stem3 is used in -ger and -cer verbs
	data.forms.ind_p_2p = add(stem3,"ez")

	export.make_ind_i(data, stem2, stem3)
	export.make_ind_ps_a(data, stem2, stem3)
	export.make_sub_p(data, stem, stem3)
	export.make_imp_p_ind(data)
	export.make_ind_f(data, add(stem, "er"))
	data.forms.ppr = add(stem2,"ant")
end

function export.make_ind_p(data, stem, stem2, stem3)
	stem2 = stem2 or stem
	stem3 = stem3 or stem2
	data.forms.ind_p_1s = add(stem,"s")
	data.forms.ind_p_2s = add(stem,"s")
	-- add t unless stem ends in -t (e.g. met), -d (e.g. vend, assied) or
	-- -c (e.g. vainc).
	data.forms.ind_p_3s = map(stem, function(s)
		return add(s, rmatch(s, "[tdc]$") and "" or "t")
	end)
	data.forms.ind_p_1p = add(stem2,"ons")
	data.forms.ind_p_2p = add(stem2,"ez")
	data.forms.ind_p_3p = add(stem3,"ent")

	export.make_ind_i(data, stem2)
	export.make_sub_p(data, stem3, stem2)
	export.make_imp_p_ind(data)
	data.forms.ppr = add(stem2,"ant")
end

function export.make_ind_i(data, stem, stem2)
	stem2 = stem2 or stem
	data.forms.ind_i_1s = add(stem,"ais")
	data.forms.ind_i_2s = add(stem,"ais")
	data.forms.ind_i_3s = add(stem,"ait")
	data.forms.ind_i_1p = add(stem2,"ions")
	data.forms.ind_i_2p = add(stem2,"iez")
	data.forms.ind_i_3p = add(stem,"aient")
end

function export.make_ind_ps_a(data, stem, stem2)
	stem2 = stem2 or stem
	data.forms.ind_ps_1s = add(stem,"ai")
	data.forms.ind_ps_2s = add(stem,"as")
	data.forms.ind_ps_3s = add(stem,"a")
	data.forms.ind_ps_1p = add(stem,"âmes")
	data.forms.ind_ps_2p = add(stem,"âtes")
	data.forms.ind_ps_3p = add(stem2,"èrent")

	export.make_sub_pa(data,add(stem,"a"))

	data.forms.pp = data.forms.pp or add(stem2,"é")
end

local function fix_circumflex(val)
	return rsub(val, "[aiïu]n?%^",{["a^"]="â", ["i^"]="î", ["ï^"]="ï", ["in^"]="în", ["u^"]="û"})
end

function export.make_ind_ps(data, stem)
	data.forms.ind_ps_1s = add(stem,"s")
	data.forms.ind_ps_2s = add(stem,"s")
	data.forms.ind_ps_3s = add(stem,"t")
	data.forms.ind_ps_1p = map(add(stem,"^mes"), fix_circumflex)
	data.forms.ind_ps_2p = map(add(stem,"^tes"), fix_circumflex)
	data.forms.ind_ps_3p = add(stem,"rent")

	export.make_sub_pa(data,stem)

	data.forms.pp = data.forms.pp or stem
end

function export.make_ind_f(data, stem)
	data.forms.ind_f_1s = add(stem,"ai")
	data.forms.ind_f_2s = add(stem,"as")
	data.forms.ind_f_3s = add(stem,"a")
	data.forms.ind_f_1p = add(stem,"ons")
	data.forms.ind_f_2p = add(stem,"ez")
	data.forms.ind_f_3p = add(stem,"ont")

	export.make_cond_p(data, stem)
end

function export.make_cond_p(data, stem)
	data.forms.cond_p_1s = add(stem,"ais")
	data.forms.cond_p_2s = add(stem,"ais")
	data.forms.cond_p_3s = add(stem,"ait")
	data.forms.cond_p_1p = add(stem,"ions")
	data.forms.cond_p_2p = add(stem,"iez")
	data.forms.cond_p_3p = add(stem,"aient")
end

function export.make_sub_p(data, stem, stem2)
	stem2 = stem2 or stem
	data.forms.sub_p_1s = add(stem,"e")
	data.forms.sub_p_2s = add(stem,"es")
	data.forms.sub_p_3s = add(stem,"e")
	data.forms.sub_p_3p = add(stem,"ent")
	data.forms.sub_p_1p = add(stem2,"ions")
	data.forms.sub_p_2p = add(stem2,"iez")
end

function export.make_sub_pa(data, stem)
	data.forms.sub_pa_1s = add(stem,"sse")
	data.forms.sub_pa_2s = add(stem,"sses")
	data.forms.sub_pa_3s = map(add(stem,"^t"), fix_circumflex)
	data.forms.sub_pa_1p = add(stem,"ssions")
	data.forms.sub_pa_2p = add(stem,"ssiez")
	data.forms.sub_pa_3p = add(stem,"ssent")
end

function export.make_imp_p_ind(data)
	data.forms.imp_p_2s = map(data.forms.ind_p_2s, function(form)
		return rsub(form, "([ae])s$", "%1")
	end)
	data.forms.imp_p_1p = data.forms.ind_p_1p
	data.forms.imp_p_2p = data.forms.ind_p_2p
end

function export.make_imp_p_ind_sub(data)
	data.forms.imp_p_2s = map(data.forms.ind_p_2s, function(form)
		return rsub(form, "([ae])s$", "%1")
	end)
	data.forms.imp_p_1p = map(data.forms.sub_p_1p, function(form)
		return rsub(form, "ions$", "ons")
	end)
	data.forms.imp_p_2p = map(data.forms.sub_p_2p, function(form)
		return rsub(form, "iez$", "ez")
	end)
end

function export.make_imp_p_sub(data)
	data.forms.imp_p_2s = map(data.forms.sub_p_2s, function(form)
		return rsub(form, "es$", "e")
	end)
	data.forms.imp_p_1p = map(data.forms.sub_p_1p, function(form)
		return rsub(form, "ions$", "ons")
	end)
	data.forms.imp_p_2p = map(data.forms.sub_p_2p, function(form)
		return rsub(form, "iez$", "ez")
	end)
end

function export.clear_imp(data)
	data.forms.imp_p_2s = "—"
	data.forms.imp_p_1p = "—"
	data.forms.imp_p_2p = "—"
end

local function add_prefix_suffix(data, key, val, pref_v, pref_c, pron_v, pron_c, imp, pron_imp, join_imp_dash)
	if key == "pp" or rmatch(key, "_nolink") then
		return
	end
	local imp_joiner, imp_pron_joiner
	if join_imp_dash then
		imp_joiner = "-"
		imp_pron_joiner = "."
	else
		imp_joiner = ""
		imp_pron_joiner = ""
	end
	local pref, suf, pref_pron, suf_pron
	local function get_pref_suf(v)
		pref, suf, pref_pron, suf_pron = "", "", "", ""
		if not rmatch(key, "imp") then
			if rmatch(v, "^[aeéêiouhywjɑɛœø]") then
				pref, pref_pron = pref_v, pron_v
			else
				pref, pref_pron = pref_c, pron_c
			end
		else
			suf, suf_pron = imp_joiner .. imp, imp_pron_joiner .. pron_imp
		end
	end
	if key == "inf" or key == "ppr" then
		data.forms[key .. '_nolink'] =
			map(data.forms[key], function(val)
				get_pref_suf(val)
				return pref .. val .. suf
			end)
	end
	data.forms[key] = map(val, function(v)
		get_pref_suf(v)
		return rsub(pref .. "[[" .. v .. "]]" .. suf, "%.h", "h")
	end)
	if data.prons[key] then
		data.prons[key] = map(data.prons[key], function(v)
			get_pref_suf(v)
			return pref_pron .. v .. suf_pron
		end)
	end
end

pref_sufs["y"] = function(data, key, val)
	local imp = rmatch(key, "2s") and not rmatch(val, "s$") and "s-y" or "-y"
	add_prefix_suffix(data, key, val, "y ", "y ", "i.j‿", "i ", imp, ".zi", false)
end

pref_sufs["en"] = function(data, key, val)
	local imp = rmatch(key, "2s") and not rmatch(val, "s$") and "s-en" or "-en"
	add_prefix_suffix(data, key, val, "en ", "en ", "ɑ̃.n‿", "ɑ̃ ", imp, ".zɑ̃", false)
end

pref_sufs["yen"] = function(data, key, val)
	local imp = rmatch(key, "2s") and not rmatch(val, "s$") and "s-y en" or "-y-en"
	add_prefix_suffix(data, key, val, "y en ", "y en ", "i.j‿ɑ̃.n‿", "i.j‿ɑ̃ ", imp, ".zi.jɑ̃", false)
end

pref_sufs["le"] = function(data, key, val)
	add_prefix_suffix(data, key, val, "l'", rmatch(key, "2p") and "le ''or'' la " or "le ", "l", "lə ", "-le", ".lə", false)
end

pref_sufs["la"] = function(data, key, val)
	add_prefix_suffix(data, key, val, "l'", "la ", "l", "la ", "-la", ".la", false)
end

pref_sufs["l"] = function(data, key, val)
	add_prefix_suffix(data, key, val, "l'", "l'", "l", "l", "-le ''or'' -la", ".lə ''or'' .la", false)
end

pref_sufs["les"] = function(data, key, val)
	add_prefix_suffix(data, key, val, "les ", "les ", "le.z‿", "le", "-les", ".le", false)
end

pref_sufs["l_y"] = function(data, key, val)
	add_prefix_suffix(data, key, val, "l'y ", "l'y ", "li.j‿", "li ", "-l'y", ".li", false)
end

pref_sufs["l_en"] = function(data, key, val)
	data.aux = "l'en avoir"
	add_prefix_suffix(data, key, val, "l'en ", "l'en ", "lɑ̃.n‿", "lɑ̃ ", "l'en", "lɑ̃", true)
end

pref_sufs["lesen"] = function(data, key, val)
	data.aux = "les en avoir"
	add_prefix_suffix(data, key, val, "les en ", "les en ", "le.z‿ɑ̃.n‿", "le.z‿ɑ̃ ", "-les-en", "le.zɑ̃", true)
end

pref_sufs["lesy"] = function(data, key, val)
	add_prefix_suffix(data, key, val, "les y ", "les y ", "le.z‿i.j‿", "le.z‿i ", "-les-y", ".le.zi", false)
end

pref_sufs["l_yen"] = function(data, key, val)
	data.aux = "l'y en avoir"
	add_prefix_suffix(data, key, val, "l'y en ", "l'y en ", "li.j‿ɑ̃.n‿", "li.j‿ɑ̃ ", "l'y-en", "li.jɑ̃", true)
end

pref_sufs["lesyen"] = function(data, key, val)
	data.aux = "les y en avoir"
	add_prefix_suffix(data, key, val, "les y en ", "les y en ", "le.z‿i.j‿ɑ̃.n‿", "le.z‿i.j‿ɑ̃ ", "-les-y-en", "le.zi.jɑ̃", true)
end

-- Add reflexive prefixes and suffixes to verb forms. KEY is the form to add to, and VAL is the value
-- without prefixes and suffixes. PREF_V is the prefix to add to non-imperative forms that begin with a
-- vowel, and PREF_C is the corresponding prefix for non-imperative forms beginning with a consonant.
-- PRON_V is the prefix to add to the pronunciation of non-imperative forms that begin with a vowel, and
-- PRON_C is the corresponding prefix for pronunciations beginning with a consonant. IMP is the suffix to
-- add to imperative forms, and PRON_IMP is the corresponding suffix to add to the pronunciation of
-- imperative forms. All prefixes and suffixes are added directly, without an intervening space.
--
-- Since the prefixes and suffixes vary depending on the person and number, the specified prefixes and
-- suffixes should contain "%c" and/or "%v" specs in them. "%c" represents the appropriate pronominal
-- form to add before a consonant (or when nothing follows, in the case of suffixes), and "%v" represents
-- the corresponding form to add before a vowel. The actual forms substituted depend on both the person
-- and number of KEY and the type of prefix (i.e. whether it's a spelled prefix, pronunciation prefix,
-- spelled imperative suffix or pronunciation imperative suffix). For example, if key contains "2s"
-- (i.e. it represents a second person singular form), a "%v" in a spelled prefix will be replaced with
-- "t'" and a "%c" in a pronounced prefix will be replaced with "tə ". Note that it's not always the
-- case that %c should occur in a pre-consonant prefix/suffix and correspondingly for %v. For example,
-- if the prefix occurs before "en" or "y" you should use %v in both the pre-vowel and pre-consonant
-- prefixes.
local function add_refl_prefix_suffix(data, key, val, pref_v, pref_c, pron_v, pron_c, imp, pron_imp)
	local sub_pref_v, sub_pref_c, sub_pron_v, sub_pron_c
	local sub_imp_v, sub_imp_c, sub_pron_imp_v, sub_pron_imp_c = "", "", "", ""
	if rmatch(key, "1s") then
		sub_pref_v, sub_pref_c, sub_pron_v, sub_pron_c = "m'", "me ", "m", "mə "
	elseif rmatch(key, "2s") then
		sub_pref_v, sub_pref_c, sub_pron_v, sub_pron_c = "t'", "te ", "t", "tə "
		sub_imp_v, sub_imp_c, sub_pron_imp_v, sub_pron_imp_c = "t'", "toi", "t", "twa"
	elseif rmatch(key, "1p") then
		sub_pref_v, sub_pref_c, sub_pron_v, sub_pron_c = "nous ", "nous ", "nu.z‿", "nu "
		sub_imp_v, sub_imp_c, sub_pron_imp_v, sub_pron_imp_c = "nous-", "nous", "nu.z", "nu"
	elseif rmatch(key, "2p") then
		sub_pref_v, sub_pref_c, sub_pron_v, sub_pron_c = "vous ", "vous ", "vu.z‿", "vu "
		sub_imp_v, sub_imp_c, sub_pron_imp_v, sub_pron_imp_c = "vous-", "vous", "vu.z", "vu"
	else
		sub_pref_v, sub_pref_c, sub_pron_v, sub_pron_c = "s'", "se ", "s", "sə "
	end
	local function dosub(spec, dotype)
		if type(spec) == "table" then
			local sing, plur = spec[1], spec[2]
			if rmatch(key, "[12]p") then
				spec = plur
			else
				spec = sing
			end
		end
		spec = rsub(spec, "%%v",
			dotype == "pron" and sub_pron_v or
			dotype == "imp" and sub_imp_v or
			dotype == "pron-imp" and sub_pron_imp_v or
			sub_pref_v
		)
		spec = rsub(spec, "%%c",
			dotype == "pron" and sub_pron_c or
			dotype == "imp" and sub_imp_c or
			dotype == "pron-imp" and sub_pron_imp_c or
			sub_pref_c
		)
		return spec
	end
	add_prefix_suffix(data, key, val, dosub(pref_v), dosub(pref_c), dosub(pron_v, "pron"), dosub(pron_c, "pron"),
		dosub(imp, "imp"), dosub(pron_imp, "pron-imp"), true)
end

pref_sufs["refl"] = function(data, key, val)
	data.aux = "s'être"
	add_refl_prefix_suffix(data, key, val, "%v", "%c", "%v", "%c", "%c", "%c")
end

pref_sufs["reflen"] = function(data, key, val)
	data.aux = "s'en être"
	add_refl_prefix_suffix(data, key, val, "%ven ", "%ven ", "%vɑ̃.n‿", "%vɑ̃ ", "%ven", "%vɑ̃")
end

pref_sufs["refly"] = function(data, key, val)
	data.aux = "s'y être"
	add_refl_prefix_suffix(data, key, val, "%vy ", "%vy ", "%vi.j‿", "%vi ", "%vy", "%vi")
end

pref_sufs["reflyen"] = function(data, key, val)
	data.aux = "s'y en être"
	add_refl_prefix_suffix(data, key, val, "%vy en ", "%vy en ", "%vi.j‿ɑ̃.n‿", "%vi.j‿ɑ̃ ", "%v'y-en", "%vi.jɑ̃")
end

pref_sufs["reflle"] = function(data, key, val)
	data.aux = "se l'être"
	add_refl_prefix_suffix(data, key, val, "%cl'", "%cle ", "%cl", "%clə ", "le-%c", "lə.%c")
end

pref_sufs["refll"] = function(data, key, val)
	data.aux = "se l'être"
	add_refl_prefix_suffix(data, key, val, "%cl'", "%cl'", "%cl", "%cl", "le-%c ''or'' -la-%c", "lə.%c ''or'' .la.%c")
end

pref_sufs["reflla"] = function(data, key, val)
	data.aux = "se l'être"
	add_refl_prefix_suffix(data, key, val, "%cl'", "%cla ", "%cl", "%cla ", "la-%c", "la.%c")
end

pref_sufs["reflles"] = function(data, key, val)
	data.aux = "se les être"
	add_refl_prefix_suffix(data, key, val, "%cles ", "%cles ", "%cle.z‿", "%cle ", "les-%c", "le.%c")
end

pref_sufs["reflly"] = function(data, key, val)
	data.aux = "se l'y être"
	add_refl_prefix_suffix(data, key, val, "%cl'y ", "%cl'y ", "%cli.j‿", "%cli ", "le-%cy <i>or</i> -la-%cy", "lə.%ci <i>or</i> .la.%ci")
end

pref_sufs["refllesy"] = function(data, key, val)
	data.aux = "se les y être"
	add_refl_prefix_suffix(data, key, val, "%cles y ", "%cles y ", "%cle.z‿i.j‿", "%cle.z‿i ", "les-%cy", "le.%ci")
end

function export.link(data)
	for key, val in pairs(data.forms) do
		if type(val) ~= "table" then
			val = {val}
		end
		-- don't destructively modify data.forms[key][i] because it might
		-- be shared among different keys
		local newval = {}
		for i,form in ipairs(val) do
			local newform = form
			if not rmatch(key,"nolink") and not rmatch(form,"—") then
				newform = m_links.full_link({term = form, lang = lang,
					accel = { form = verb_slot_to_accel(key) }
				})
			end
			if rmatch(form, "—") then
				newform = "—"
			end
			table.insert(newval, newform)
		end
		data.forms[key] = table.concat(newval, " or ")
	end
	for key, val in pairs(data.prons) do
		if not rmatch(key,"nolink") then
			if type(val) ~= "table" then
				val = {val}
			end
			-- don't destructively modify data.forms[key][i] because it might
			-- be shared among different keys
			local newprons = {}
			for i,form in ipairs(val) do
				if not rmatch(form,"—") then
					table.insert(newprons, IPA('/' .. form .. '/'))
				end
			end
			if #newprons > 0 and data.forms[key] ~= "—" then
				data.forms[key] = data.forms[key] .. '<br /><span style="color:#7F7F7F">' .. table.concat(newprons, " or ") .. '</span>'
			end
		end
	end
end

-- not sure if it's still used by something so I'm leaving a stub function instead of removing it entirely
function export.make_table(data)
	return m_conj.make_table(data)
end

function export.extract(data, args)
	if args.inf then
		data.forms.inf = args.inf
		export.make_ind_f(data, rsub(args.inf,"e$",""))
		export.make_cond_p(data, rsub(args.inf,"e$",""))
	end
	if args.pp then
		data.forms.pp = args.pp
		if rmatch(args.pp, "[iu]$") then
			export.make_ind_ps(data, args.pp)
			export.make_sub_pa(data, args.pp)
		end
	end
	for _,form in ipairs({"ind_p","ind_i","ind_ps","ind_f","cond_p","sub_p","sub_pa","imp_p"}) do
		local dot_form = rsub(form,"_",".")
		if args[dot_form] then
			if form == "ind_p" then
				local stem = args[dot_form]
				local stem2 = stem
				local stem3 = stem
				if rmatch(stem, "^[^/]+/[^/]+/[^/]+$") then
					stem = rsub(stem, "^([^/]+)/([^/]+)/([^/]+)$", "%1")
					stem2 = rsub(stem2, "^([^/]+)/([^/]+)/([^/]+)$", "%2")
					stem3 = rsub(stem3, "^([^/]+)/([^/]+)/([^/]+)$", "%3")
				elseif rmatch(stem, "^[^/]+/[^/]+$") then
					stem = rsub(stem, "^([^/]+)/([^/]+)$", "%1")
					stem2 = rsub(stem2, "^([^/]+)/([^/]+)$", "%2")
					stem3 = stem2
				end
				if args["ind.p_e"] then
					export.make_ind_p_e(data, stem, stem2, stem3)
				else
					export.make_ind_p(data, stem, stem2, stem3)
				end
				for _,person in ipairs({"1s","2s","3s","1p","2p","3p"}) do
					data.forms[form .. "_" .. person] = args[dot_form .. "." .. person] or data.forms[form .. "_" .. person]
				end
				export.make_imp_p_ind(data)
			elseif form == "ind_i" then
				export.make_ind_i(data, args[dot_form])
			elseif form == "ind_ps" then
				if rmatch(args["ind.ps"], "a$") then
					local stem = rsub(args[dot_form],"a$","")
					export.make_ind_ps_a(data, stem)
				else
					export.make_ind_ps(data, args[dot_form])
				end
				export.make_sub_pa(data, args[dot_form])
			elseif form == "ind_f" then
				export.make_ind_f(data, args[dot_form])
			elseif form == "cond_p" then
				export.make_cond_p(data, args[dot_form])
			elseif form == "sub_p" then
				local stem = args[dot_form]
				local stem2 = stem
				if rmatch(stem, "^[^/]+/[^/]+$") then
					stem = rsub(stem, "^([^/]+)/([^/]+)$", "%1")
					stem2 = rsub(stem2, "^([^/]+)/([^/]+)$", "%2")
				end
				export.make_sub_p(data, stem, stem2)
			elseif form == "sub_pa" then
				export.make_sub_pa(data, args[dot_form])
			elseif form == "imp_p" then
				if args[dot_form] == "sub" then
					export.make_imp_p_sub(data)
				elseif args[dot_form] == "ind_sub" then
					export.make_imp_p_ind_sub(data)
				else
					export.make_imp_p_ind(data)
				end
			end
		end
		for _,person in ipairs({"1s","2s","3s","1p","2p","3p"}) do
			data.forms[form .. "_" .. person] = args[dot_form .. "." .. person] or data.forms[form .. "_" .. person]
		end
	end

	data.forms.ppr = args.ppr or data.forms.ppr
	if not data.forms.ppr then
		if data.forms.ind_p_1p then
			data.forms.ppr = map(data.forms.ind_p_1p, function(val)
				return rsub(val, "ons$", "ant")
			end)
		else
			data.forms.ppr = ""
		end
	end
	data.forms.pp = args.pp or data.forms.pp
end

return export

-- For Vim, so we get 4-space tabs
-- vim: set ts=4 sw=4 noet: