This module implements {{auto cat}}.


local export = {}


local function splitLabelLang(titleObject)
	local words = mw.text.split(titleObject.text, " ", true)
	local getByCanonicalName = require("Module:languages").getByCanonicalName
	local lang
	
	-- Progressively remove words from the end of the category name until it matches a language.
	while words[1] and not lang do
		words[#words] = nil
		lang = getByCanonicalName(table.concat(words, " "))
	end
	
	local label = (lang and mw.ustring.sub(titleObject.text, mw.ustring.len(lang:getCanonicalName() .. " ") + 1) or titleObject.text)
	
	return label, lang
end


-- List of handler functions that try to match the page name.
-- A handler should return a table of template title plus arguments
-- that is passed to frame:expandTemplate.
-- If a handler does not recognise the page name, it should return nil.
-- Note that the order of functions matters!

local handlers = {}


-- Topical categories
table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "^[a-z-]+:.") then
		return nil
	end
	
	local code, label = mw.ustring.match(titleObject.text, "^([a-z-]+):(.+)$")
	return {title = "topic cat", args = {code, label}}
end)


--[[	langcatboiler
		Shouldn't be used because there are additional parameters, such as
		countries where that the language is or was spoken,
		that should always be supplied. --]]

table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "[lL]anguage$") then
		return nil
	end
	
	local langName = mw.ustring.match(titleObject.text, "^(.+) language$")
	
	-- Use the entire category name if it doesn't end in "language", to handle
	-- cases where "language" is part of the name, e.g. ASL.
	local lang = require("Module:languages").getByCanonicalName(langName) or require("Module:languages").getByCanonicalName(titleObject.text)
	
	if not lang then
		local lang2 = require("Module:languages").getByName(langName)
		if lang2 then
			error('"' .. langName .. '" is not a valid canonical name. Use "' .. lang2:getCanonicalName() .. '" instead.')
		end
		
		return nil
	end
	
	return { title = "langcatboiler", args = { lang:getCode() } }
end)


-- Letter names
table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "letter names$") then
		return nil
	end
	
	local langCode = mw.ustring.match(titleObject.text, "^([^:]+):")
	local lang, cat
	
	if langCode then
		lang = require("Module:languages").getByCode(langCode) or error('The language code "' .. langCode .. '" is not valid.')
		cat = mw.ustring.match(titleObject.text, ":(.+)$")
	else
		cat = titleObject.text
	end
	
	return {title = "topic cat", args = {lang and lang:getCode() or nil, cat}}
end)


-- letter cat
table.insert(handlers, function(titleObject)
	if not (mw.ustring.len(titleObject.text) <= 3 or mw.ustring.len(titleObject.text) <= 6 and mw.ustring.find(titleObject.text, ":", 1, true)) then
		return nil
	end
	
	return {title = "letter cat"}
end)


-- Japanese kanji reading cat
table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "^Japanese kanji") then
		return nil
	end
	
	return {title = "ja-readings-cat"}
end)


-- Unicode block cat
table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "block$") then
		return nil
	end
	
	return {title = "Unicode block cat"}
end)


-- request cat
table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "^Requests") then
		return nil
	end
	
	return {title = "request cat"}
end)


-- PIE root cat
table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "[Tt]erms derived from the PIE root") then
		return nil
	end
	
	return {title = "PIE root cat"}
end)


-- PIE word cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^[tT]erms derived from the PIE word %*") then
		return nil
	end
	
	local word = mw.ustring.match(label, "^[Tt]erms derived from the PIE word %*(.+)$")
	return {title = "PIE word cat", args = {lang and lang:getCode() or nil, word}}
end)


-- ar-root cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not (lang and lang:getCode() == "ar" and mw.ustring.find(label, "^terms belonging to the root .+")) then
		return nil
	end
	
	return {title = "ar-root cat"}
end)


--HE root cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not (lang and lang:getCode() == "he" and mw.ustring.find(label, "^terms belonging to the root .+")) then
		return nil
	end
	
	local root = mw.ustring.match(label, "^terms belonging to the root (.+)$")
	local parts = mw.text.split(root, "־", true)
	return {title = "HE root cat", args = parts}
end)


-- he-patterncat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not (lang and lang:getCode() == "he" and mw.ustring.find(label, "^terms in the pattern .+")) then
		return nil
	end
	
	return {title = "he-patterncat"}
end)


-- derivcatboiler
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^[Tt]erms derived from .") then
		return nil
	end
	
	local sourcename = mw.ustring.match(label, "^[Tt]erms derived from (.+)$")
	local source
	
	if mw.ustring.find(sourcename, " [Ll]anguages$") then
		sourcename = mw.ustring.gsub(sourcename, " languages$", "")
		source = require("Module:families").getByCanonicalName(sourcename)
	else
		source = require("Module:etymology languages").getByCanonicalName(sourcename) or require("Module:languages").getByCanonicalName(sourcename)
	end
	
	if source then
		return {title = "derivcatboiler", args = {lang and lang:getCode() or nil, source:getCode()}}
	end
end)


-- inherited cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^[Tt]erms inherited from .") then
		return nil
	end
	
	local sourcename = mw.ustring.match(label, "^[Tt]erms inherited from (.+)$")
	local source = require("Module:etymology languages").getByCanonicalName(sourcename) or require("Module:languages").getByCanonicalName(sourcename)
	
	if source then
		return {title = "inherited cat", args = {lang and lang:getCode() or nil, source:getCode()}}
	end
end)


-- borrowed cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^[Tt]erms borrowed from .") then
		return nil
	end
	
	local sourcename = mw.ustring.match(label, "^[Tt]erms borrowed from (.+)$")
	local source
	
	if mw.ustring.find(sourcename, " [Ll]anguages$") then
		sourcename = mw.ustring.gsub(sourcename, " languages$", "")
		source = require("Module:families").getByCanonicalName(sourcename)
	else
		source = require("Module:etymology languages").getByCanonicalName(sourcename) or require("Module:languages").getByCanonicalName(sourcename)
	end
	
	if source then
		return {title = "borrowed cat", args = {lang and lang:getCode() or nil, source:getCode()}}
	end
end)


-- calque cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^[Tt]erms calqued from .") then
		return nil
	end
	
	local sourcename = mw.ustring.match(label, "^[Tt]erms calqued from (.+)$")
	local source
	
	if mw.ustring.find(sourcename, " [Ll]anguages$") then
		sourcename = mw.ustring.gsub(sourcename, " languages$", "")
		source = require("Module:families").getByCanonicalName(sourcename)
	else
		source = require("Module:etymology languages").getByCanonicalName(sourcename) or require("Module:languages").getByCanonicalName(sourcename)
	end
	
	if source then
		return {title = "calque cat", args = {lang and lang:getCode() or nil, source:getCode()}}
	end
end)

-- semantic loan cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^[Ss]emantic loans from .") then
		return nil
	end
	
	local sourcename = mw.ustring.match(label, "^[Ss]emantic loans from (.+)$")
	local source
	
	if mw.ustring.find(sourcename, " [Ll]anguages$") then
		sourcename = mw.ustring.gsub(sourcename, " languages$", "")
		source = require("Module:families").getByCanonicalName(sourcename)
	else
		source = require("Module:etymology languages").getByCanonicalName(sourcename) or require("Module:languages").getByCanonicalName(sourcename)
	end
	
	if source then
		return {title = "semantic loan cat", args = {lang and lang:getCode() or nil, source:getCode()}}
	end
end)

-- translitcatboiler
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^terms transliterated from other languages") then
		return nil
	end
	
	return {title = "translitcatboiler", args = {lang and lang:getCode() or nil}}
end)


-- translitcatboiler
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^[Tt]ransliterations of") then
		return nil
	end
	
	local sourcename = mw.ustring.match(label, "[Tt]ransliterations of (.+) terms")
	local source = require("Module:etymology languages").getByCanonicalName(sourcename) or require("Module:languages").getByCanonicalName(sourcename)
	
	if not lang then
		local lang = ""
	end
	
	if source then
		return {title = "translitcatboiler", args = {lang and lang:getCode(), source:getCode()}}
	end
end)


-- circumfixcat, infixcat, interfixcat, prefixcat, suffixcat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	for _, affixtype in ipairs({"circumfix", "infix", "interfix", "prefix", "suffix"}) do
		if mw.ustring.find(label, "^.+ " .. affixtype .. "ed with .") then
			local pos, after = mw.ustring.match(label, "^(.+) " .. affixtype .. "ed with (.+)$")
			
			if pos == "words" then
				pos = nil
			end
			
			local term, id
			
			if mw.ustring.find(after, ". %([^()]+%)$") then
				term, id = mw.ustring.match(after, "^(.+) %(([^()]+)%)$")
			else
				term = after
			end
			
			return {title = affixtype .. "cat", args = {lang:getCode(), term, pos = pos, id = id}}
		end
	end
end)


table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not lang then
		return nil
	end
	
	for _, nametype in ipairs({"surnames", "male given names", "female given names"}) do
		local sourcename = mw.ustring.match(label, "^" .. nametype .. " from (.+)$")
		
		if sourcename then
			local source = require("Module:languages").getByCanonicalName(sourcename)
			
			if source then
				return {title = "nameboiler", args = {nametype, lang:getCode(), source:getCode()}}
			end
		end
	end
end)


-- charactercat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^terms spelled with .+") then
		return nil
	end
	
	local term = mw.ustring.match(label, "^terms spelled with (.+)$")
	return {title = "charactercat", args = {lang:getCode(), term}}
end)


-- pbcatboiler
table.insert(handlers, function(titleObject)
	if titleObject.text == "Phrasebooks by language" then
		return {title = "pbcatboiler", args = {}}
	else
		local label, lang = splitLabelLang(titleObject)
		
		if label == "phrasebook" then
			return {title = "pbcatboiler", args = {lang:getCode()}}
		elseif mw.ustring.find(label, "^phrasebook/.") then
			label = mw.ustring.match(label, "^phrasebook/(.+)$")
			return {title = "pbcatboiler", args = {lang:getCode(), label}}
		end
	end
end)

-- no entry cat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "entries that don't exist$") then
		return nil
	end
	
	return { title = "no entry cat", args = { lang:getCode() } }
end)

-- poscatboiler
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if lang then
		return {title = "poscatboiler", args = {lang:getCode(), label}}
	elseif mw.ustring.find(label, ". by language$") then
		local label = mw.getContentLanguage():lcfirst(mw.ustring.match(label, "^(.+) by language$"))
		return {title = "poscatboiler", args = {nil, label}}
	end
end)


--[[	famcatboiler
		
		Must go after the "derived", "borrowed", "transliterated", and poscatboiler
		category handlers, which sometimes have "languages" at the end.
]]
table.insert(handlers, function(titleObject)
	if not mw.ustring.find(titleObject.text, "languages$") then
		return nil
	end
	
	local familyName = mw.ustring.match(titleObject.text, "^(.+) languages$")
	
	local family = require("Module:families").getByCanonicalName(familyName) or
		require("Module:families").getByCanonicalName(mw.ustring.lower(familyName))
	
	if not family then
		return nil
	end
	
	return { title = "famcatboiler", args = { family:getCode() } }
end)


-- trredcat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^Fordítások redundáns átírással") then
		return nil
	end
	
	langCode = mw.ustring.match(label, "/(.+)")
	
	if langCode then
		return {title = "trredcat", args = {langCode}}
	end
end)


-- trmandiffcat
table.insert(handlers, function(titleObject)
	local label, lang = splitLabelLang(titleObject)
	
	if not mw.ustring.find(label, "^Fordítások automatikustól eltérő manuális átírással") then
		return nil
	end
	
	local langCode = mw.ustring.match(label, "/(.+)")
	
	if langCode then
		return {title = "trmandiffcat", args = {langCode}}
	end
end)


-- topic cat
table.insert(handlers, function(titleObject)
	return {title = "topic cat", args = {nil, titleObject.text}}
end)


function export.show(frame)
	require("Module:parameters").process(frame:getParent().args, {})
	local titleObject = mw.title.getCurrentTitle()
	
	if titleObject.nsText == "Sablon" then
		return "(This template should be used on pages in the Category: namespace.)"
	elseif titleObject.nsText ~= "Kategória" then
		error("This template/module can only be used on pages in the Category: namespace.")
	end
	
	for _, handler in ipairs(handlers) do
		local t = handler(titleObject)
		
		if t then
			require("Module:debug").track("auto cat/" .. t.title)
			return frame:expandTemplate(t)
		end
	end
end

-- test function for injecting title string
function export.test(title)
	if type(title) == "table" then
		title = title:getParent().args[1]
	end
	
	local titleObject = {}
	titleObject.text = title
	
	for _, handler in ipairs(handlers) do
		local t = handler(titleObject)
		
		if t then
			return t.title
		end
	end	
end

return export