Open main menu

Implements {{place}}


local export = {}

local data = mw.loadData("Module:place/data")
local m_links = require("Module:links")
local m_langs = require("Module:languages")
local data = require("Module:place/data")

local cat_data = data.cat_data


local namespace = mw.title.getCurrentTitle().nsText

function export.show(frame)
	local params = {
		[1] = {required = true},
		
		-- This is temporary; should become a list parameter eventually
		[2] = {required = true},
		[3] = {},
		[4] = {},
		[5] = {},
		[6] = {},
		[7] = {},
		[8] = {},
		[9] = {},
		
		["t"] = {list = true},

		["a"] = {},
		["also"] = {},
		["def"] = {},
		
		["modern"] = {},
		["official"] = {},
		["capital"] = {},
		["largest city"] = {},
		["caplc"] = {},
	}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	local lang = require("Module:languages").getByCode(args[1]) or error("The language code \"" .. args[1] .. "\" is not valid.")
	local specs = get_specs(args)
	
	return get_def(args, specs) .. get_cats(lang, specs)
end



function get_specs(args)
	local specs = {}
	local c = 2
	local cY = 1
	local cX = 2
	
	while args[c] do
		if args[c] == ";" then
			cY = cY + 1
			cX = 2
		else
			if cX == 2 then
				specs[cY] = {"foobar", mw.text.split(args[c], "/", true)}
			else
				specs[cY][cX] = split_datum(args[c])
				specs[cY] = add_string_param(specs[cY], specs[cY][cX])
			end
			
			cX = cX + 1
		end
		
		c = c + 1
	end
	
	for n, spec in ipairs(specs) do
		local lastarg = table.getn(spec)
		
		for c = 3, lastarg do
			if spec[c][1] and data.implications[spec[c][1]] and data.implications[spec[c][1]][spec[c][2]] then
				spec[table.getn(spec)+1] = data.implications[spec[c][1]][m_links.remove_links(spec[c][2])]			
				spec = add_string_param(spec, data.implications[spec[c][1]][spec[c][2]])
			end
		end
	end
	
	return specs
end


function split_datum(datum)
	datum = mw.text.split(datum, "/", true)
	
	if table.getn(datum) < 2 then
		datum = {nil, datum[1]}
	end

	local links = mw.text.split(datum[2], ":", true)
	
	if table.getn(links) > 1 then
		datum[2] = links[2]
		datum[3] = links[1]
	end

	if datum[1] then	
		if data.placetype_aliases[datum[1]] then
			datum[1] = data.placetype_aliases[datum[1]]
		end

		if data.placename_aliases[datum[1]] and data.placename_aliases[datum[1]][datum[2]] then
			datum[2] = data.placename_aliases[datum[1]][datum[2]]
		end

		if not data.autolink[datum[3] and datum[1]] then
			datum[3] = "en"
		end
	end
	return datum
end


function add_string_param(spec, param)
	if not param[1] then
		return spec
	end
	
	if not spec[param[1]] then
		spec[param[1]] = {param[2]}
	else
		spec[param[1]][table.getn(spec[param[1]]) + 1] = param[2]
	end
	
	return spec
end



-------- Definition-generating functions


-- Returns the definition line.
function get_def(args, specs)
	if #args["t"] > 0 then
		return get_translations(args["t"]) .. " (" .. get_gloss(args, specs, false) .. ")"
	else
		return get_gloss(args, specs, true)
	end
end


-- Returns a string with the gloss (the description of the place itself, as
-- opposed to translations). If sentence == true, the gloss’s first letter is
-- made upper case and a period is added to the end.
function get_gloss(args, specs, sentence)
	if args["def"] then
		return args["def"]
	end
	
	local glosses = {}
	
	for n1, spec in ipairs(specs) do
		local gloss = ""
		
		for n2, placetype in ipairs(spec[2]) do
			if placetype == "and" then
				gloss = gloss .. " and "
			else
				if n2 > 1 and spec[2][n2-1] ~= "and" then
					gloss = gloss .. ", "
					
					if cat_data[placetype] ~= nil and cat_data[placetype].article == "the" then
						gloss = gloss .. "the "
					end
				end
				
				if cat_data[placetype] ~= nil and cat_data[placetype].real_name ~= nil then
					gloss = gloss .. cat_data[placetype].real_name
				else
					gloss = gloss .. placetype
				end
			end
		end
		
		if args["also"] then
			gloss = gloss .. " and " .. args["also"]
		end
		
		local c = 3
		
		while spec[c] do
			local prev = nil
			
			if c > 3 then
				prev = spec[c-1]
			else
				prev = {}
			end
			
			gloss = gloss .. get_description(spec[2][table.getn(spec[2])], prev, spec[c], spec[c+1], (c == 3))
			c = c + 1
		end
		
		table.insert(glosses, gloss)
	end
	
	local ret = {(args["a"] or get_article(specs[1][2][1], sentence)) .. " " .. table.concat(glosses, "; ")}
	
	table.insert(ret, get_extra_info("modern", args["modern"], false))
	table.insert(ret, get_extra_info("official name:", args["official"], sentence))
	table.insert(ret, get_extra_info("capital:", args["capital"], sentence))
	table.insert(ret, get_extra_info("largest city:", args["largest city"], sentence))
	table.insert(ret, get_extra_info("capital and largest city:", args["caplc"], sentence))

	if (sentence == true) then
		table.insert(ret, ".")
	end
	
	return table.concat(ret)
end


-- Returns a string with extra information that is sometimes added to a
-- definition. This consists of the tag, a whitespace and the value (wikilinked
-- if it language contains a language code; if sentence == true, ". " is added
-- before the string and the first character is made upper case.
function get_extra_info(tag, value, sentence)
	if not value then
		return ""
	end
	
	value = mw.text.split(value, ":", true)
	
	if table.getn(value) < 2 then
		value = {nil, value[1]}
	end
	
	local s = ""
	
	if sentence then
		s = s .. ". " .. upper_case(tag)
	else
		s = s .. "; " .. tag
	end
	
	return s .. " " .. link(value[2], value[1])
end


-- returns a string containing a placename, with an extra article if necessary
-- and wikilinked if necessary.
-- Example: ({"country", "United States", "en"}, true, true) returns "the {{l|en|United States}}"
function get_place_string(place, needs_article, needs_links)
	local ps = place[2]
	
	if needs_links then
		ps = link(place[2], place[3])
	end
	
	if needs_article then
		if place[1] and data.place_article[place[1]] and data.place_article[place[1]][place[2]] then
			ps = data.place_article[place[1]][place[2]] .. " " .. ps
		end
	end
	
	return ps
end

-- Returns a special description generated from a synergy table fetched from
-- the data module and two place tables.
function get_synergic_description(synergy, place1, place2)
	local desc = ""

	if place1 then
		
		if synergy.before then
			desc = desc .. " " .. synergy.before
		end
		
		desc = desc .. " " .. get_place_string(place1, true, true)
	end
	
	if synergy.between then
		desc = desc .. " " .. synergy.between
	end
	
	desc = desc .. " "  .. get_place_string(place2, true, true)
	
	if synergy.after then
		desc = desc .. " " .. synergy.after
	end
	
	return desc
end


-- Returns a string that contains the information of how a given place (place2)
-- should be formatted in the gloss, considering the entry’s place type, the 
-- place preceding it in the template’s parameter (place1) and following it 
-- (place3), and whether it is the first place (parameter 3 of the template)
function get_description(entry_placetype, place1, place2, place3, first)
	local desc = ""
	
	local synergy = get_synergy_table(place2, place3)
	
	if synergy then
		return ""
	end
	
	synergy = get_synergy_table(place1, place2)
	
	if first then
		if place2[1] then
			desc = desc .. get_in_or_of(entry_placetype, "")
		else
			desc = desc .. " "
		end
	else
		if not synergy then
			if place1[1] and place2[2] ~= "and" and place2[2] ~= "in" then
				desc = desc .. ","
			end
			
			desc = desc .. " "
		end
	end
	
	if not synergy then
		desc = desc .. get_place_string(place2, first, true)
	else
		desc = desc .. get_synergic_description(synergy, place1, place2)
	end
	

	return desc
end


-- Returns a string with the wikilinks to the English translations of the word.
function get_translations(transl)
	local ret = {}
	
	for _, t in ipairs(transl) do
		if t:find("[[", nil, true) then
			table.insert(ret, t)
		elseif t == mw.title.getCurrentTitle().prefixedText then
			table.insert(ret, "[[#English|" .. t .. "]]")
		else
			table.insert(ret, "[[" .. t .. "]]")
		end
	end
	
	return table.concat(ret, "; ")
end


-- Returns the preposition that should be used between the placetypes place1 and
-- place2 (i.e. "city >in< France.", "country >of< South America"
-- If there is no place2, a single whitespace is returned. Otherwise, the
-- preposition is fetched from the data module. If there isn’t any, the default
-- is "in".
-- The preposition is return with a whitespace before and after.
function get_in_or_of(place1, place2)
	if not place2 then
		return " "
	end
	
	local preposition = "in"
	
	if cat_data[place1] and cat_data[place1].preposition then
		preposition = cat_data[place1].preposition
	end
	
	return " " .. preposition .. " "
end





---------- Functions for the category wikicode


-- Iterates through each type of place given in parameter 2 and returns a string
-- with the links to all categories that need to be added to the entry. 
function get_cats(lang, specs)
	local cats = ""
	
	for n1, spec in ipairs(specs) do
		for n2, placetype in ipairs(spec[2]) do
			if placetype ~= "and" then
				cats = cats .. get_cat(lang, spec, placetype)
			end
		end
	end
	
	return cats
end


-- Returns the category wikicode that should be added to the entry, given the
-- type of place and the other arguments.
function get_cat(lang, spec, placetype)
	local cat = ""
	local pt_data = cat_data[placetype]
	
	if not pt_data then
		return ""
	end
	
	local real_name = pt_data.real_name or placetype
	
	local data
	local c
	local itself
	
	data, c, itself = find_data(placetype, pt_data, spec)
	
	if not data then
		return ""
	end
	
	
	if itself and type(data[1]) == "string" then
		return catlink(lang, data[1])
	end
	
	if c > 2 then
		cat = get_possible_cat(lang, data, placetype, spec[c], real_name, spec, itself)
		
		if cat ~= "" then
			local c2 = 2
			
			while spec[spec[c][1]][c2] do
				cat = cat .. get_possible_cat(lang, data, placetype, {spec[c][1], spec[spec[c][1]][c2]}, real_name, spec, itself)
				c2 = c2 + 1
			end
		end
	else
		for n, name in ipairs(data) do
			if name then
				name = get_plural(placetype)
			end
			
			cat = cat .. catlink(lang, name)
		end
	end
	
	return cat
end


function find_data(placetype, pt_data, spec)
	local inner_data = nil
	
	local c = 3
	
	while spec[c] and not inner_data do
		inner_data = pt_data[(spec[c][1] or "") .. "/" .. spec[c][2]]
		c = c + 1
	end
	
	c = c - 1
	
	if not inner_data then
		inner_data = pt_data["default"]
		c = -1
	end
	
	local c2 = 3
	
	while spec[c2] do
		if inner_data[spec[c2][1]] then
			return inner_data[spec[c2][1]], c2, false
		end
		
		c2 = c2 + 1
	end
	
	return inner_data["itself"], c, true
end


function get_possible_cat(lang, data, entry_placetype, arg, real_name, spec, itself)
	if not data or not entry_placetype or not arg or not real_name or not spec then
		return ""
	end
	
	local all_cats = ""
	local cat = ""

	local place = arg[1]
	
	for n, name in ipairs(data) do
		if name then
			name = get_plural(real_name) .. get_in_or_of(entry_placetype, place)
		end
		
		if type(data[1]) == "string" then
			name = data[1]
		end
		
		cat = cat .. name
		
		if not itself then
			cat = cat .. get_place_string(arg, true, false)
		end

		if (place == "state" or place == "province" or place == "region") and spec["country"] then
			cat = cat .. ", " .. spec["country"][1]
		end
		
		all_cats = all_cats .. catlink(lang, cat)
	end
	
	return all_cats
end


-- Returns the category link for a category, given the language code and the
-- name of the category.
function catlink(lang, text)
	return require("Module:utilities").format_categories({lang:getCode() .. ":" .. m_links.remove_links(text)}, lang)
end





----------- Wikicode utility functions



-- returns a wikilink link {{l|language|text}}
function link(text, language)
	if not language or language == "" then
		return text
	end
	
	return m_links.full_link({term = text, lang = m_langs.getByCode(language)}, nil, true)
end




---------- Basic utility functions

-- Returns an input string with its first letter in upper case
function upper_case(word)
	return string.upper(string.sub(word, 1, 1)) .. string.sub(word, 2)
end



-- Returns the plural of a word and makes its first letter upper case.
-- The plural is fetched from the data module; if it doesn’t find one,
-- the default is <word> + s
function get_plural(word)
	word = cat_data[word].plural or (word .. "s")
	
	return upper_case(word)
	
end


-- Fetches the synergy table from cat_data, which describes the format of
-- glosses consisting of <placetype1> and <placetype2>.
-- The parameters are tables in the formar {placetype, placename, langcode}.
function get_synergy_table(place1, place2)
	if not place2 or not cat_data[place2[1]] then
		return nil
	end
	
	if not cat_data[place2[1]].synergy then
		return nil
	end
	
	if not place1 then
		place1 = {}
	end
	
	return cat_data[place2[1]].synergy[place1[1]] or cat_data[place2[1]].synergy["default"]
end


-- Returns the article that is used with a word. It is fetched from the cat_data
-- table; if that doesn’t exist, "an" is given for words beginning with a vowel
-- and "a" otherwise.
-- If sentence == true, the first letter of the article is made upper-case.
function get_article(word, sentence)
	local art = ""

	if cat_data[word] and cat_data[word].article then
		art = cat_data[word].article
	else
		local l = string.sub(word, 1, 1)
		art = "a"

		if l == "a" or l == "e" or l == "i" or l == "o" or l == "u" then
			art = art .. "n"
		end
	end
	
	if sentence then
		art = upper_case(art)
	end
	
	return art
end


return export