--[=[

Implement Template:Wiktionary.

Things that need doing:
	better error messaging [done]
	categorize:
		templates that use article title as <term> (no parameters)
		templates that have <term#anchor>
		templates with terms that have <term> without <tag>
		templates with errors
	special ISO 639-3 tags that are supported by ((lang))?
		mis – uncoded languages; not known to wiktionary
		mul – multiple languages; known to wiktionary as 'Translingual'
		und – undetermined; not known to wiktionary; used by this module to indicate the language of a <term> without language tag prefix
		zxx – no linguistic content / not applicable; not known to wiktionary
	Wiktionary has [[Wiktionary:List of languages]], a wikitext list of language tags and their associated names.
		Not all of these tags appear in [[:en:Module:Language/data]].  For example, these language tags are not in
		Module:Language/data (as of 2023-12-13) but are in Wiktionary:List of languages:
			ca – Catalan
			nl – Dutch
			enm – Middle English
			fro – Old French
		There does not appear to be a mechanism that copies the appropriate data from Wiktionary to en.wiki so Module:Language/data
		grows organically; ugh.  It may be necessary to extract a list of currently supported ISO 639-1 tags from
		wikt:Wiktionary:List_of_languages#Two-letter_codes to make sure that at the least those are part of Module:Language/data
		[list extracted from wikt:Module:languages/data/2 as Module:Sandbox/Trappist_the_monk/Wiktionary/data]
	Support language tag redirects in Module:Language/data [done]
]=]

require ('strict');
local local_wiki_tag = mw.language.getContentLanguage().code;					-- 'en' at enwiki

--[[--------------------------< E R R O R _ S I D E _ B O X >--------------------------------------------------

Return rendered ((wiktionary)) side box with error message content.  This function can likely go away if this or
a similar module is adopted for creating the side box content.

((Sister project
|position=(({position|))}
|project=wiktionary
|text=((#invoke:<whatever the module is called>|main)) <!-- creates side box content -->
))

]]

local function error_side_box (args_t, message, categories_t, frame)
	return frame:expandTemplate ({title = 'Sister project', args = {			-- render the ((wiktionary)) template
		position = args_t.position,												-- defaults to right
		project = 'wiktionary',													-- to select appropriate image
		text = string.format ('<span style="color:#d33">%s: %s</span>',
			'<tt>(([[Template:Wiktionary|Wiktionary]]))</tt> error: ',
			message);															-- render the error message
		))) .. table.concat (categories_t);										-- with categories
	end


--[[--------------------------< S I D E _ B O X >--------------------------------------------------------------

Return rendered ((wiktionary)) side box.  This function can likely go away if this or a similar module is adopted
for creating the side box content.

((Sister project
|position=(({position|))}
|project=wiktionary
|text=((#invoke:<whatever the module is called>|main)) <!-- creates side box content -->
))

]]

local function side_box (args_t, items_t, categories_t, frame)
	local links_t = {};															-- holds a list of interwiki-linked terms
	for i, item_t in ipairs (items_t) do										-- build an interwiki link to wiktionary
		local title = item_t.term;												-- wiktionary article title
		if item_t.anchor then
			title = title .. item_t.anchor;										-- add a language-specific anchor if language tag or #anchor supplied in template call
		end
		links_t[i] = string.format ('[[wiktionary:%s%s|%s]]',					-- create interwiki link for each individual term
			(item_t.isSearch and "Special:Search/" or ""),
			title,																-- wiktionary article title + anchor
			item_t.lang_text													-- term wrapped in ((lang))-provided markup	for display
			);
	end
	local join1 = ', ';															-- create appropriate separators for the list of terms
	local join2 = #items_t > 2 and ',&nbsp;or ' or '&nbsp;or ';

	return frame:expandTemplate ({title = 'Sister project', args = {			-- render the ((wiktionary)) template
		position = args_t.position,												-- defaults to right
		project = 'wiktionary',													-- to select appropriate image
		text = string.format ('Look up %s in Wiktionary, the free dictionary.',	-- construct the text the goes inside the box
			mw.text.listToText (links_t, join1, join2)							-- concatenate the list of terms
			)
		))) .. table.concat (categories_t);										-- add categories
end


--[[--------------------------< C A T E G O R Y _ A D D >------------------------------------------------------

returns nothing.  Adds category wikilink to <categories_t> if the template is used in mainspace

TODO: categorize templates that have <term> without <tag>?  On initial implementation of this module, that would
	be every instance of the template so impractical at that time

]]

local added_cats_t = {};

local function category_add (category, categories_t)
	if not mw.title.getCurrentTitle():inNamespace (0) then						-- when not in mainspace
		return;																	-- abandon
	end
	
	if added_cats_t[category] then												-- when we've already added this category
		return;																	-- abandon
	end
	
	local category_names_t = {													-- a list of category names used by this module
		['error'] = 'Wiktionary template errors',
		['article title'] = 'Wiktionary template uses article title',
		['anchor'] = 'Wiktionary template uses manual anchor',
	--	['no tag'] = 'Wiktionary template without language tag',				-- see TODO above
		}

	table.insert (categories_t, table.concat ({									-- construct category wikilink
		'[[Category:',
		category_names_t[category],
		']]'
	}));

	added_cats_t[category] = true;												-- remember that we've added this category to <categories_t>
end


--[[--------------------------< S E T _ A N C H O R >----------------------------------------------------------

create a language-specific anchor (URI fragment) where the fragment is the wiktionary language name associated
with the language tag prefixed to the 'term'.  

]]

local function set_anchor (item_t)
	local wikt_lang_data = mw.loadData ('Module:Language/data');				-- load wikitionary supported languages list

	if not item_t.anchor then
		if wikt_lang_data.redirects[item_t.tag] then
			item_t.tag = wikt_lang_data.redirects[item_t.tag];
		end
		local data_t = wikt_lang_data.languages[item_t.tag];					-- primary wiktionary data source

		local mw_lang_name;														-- holds MediaWiki name for language tag
		if not ({['mis'] = true, ['mul'] = true, ['zxx'] = true})[item_t.tag] then		-- these are supported by MediaWiki but have no value here
			mw_lang_name = mw.language.fetchLanguageName (item_t.tag, local_wiki_tag);	-- get MediaWiki's language name as fallback
		end
		
		if data_t and data_t.name then											-- if wiktionary has name for language tag <item.tag>; not present for default tag 'und'
			item_t.anchor = mw.uri.anchorEncode ('#' .. data_t.name);			-- make an encoded anchor from the wiktionary language name so that it matches the language tag from the template call
		elseif mw_lang_name and (mw_lang_name ~= item_t.tag) and ('und' ~= item_t.tag) then	-- fetchLanguageName() returns tag if tag not supported
			item_t.anchor = mw.uri.anchorEncode ('#' .. mw_lang_name);			-- make an encoded anchor from the MediaWiki language name so that it matches the language tag from the template call
		elseif 'und' ~= item_t.tag then											-- when neither wikitionary nor MediaWiki have a definition for <item_t.tag> ('und' excepted)
			item_t.error = 'language tag "' .. item_t.tag .. '" is not known to wiktionary';
			return;																-- abandon
		end
	end

	if '' == item_t.anchor then													-- if anchor is set to empty string
		item_t.anchor = nil;													-- unset so we don't create a link with trailing '#' fragment marker
	end
end


--[[--------------------------< M A I N >----------------------------------------------------------------------

Return wikitext to display ((wiktionary)) box.

]]

local function main (frame)
	local args_t = require ('Module:Arguments').getArgs (frame);

	local items_t = {};
	local categories_t = {};
	local lang_mod = require ('Module:lang');									-- for direct access to the functions in Module:Lang
	local lang_text;															-- term and markup returned from Module:Lang.lang
	
	for _, arg in ipairs (args_t) do
		arg = mw.text.trim (arg);
		if arg ~= '' then
			local tag, term, anchor;
			if arg:match ('^%a%a%a?:.+') then									-- does this arg have language tag prefix and a term?
				tag, term = arg:match ('^(%a%a%a?):(.*)');						-- yep, then extract them
			else
				term = arg;														-- nope, just a term
			end

			if term:match ('.+#.+') then										-- does <term> hav an anchor (URL fragment)?
				term, anchor = term:match ('(.+)(#.+)');						-- split the 'term#ancor' into <term> and <anchor>
				category_add ('anchor', categories_t);							-- add a category
			end
			table.insert (items_t, {tag = tag or 'und', term = term, anchor = anchor});
		end
	end

	if items_t[1] then
		for _, item_t in ipairs (items_t) do
			local italic = (item_t.tag == local_wiki_tag) and 'yes' or nil;		-- local language same as language tag, italicize per words-as-words
			lang_text = lang_mod._lang ({item_t.tag, item_t.term, cat = 'no', italic = italic});		-- let ((lang)) control italics
			if lang_text:match ('class="error"') then
				category_add ('error', categories_t);							-- add a category
				return table.concat ({
					error_side_box (args_t, 'language tag "' .. item_t.tag .. '" is not known to wikipedia', categories_t, frame),
					(mw.title.getCurrentTitle():inNamespace (0) and '[[Category:Wiktionary template errors]]') or ''
				});																-- abandon with error message
			end
			item_t.lang_text = lang_text;
			set_anchor (item_t);
			if item_t.error then
				category_add ('error', categories_t);							-- add a category
				return table.concat ({											-- abandon with error message
					error_side_box (args_t, item_t.error, categories_t, frame),
					(mw.title.getCurrentTitle():inNamespace (0) and '[[Category:Wiktionary template errors]]') or ''
				});																-- abandon with error message
			end
		end
	else																		-- no terms supplied in template call so use article title
		local title = mw.ustring.lower(mw.title.getCurrentTitle().subpageText);	-- get the title; TODO: should we be setting page title to lowercase?
		lang_text = lang_mod._lang ({local_wiki_tag, title, cat = 'no', italic = 'yes'});	-- for consistency use ((lang)) for article title; italic per words-as-words
		items_t[1] = {term = title, lang_text = lang_text, isSearch = true };	-- don't break side box
		category_add ('article title', categories_t);							-- add a category
	end
	return side_box (args_t, items_t, categories_t, frame);
end


--[[--------------------------< E X P O R T E D   F U N C T I O N S >------------------------------------------
]]

return {
	main = main
	}