local m_links = require('Module:links')

local export = {}

local lang = require("Module:languages").getByCode("la")

-- A wrapper function allowing the contents of this module to be called from
-- templates. For example, '{{#invoke:la-utilities|main|strip_macrons|mȳthos}}'
-- produces 'mythos'.
function export.main(frame)
	if(frame.args[1] == 'strip_macrons') then
		return lang:makeEntryName(frame.args[2])
	end
	if type(p[frame.args[1]]) == 'function' then
		return p[frame.args[1]](frame.args[2], frame.args[3])
	else
		return p[frame.args[1]][frame.args[2]]
	end
end

-- public domain
-- via http://snippets.luacode.org/?p=snippets/Check_string_ends_with_other_string_74
local function endswith(s, send)
	return #s >= #send and s:find(send, #s-#send+1, true) and true or false
end

-- helper function
-- add suffixes to a stem based on which form is chosen.
-- parameter baseform is used for 2nd declension words (unused for 1st declension). Replaces 'W'.
-- num = 'sg' 'pl' 'both' or nil (nil is same as 'both')
-- loc = true or false (to add locative)
function export.decline(form, stem, baseform, num, loc)
	
	local suffix = {
		['1st'] = { 
			['title']='[[Appendix:Latin first declension|First declension]].',
			['nom_sg']='Xa', ['gen_sg']='Xae', ['dat_sg']='Xae', ['acc_sg']='Xam', ['abl_sg']='Xā', ['voc_sg']='Xa', 
			['nom_pl']='Xae', ['gen_pl']='Xārum', ['dat_pl']='Xīs', ['acc_pl']='Xās', ['abl_pl']='Xīs', ['voc_pl']='Xae',
			['if_loc'] = { ['title'] = '[[Appendix:Latin first declension|First declension]] with locative.', 	
				['loc_sg']='Xae', ['loc_pl']='Xīs' } },

		['1st-abus'] = { ['stem']='X',
			['title']='[[Appendix:Latin first declension|First declension]] with dative/ablative plural in {{m|la||-ābus}}.',
			['nom_sg']='Xa', ['gen_sg']='Xae', ['dat_sg']='Xae', ['acc_sg']='Xam', ['abl_sg']='Xā', ['voc_sg']='Xa', 
			['nom_pl']='Xae', ['gen_pl']='Xārum', ['dat_pl']='Xābus', ['acc_pl']='Xās', ['abl_pl']='Xābus', ['voc_pl']='Xae' },

		['1st-Greek'] = { ['stem']='X',
			['title']='[[Appendix:Latin first declension|First declension]], Greek type.',
			['nom_sg']='Xē', ['gen_sg']='Xēs', ['dat_sg']='Xae', ['acc_sg']='Xēn', ['abl_sg']='Xē', ['voc_sg']='Xē', 
			['nom_pl']='Xae', ['gen_pl']='Xārum', ['dat_pl']='Xīs', ['acc_pl']='Xās', ['abl_pl']='Xīs', ['voc_pl']='Xae' },

		['1st-Greek-Ma'] = { 
			['title']='[[Appendix:Latin first declension|First declension]], Greek type masculine in {{m|la||-ās}}.',
			['nom_sg']='Xās', ['gen_sg']='Xae', ['dat_sg']='Xae', ['acc_sg']='Xān', ['abl_sg']='Xā', ['voc_sg']='Xā', 
			['nom_pl']='Xae', ['gen_pl']='Xārum', ['dat_pl']='Xīs', ['acc_pl']='Xās', ['abl_pl']='Xīs', ['voc_pl']='Xae' },

		['1st-Greek-Me'] = { 
			['title']='[[Appendix:Latin first declension|First declension]], Greek type masculine in {{m|la||-ēs}}.',
			['nom_sg']='Xēs', ['gen_sg']='Xae', ['dat_sg']='Xae', ['acc_sg']='Xēn', ['abl_sg']='Xē', ['voc_sg']='Xē', 
			['nom_pl']='Xae', ['gen_pl']='Xārum', ['dat_pl']='Xīs', ['acc_pl']='Xās', ['abl_pl']='Xīs', ['voc_pl']='Xae' },

		['1st-am'] = { 
			['title']='[[Appendix:Latin first declension|First declension]] with nominative singular in {{m|la||-am}}.',
			['nom_sg']='Xam', ['gen_sg']='Xae', ['dat_sg']='Xae', ['acc_sg']='Xam', ['abl_sg']='Xā', ['voc_sg']='Xam', 
			['nom_pl']='Xae', ['gen_pl']='Xārum', ['dat_pl']='Xīs', ['acc_pl']='Xās', ['abl_pl']='Xīs', ['voc_pl']='Xae' },

		['2nd'] = {
			['title']='[[Appendix:Latin second declension|Second declension]].',
			['nom_sg']='Xus', ['gen_sg']='Xī', ['dat_sg']='Xō', ['acc_sg']='Xum', ['abl_sg']='Xō',  ['voc_sg']='Xe', 
			['nom_pl']='Xī', ['gen_pl']='Xōrum', ['dat_pl']='Xīs', ['acc_pl']='Xōs', ['abl_pl']='Xīs', ['voc_pl']='Xī',
			['if_loc'] = { ['title'] = '[[Appendix:Latin second declension|Second declension]] with locative.', 
				['loc_sg']='Xī', ['loc_pl']='Xīs' }  },

		['2nd-N'] = {
			['title']='[[Appendix:Latin second declension|Second declension]] neuter.',
			['nom_sg']='Xum', ['gen_sg']='Xī', ['dat_sg']='Xō', ['acc_sg']='Xum', ['abl_sg']='Xō',  ['voc_sg']='Xum', 
			['nom_pl']='Xa', ['gen_pl']='Xōrum', ['dat_pl']='Xīs', ['acc_pl']='Xa', ['abl_pl']='Xīs', ['voc_pl']='Xa',
			['if_loc'] = { ['title'] = '[[Appendix:Latin second declension|Second declension]] neuter with locative.', 
				['loc_sg']='Xī', ['loc_pl']='Xīs' } },

		['2nd-er'] = {
			['title']='[[Appendix:Latin second declension|Second declension]], nominative singular in {{m|la||-er}}.',
			['nom_sg']='W', ['gen_sg']='Xī', ['dat_sg']='Xō', ['acc_sg']='Xum', ['abl_sg']='Xō',  ['voc_sg']='W', ['voc_sg2']='Xe',
			['nom_pl']='Xī', ['gen_pl']='Xōrum', ['dat_pl']='Xīs', ['acc_pl']='Xōs', ['abl_pl']='Xīs', ['voc_pl']='Xī',
			['if_loc'] = { ['title'] = '[[Appendix:Latin second declension|Second declension]] with locative.', 
				['loc_sg']='Xī', ['loc_pl']='Xīs' }  },

		['2nd-Greek'] = {
			['title']='[[Appendix:Latin second declension|Second declension]], Greek type',
			['nom_sg']='Xos', ['gen_sg']='Xī', ['dat_sg']='Xō', ['acc_sg']='Xon', ['acc_sg2']= 'Xum', ['abl_sg']='Xō', ['voc_sg']='Xe', 
			['nom_pl']='Xī', ['gen_pl']='Xōrum', ['dat_pl']='Xīs', ['acc_pl']='Xōs', ['abl_pl']='Xīs', ['voc_pl']='Xī' },
		
		['2nd-N-Greek'] = {
			['title']='[[Appendix:Latin second declension|Second declension]] neuter, Greek type',
			['nom_sg']='Xon', ['gen_sg']='Xī', ['dat_sg']='Xō', ['acc_sg']='Xon', ['abl_sg']='Xō', ['voc_sg']='Xon', 
			['nom_pl']='Xa', ['gen_pl']='Xōrum', ['dat_pl']='Xīs', ['acc_pl']='Xa', ['abl_pl']='Xīs', ['voc_pl']='Xa' },
		
		['2nd-ius'] = { -- todo: footnote for gen_sg
			['title']='[[Appendix:Latin second declension|Second declension]], nominative singular in {{m|la||-ius}}.',
			['nom_sg']='Xïus', ['gen_sg']='Xïī', ['dat_sg']='Xiō', ['acc_sg']='Xium', ['abl_sg']='Xiō',  ['voc_sg']='Xī', 
			['nom_pl']='Xiī', ['gen_pl']='Xiōrum', ['dat_pl']='Xiīs', ['acc_pl']='Xiōs', ['abl_pl']='Xiīs', ['voc_pl']='Xiī'},
		
		['2nd-N-us'] = {
			['title']='[[Appendix:Latin second declension|Second declension]] neuter with nominative/accusative/vocative in {{m|la||-us}}.',
			['nom_sg']='Xus', ['gen_sg']='Xī', ['dat_sg']='Xō', ['acc_sg']='Xus', ['abl_sg']='Xō', ['voc_sg']='Xus' } }

	local dtable = suffix[form]
	if (dtable == nil) then error ('form missing: ' .. form ) end
	dtable['stem'] = 'X'
	dtable['no_include'] = { ['if_loc']=true, ['table']=true, ['no_include']=true, ['no_expand']=true }
	dtable['no_expand'] = { ['title']=true, ['if_loc']=true, ['table']=true, ['no_include']=true, ['no_expand']=true }

	if loc == true then
		if (not dtable['if_loc']) then
			error('locative cannot be used here')
		end
		for key, value in pairs(dtable['if_loc']) do 
			dtable[key] = value
		end
	end
	
	for key, value in pairs(dtable) do 
		if (not dtable['no_expand'][key]) then
			-- if (value == nil) then error('value missing for key: ' .. key ) end
			-- if (stem == nil) then error('value missing for stem: ' .. stem ) end
			dtable[key] = value:gsub('X', stem)
			if (baseform ~= nil) then 
				dtable[key] = dtable[key]:gsub('W', baseform)
			end
		end
	end
	
	if num == 'sg' or num == 'pl' then
		-- todo: include sg/pl in title
		dtable['table']='la-decl-noun-table-single'
		dtable['num'] = num
	elseif num == nil and loc == true then
		-- default to num=sg (if not desired, num=pl or num=both should be set by calling method)
		dtable['table']='la-decl-noun-table-single'
		dtable['num'] = 'sg' -- todo: check dtable['num'] isn't already filleda
	else 
		dtable['table']='la-decl-noun-table'
	end
	
	-- not really needed now: values for expanding into old templates, which didn't have full table templates: (la-decl-1st-loc, la-decl-1st-p-loc)
	--dtable[1] = export.strip_macrons(stem)
	--dtable[2] = stem
	return dtable
end

-- turn certain values of a declension table into links
-- helper function
function export.linkify(dtable)
	local linkifable = {
		['nom-sg']=true, ['gen-sg']=true, ['dat-sg']=true, 
		['acc-sg']=true, ['abl-sg']=true, ['voc-sg']=true, ['loc-sg']=true,
		['nom-pl']=true, ['gen-pl']=true, ['dat-pl']=true, ['acc-pl']=true, 
		['abl-pl']=true, ['voc-pl']=true, ['loc-pl']=true
	}
 
	for key, value in pairs(dtable) do 
		if linkifable[key] then
			dtable[key] = m_links.full_link(value, nil, lang)
		end
	end
end

-- Build a declension table, for a first declension Latin word. 
-- See export.firstDeclensionTable for usage
function export.firstDeclensions(word, num, loc)
	-- the two exceptional words which won't work automatically:
	if word == 'dea' then word = 'deābus' elseif word == 'filia' then word = 'filiābus' end
	
	local suffixes = {['a']='1st', ['ābus']='1st-abus', ['ē']='1st-Greek', 
		['ās']='1st-Greek-Ma', ['ēs']='1st-Greek-Me', ['am']='1st-am'
		-- ['ae']='1st-pl', -- handle manually
		-- ['īs']='1st-loc-pl' -- handle manually
	}
	local stem
	word = mw.text.decode(word) -- sanitize
	
	if num ~= nil and num == '{{{num}}}' then
		num = nil -- defaults to 'both' (unless it's a locative)
	elseif num == 'p' then
		num = 'pl'
	elseif num == 's' then
		num = 'sg'
	end
	
	local useLoc = false
	if loc ~= nil and (loc == 'y' or loc == '1') then
		useLoc = true
	end
	
	-- special cases: check if word ends in -īs or -ae
	if endswith(word, 'īs') and (num == nil or num == 'pl') then
		local key = 'īs'
		stem = word:sub(0, -(key:len())-1 )
		return export.decline('1st', stem, nil, 'pl', true)
	elseif endswith(word, 'ae') then
		stem = word:sub(0, -3)
		if (num == nil or num == 'pl') then
			-- treat as plural nominative
			return export.decline('1st', stem, nil, 'pl', useLoc)
		elseif num == 'sg' then
			 -- treat as locative. 
			 -- todo: tell the user to -loc template and -a word instead
			return export.decline('1st', stem, nil, 'sg', true) 
		else
			-- user has requested num=both.
			return export.decline('1st', stem, nil, num, useLoc)
		end
	end
	
	-- default case: uses 'suffixes' table to choose declension template
	for key,value in pairs(suffixes) do 
		if endswith(word, key) then 
			stem = word:sub(0, -(key:len())-1)
			return export.decline(value, stem, nil, num, useLoc)
		end
	end

	-- error: Suffix not found. List allowed endings.
	local allowed_endings = ''
	for key,value in pairs(suffixes) do 
		allowed_endings = allowed_endings .. '-' .. key .. ' '
	end
	error('Parameter 1 (' .. word .. ') is not a Latin word or does not have an allowed word ending: ' .. allowed_endings .. '. Please see [[Template:la-decl-first]] for more information.', 2)
end


-- Build a declension table, for a second declension Latin word. 
function export.secondDeclensions(word, word2, num, loc)
	
	local suffixes = {['ius']='2nd-ius', ['us']='2nd', ['um']='2nd-N', 
		['r']='2nd-er', -- ignored except by error message
		['os']='2nd-Greek', ['on']='2nd-N-Greek', 
		['ī']='2nd', ['a']='2nd-N'
		-- -loc	-N-loc	-N-us
	}
	local stem
	word = mw.text.decode(word) -- sanitize
	
	if word2 ~= nil and (word2 == '' or word2 == '{{{2}}}') then
		word2 = nil
	end
	
	if (word2 ~= nil) then word2 = mw.text.decode(word2) end

	-- TODO: too much copy/paste code
	if num ~= nil and num == '{{{num}}}' then
		num = nil -- defaults to 'both' (unless it's a locative)
	elseif num == 'p' then
		num = 'pl'
	elseif num == 's' then
		num = 'sg'
	end
	
	local useLoc = false
	if loc ~= nil and (loc == 'y' or loc == '1') then
		useLoc = true
	end
	
	-- special case: locative with both sg and pl requested
	if (useLoc) and (num == nil) then
		if endswith(word, 'us') then num = 'sg' 
		elseif endswith(word, 'um') then num = 'sg'
		elseif endswith(word, 'ī') then num = 'pl'
		elseif endswith(word, 'a') then num = 'pl' end
	end
	
	-- special case:
	if endswith(word, 'us') and (useLoc == false) and (num == 'sg') then
		local key = 'us'
		stem = word:sub(0, -(key:len())-1)
		return export.decline('2nd-N-us', stem, nil, 'sg', useLoc)
	end
	
	-- special csae: -r words
	if endswith(word, 'r') then
		if (word2 == nil or word2:len() < 2) then
			stem = word
			return export.decline('2nd-er', stem, word, num, useLoc)
		else
			local key = 'ī'
			stem = word2:sub(0, -(key:len())-1 )
			return export.decline('2nd-er', stem, word, num, useLoc)
		end
	end

	-- -- default case: uses 'suffixes' table to choose declension template
	for key,value in pairs(suffixes) do 
		if endswith(word, key) then 
			stem = word:sub(0, -(key:len())-1)
			return export.decline(value, stem, nil, num, useLoc)
		end
	end

	-- error: Suffix not found. List allowed endings.
	local allowed_endings = ''
	for key,value in pairs(suffixes) do 
		allowed_endings = allowed_endings .. '-' .. key .. ' '
	end
	error('Parameter 1 (' .. word .. ') is not a Latin word or does not have an allowed word ending: ' .. allowed_endings .. '. Please see [[Template:la-decl-second]] for more information.', 2)
end

-- Create a declension table for a Latin word in the first declension. 
-- called by [[Template:la-decl-first]]
-- Parameters:
-- 1 = the Latin word, with macrons. Usually the singular nominative form. The exceptions are:
--       a) if the word allows a locative form, give that the singular plural form: (ending in -ae), unless it only plural, then use that form (-īs). use loc=s for only singular (hides the plural column)
--       b) the word allows an -ābus suffix, in which case give that form
-- loc=s = singular locative form (or use -ae ending for parameter 1)
-- loc=pl = plural locative form (or use -īs ending for parameter 1)
-- todo: replace loc parameter with num parameter, and make it work for all inputs

function export.firstDeclensionTable(frame)
	local NAMESPACE = mw.title.getCurrentTitle().nsText
	local word = frame.args[1]
	local num = frame.args['num']
	local loc = frame.args['loc']
	
	if word == nil or word == '' or word == '{{{1}}}' then
		if NAMESPACE == "Template" then
			word = "-a"
		else
			-- use page name
			-- error('Missing argument. 1 required (a Latin word). See [[Template:la-decl-first]] for usage information.') 
			word = mw.title.getCurrentTitle().text
		end
	end
	
	local decl = export.firstDeclensions(word, num, loc)
	-- export.linkify(decl) -- not strictly needed, as done by Template:l now.
	
	if decl['title'] ~= nil then
		decl['title'] = frame:preprocess(decl['title']) -- process any templates contained within the title parameter
	end
	
	local tablename = decl['table']

	for key, value in pairs(decl['no_include']) do 
		decl[key] = nil	
	end
	
	return frame:expandTemplate{title = tablename, args = decl}
end

-- Create a declension table for a Latin word in the second declension. 
-- called by [[Template:la-decl-second]]
function export.secondDeclensionTable(frame)
	local NAMESPACE = mw.title.getCurrentTitle().nsText
	local word = frame.args[1]
	local word2 = frame.args[2]
	local num = frame.args['num']
	local loc = frame.args['loc']
	
	if word == nil or word == '' or word == '{{{1}}}' then
		if NAMESPACE == "Template" then
			word = "-us"
		else
			-- use page name
			-- error('Missing argument. 1 required (a Latin word). See [[Template:la-decl-first]] for usage information.') 
			word = mw.title.getCurrentTitle().text
		end
	end
	
	local decl = export.secondDeclensions(word, word2, num, loc)

	-- too much copy/paste below here (TODO: make a function)
	if decl['title'] ~= nil then
		decl['title'] = frame:preprocess(decl['title']) -- process any templates contained within the title parameter
	end
	
	local tablename = decl['table']

	for key, value in pairs(decl['no_include']) do 
		decl[key] = nil	
	end
	
	return frame:expandTemplate{title = tablename, args = decl}
end


-- Create a declension table for a word. 
-- Parameters:
-- 1 = which declension: first, second, third, fourth, fifth (always first
-- 2 = the Latin word, with macrons. Usually the singular nominative form. The exceptions are:
--       a) for first declension words which allow a locative form, give that form (ending in -ae or -īs). Or use singular nominative and loc=s, or loc=pl (as for other declensions)
--       b) the word allows an -ābus suffix, in which case give that form
-- 3 = the stem (not used for first declension words)
-- loc=s = singular locative form
-- loc=pl = plural locative form

function export.declensionTable(frame)
	-- TODO
end

function export.strip_macrons(frame)
	return lang:makeEntryName(frame.args[1])
end

local patterns_override = {
	["tūdō"] = "tūdin",
	["is"] = "",
	["āns"] = "ant",
	["ēns"] = "ent",
	["ōns"] = "ont",
	["ceps"] = "cipit",
	["us"] = "or",
	["ex"] = "ic",
}

local patterns = {
	["ma"] = "mat",
	["e"] = "",
	["men"] = "min",
	["er"] = "r",
	["or"] = "ōr",
	["ō"] = "ōn",
	["s"] = "t",
	["x"] = "c",
}

function export.make_stem2(stem)
	for key,val in pairs(patterns_override) do
		if mw.ustring.match(stem,key.."$") then
			stem = mw.ustring.gsub(stem,key.."$",val)
			require('Module:debug').track("la-utilities/"..key)
			return stem
		end
	end
	for key,val in pairs(patterns) do
		if mw.ustring.match(stem,key.."$") then
			stem = mw.ustring.gsub(stem,key.."$",val)
			require('Module:debug').track("la-utilities/"..key)
			return stem
		end
	end
	require('Module:debug').track("la-utilities")
	return stem
end

return export