local export = {}


function no_rule_error(params) -- Intentionally global; better way to do this?
	return error(('No rule for "%s" in language "%s".')
		:format(params.form, params.lang), 2)
end


messages = require("Module:array")() -- intentionally global


local function default_entry(params)
	local entry = {
		pronunc = nil,
		pos_header = mw.getContentLanguage():ucfirst(params.pos),
		head =
			"{{head|" .. params.lang .. "|" .. params.pos .. " form" ..
			(params.target ~= params.target_pagename and '|head=' .. params.target or "") ..
			(params.transliteration and "|tr=" .. params.transliteration or "") ..
			(params.gender and "|g=" .. params.gender or "") ..
			"}}",
		def =
			"{{inflection of|" ..
			params.origin ..
			(params.origin_transliteration and "|tr=" .. params.origin_transliteration or "") ..
			"||" .. params.form ..
			"|lang=" .. params.lang ..
			"}}",
		inflection = nil,
		declension = nil,
		conjugation = nil,
		mutation = nil,
	}
	
	-- Exceptions for some forms
	local templates = {
		["p"] = "plural of",
		["f"] = "feminine of",
		["n"] = "neuter of",
		["f|s"] = "feminine singular of",
		["m|p"] = "masculine plural of",
		["f|p"] = "feminine plural of",
	}
	
	if params.form == "comparative" or params.form == "superlative" then
		entry.head =
			"{{head|" .. params.lang .. "|" .. params.form .. " " .. params.pos ..
			(params.target ~= params.target_pagename and "|head=" .. params.target or "") ..
			(params.gender and "|g=" .. params.gender or "") ..
			"}}"
		entry.def =
			"{{" .. params.form .. " of" ..
			"|" .. params.origin ..
			(params.origin_transliteration and "|tr=" .. params.origin_transliteration or "") ..
			(params.pos ~= "adjective" and "|POS=" .. params.pos or "") ..
			"|lang=" .. params.lang ..
			"|nocat=1}}"
	elseif params.form == "equative" then
		entry.head =
			"{{head|" .. params.lang .. "|" .. params.pos .. " " .. params.form .. " form" ..
			(params.target ~= params.target_pagename and "|head=" .. params.target or "") ..
			(params.gender and "|g=" .. params.gender or "") ..
			"}}"
		entry.def =
			"{{" .. params.form .. " of" ..
			"|" .. params.origin ..
			(params.origin_transliteration and "|tr=" .. params.origin_transliteration or "") ..
			(params.pos ~= "adjective" and "|POS=" .. params.pos or "") ..
			"|lang=" .. params.lang ..
			"|nocat=1}}"
	elseif templates[params.form] then
		entry.def =
			"{{" .. templates[params.form] ..
			"|" .. params.origin ..
			(params.origin_transliteration and "|tr=" .. params.origin_transliteration or "") ..
			"|lang=" .. params.lang ..
			"}}"
	end
	
	return entry
end

-- Merges multiple entries into one if they differ only in the definition
local function merge_entries(entries)
	local entries_new = {}
	
	for i, entry in ipairs(entries) do
		local last_entry = entries_new[#entries_new]
		
		if last_entry and
			entry.pronunc == last_entry.pronunc and
			entry.pos_header == last_entry.pos_header and
			entry.head == last_entry.head and
			entry.inflection == last_entry.inflection and
			entry.declension == last_entry.declension and
			entry.conjugation == last_entry.conjugation then
			
			local params1 = mw.ustring.match(last_entry.def, "^{{inflection of|([^{}]+)}}$")
			local params2 = mw.ustring.match(entry.def, "^{{inflection of|([^{}]+)}}$")
			
			last_entry.def = last_entry.def .. "\n# " .. entry.def
			
			-- Do some extra-special merging with "inflection of"
			if params1 and params2 then
				-- Find the last unnamed parameter of the first template
				params1 = mw.text.split(params1, "|", true)
				local last_numbered_index
				
				for j, param in ipairs(params1) do
					if not mw.ustring.find(param, "=", nil, true) then
						last_numbered_index = j
					end
				end
				
				-- Add grammar tags of the second template
				params2 = mw.text.split(params2, "|")
				local tags = {}
				local n = 0
				
				for k, param in ipairs(params2) do
					if not mw.ustring.find(param, "=", nil, true) then
						n = n + 1
						
						-- Skip the first two unnamed parameters,
						-- which don't indicate grammar tags
						if n >= 3 then
							-- Now append the tags
							table.insert(tags, param)
						end
					end
				end
				
				-- Add the new parameters after the existing ones
				params1[last_numbered_index] = params1[last_numbered_index] .. "|;|" .. table.concat(tags, "|")
				last_entry.def = "{{inflection of|" .. table.concat(params1, "|") .. "}}"
			end
		else
			table.insert(entries_new, entry)
		end
	end
	
	return entries_new
end

local function entries_to_text(entries, lang)
	lang = require("Module:languages").getByCode(lang) or require("Module:languages").err(lang, "lang")
	
	for i, entry in ipairs(entries) do
		entry =
			(entry.pronunc and "===Pronunciation===\n" .. entry.pronunc .. "\n\n" or "") ..
			"===" .. entry.pos_header .. "===\n" ..
			entry.head .. "\n\n" ..
			"# " .. entry.def ..
			(entry.inflection and "\n\n====Inflection====\n" .. entry.inflection or "") ..
			(entry.declension and "\n\n====Declension====\n" .. entry.declension or "") ..
			(entry.conjugation and "\n\n====Conjugation====\n" .. entry.conjugation or "") ..
			(entry.mutation and "\n\n===Mutation===\n" .. entry.mutation or "")
		
		entries[i] = entry
	end
	
	return "==" .. lang:getCanonicalName() .. "==\n\n" .. table.concat(entries, "\n\n")
end

function export.generate(frame)
	local fparams = {
		lang            = {required = true},
		origin_pagename = {required = true},
		target_pagename = {required = true},
		num             = {required = true, type = "number"},
		
		pos                    = {list = true, allow_holes = true},
		form                   = {list = true, allow_holes = true},
		gender                 = {list = true, allow_holes = true},
		transliteration        = {list = true, allow_holes = true},
		origin                 = {list = true, allow_holes = true},
		origin_transliteration = {list = true, allow_holes = true},
		target                 = {list = true, allow_holes = true},
	}
	
	local args = require("Module:parameters").process(frame.args, fparams)
	
	local entries = {}
	
	-- Generate each entry
	for i = 1, args.num do
		local params = {
			lang = args.lang,
			origin_pagename = args.origin_pagename,
			target_pagename = args.target_pagename,
			
			pos = args.pos[i] or error("The argument \"pos\" is missing for entry " .. i),
			form = args.form[i] or error("The argument \"form\" is missing for entry " .. i),
			gender = args.gender[i],
			transliteration = args.transliteration[i],
			origin = args.origin[i] or error("The argument \"origin\" is missing for entry " .. i),
			origin_transliteration = args.origin_transliteration[i],
			target = args.target[i],
		}
		
		params.form = mw.ustring.gsub(params.form, "|", "|")
		
		-- Make a default entry
		local entry = default_entry(params)
		
		-- Try to use a language-specific module, if one exists
		local success, lang_module = pcall(require, "Module:accel/" .. args.lang)
		
		if success then
			lang_module.generate(params, entry)
		end
		
		-- Add it to the list
		table.insert(entries, entry)
	end
	
	-- Merge entries if possible
	entries = merge_entries(entries)
	entries = entries_to_text(entries, args.lang)
	
	return entries
end


function export.generate_JSON(frame)
	local success, entries = pcall(export.generate, frame)
	
	-- If success is false, entries is an error message.
	local ret = { [success and "entries" or "error"] = entries, messages = messages }
	
	return require("Module:JSON").toJSON(ret)
end


return export
"https://si.wiktionary.org/w/index.php?title=Module:accel/sandbox&oldid=34041" වෙතින් සම්ප්‍රවේශනය කෙරිණි