--[[
-- This module is a replacement for {{lang}} and all lang-x templates.
-- It wraps text in an HTML tag containing a lang attribute with a specific
-- language code. This often makes no visible changes to the text, but can help
-- web browsers choose the right font, screen readers use the right
-- pronunciation and more. The plain function wraps the text in the HTML tag,
-- but does not otherwise change the output. The named function adds the name of
-- the language before the text.
--]]

-- Load required modules.
local mLanguageName = require("Module:Language/name")
local yesno = require("Module:Yesno")
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local data = mw.loadData("Module:Language/data/wp languages")

-- Lazily initialise [[Module:Arguments]], as we will only need it sometimes.
-- Not loading it when we don't have to will keep its transclusion count down,
-- which helps to reduce load on the job queue.
local mArguments

-- The p table contains functions that will be available from #invoke and that
-- will be available to other Lua modules (including for use in the test cases).
-- The f table contains functions linking #invoke functions to Lua functions.
local p, f = {}, {}

function f.plain(code, text, options)
	checkType("plain", 1, code, "string")
	checkType("plain", 2, text, "string")
	checkType("plain", 3, options, "table", true)
	options = options or {}
	local name = p._fetchLanguageName(code)
	if not name then
		return nil
	end
	return p._createElement(code, text, name, options)
end

function f.named(code, text, options)
	checkType("named", 1, code, "string")
	checkType("named", 2, text, "string")
	checkType("named", 3, options, "table", true)
	options = options or {}
	local name = p._fetchLanguageName(code)
	if not name then
		return nil
	end
	return string.format(
		"[[%s leid|%s]]: %s",
		name,
		name,
		p._createElement(code, text, name, options)
	)
end

function p._fetchLanguageName(code)
	-- Fetches a language name, given a language code. If no name can be
	-- found, returns nil.
	if not code then
		return nil
	end
	local name = mLanguageName.fuzzy{code = code}
	if name and name ~= "" then
		return name
	end
	return nil
end

function p._createElement(code, text, name, options)
	-- Creates the HTML element.
	-- Check whether a preferred code is specified in the Wikipedia language
	-- data for the input code we are given.
	code = data[code] and data[code][2]	or code
	local tag = options.tagName or "span"
	local root = mw.html.create(tag)
	root
		:attr("lang", code)
		:wikitext(text .. p._categorise(code, name, options))
	return tostring(root)
end

function p._categorise(code, name, options)
	-- Generates the tracking category.

	local function makeCategory(cat)
		-- Makes a category wikilink.
		return string.format("[[%s:%s]]", mw.site.namespaces[14].name, cat)
	end

	local function makeTitle(...)
		-- Get a title object, using pcall to call mw.title.new to
		-- avoid producing script errors. mw.title.new will result
		-- in a script error if we are over the expensive function count
		-- limit, among other fun and exciting scenarios.
		local success, title = pcall(mw.title.new, ...)
		if success then
			return title
		else
			return nil
		end
	end

	-- Don't categorise if nocat is set.
	if yesno(options.nocat, true) then
		return ""
	end

	--[[
	-- Don't categorise if we are not on a content page.
	-- Allow specification of options.page so that this function can be
	-- tested as if it were being run on different pages. This option
	-- isn't provided for Lua functions as it would probably not be used
	-- and would decrease performance slightly.
	--]]
	local titleObj = options.page
		and makeTitle(options.page)
		or mw.title.getCurrentTitle()
	if not titleObj or not titleObj.isContentPage then
		return ""
	end

	-- Assemble the category.
	if code == "sco" then
		return makeCategory(
			"Airticles conteenin expleecitly citit Scots-leid text"
		)
	else
		local categoryName = "Airticles conteenin "
			.. name
			.. "-leid text"
		local categoryTitle = makeTitle(categoryName, 14)
		if categoryTitle and categoryTitle.exists then
			return makeCategory(categoryName)
		else
			return makeCategory(
				"Airticles conteenin non-Scots-leid text"
			)
		end
	end
end

--[=[
-- Make the #invoke functions. The function names are taken from the f table.
-- The #invoke function is named p.funcName, and the function it calls is named
-- f.funcName. If the result of f.funcName(args) is nil, then p.funcName
-- returns the blank string. This allows for use of both Lua-like and
-- wikitext-like idioms should any of the validation checks fail. We use
-- [[Module:Arguments]] to get the arguments from the frame object. We also
-- make the f.funcName functions available to other Lua modules under the name
-- p._funcName.
--]=]
for funcName, func in pairs(f) do
	p[funcName] = function (frame)
		mArguments = require("Module:Arguments")
		local args = mArguments.getArgs(frame)
		-- Get the code and text variables.
		local code = args.code
		local text = args.text
		if not code or not text then
			-- Give up gracefully if we don't have the required input.
			return text or ""
		end
		-- Pass values through to the options table.
		local options = {}
		options.tagName = args.type
		options.nocat = args.nocat
		return f[funcName](code, text, options) or ""
	end
	-- Create the function to access from other Lua modules.
	p["_" .. funcName] = func
end

return p