A documentação para este módulo pode ser criada na página Módulo:Infobox/doc

-- Modulo per implementare le funzionalità di infobox
local p = {} -- per l'esportazione delle funzioni del modulo

local args = {}-- variabile che contiene gli argomenti passati al template
local origArgs
local root -- radice del markup html
local dump = {}

local function checkList(valore)
	-- Permette al software Mediawiki di gestire le liste # o *
	local c = mw.ustring.sub(valore, 1, 1)
	if c == '#' or c == '*' then
		valore = '<div>\n' .. valore .. '\n</div>'
	end
	return valore
end

local function getArgNums(...)
	-- Restituisce una lista che contiene il suffisso numerico  di tutti gli argomenti
	-- che iniziano con il prefisso "prefix"
	-- Per esempio se nella lista argomenti sono valorizzati "Valore1, Valore2 e Valore4"
	-- retistuirà la lista [1, 2, 4]
	local prefixs = {...}
	local nums = {}
	for k, _ in pairs(args) do
		local num = nil
		for _, candidate in ipairs(prefixs) do
			num = ('' .. k):match('^' .. candidate .. '(%d+)$')
			if num ~= nil then break end
		end
		if num then table.insert(nums, tonumber(num)) end
	end
	table.sort(nums)
	return nums
end

local function addRow(rowArgs)
	-- Aggiunge una riga alla tabella
	-- Se rowArgs.gruppo non è nullo la considera come una riga di testata di gruppo
	-- e ignora eventuali valorizzazioni di rowArgs.valore
	if rowArgs.gruppo then
		root
			:tag('tr')
				:addClass("sinottico_divisione")
				:tag('th')
					:attr('colspan', 2)
					:cssText(rowArgs.stile or args.StileGruppo or '')
					:wikitext(rowArgs.gruppo)
	-- Altrimenti se rowArgs.valore non è nullo inserisce una riga dati, verificando
	-- se esiste o meno la testata
	elseif rowArgs.valore then
		local row = root:tag('tr')
		local dataCell
		if rowArgs.nome then
			row
				:tag('th')
					:cssText(args.StileNome or '')
					:wikitext(rowArgs.nome)
			dataCell = row:tag('td')
		else
			dataCell = row:tag('td')
				:attr('colspan', 2)
				:css('text-align', 'center')
		end
		dataCell
			:addClass(rowArgs.classe or '')
			:cssText(args.StileValore or '')
			:wikitext(checkList(rowArgs.valore))
	end
end

local function renderTitle()
	local suptitle = mw.html.create('')
	if args.SopraTitolo then
		suptitle
			:tag('span')
				:addClass('sinottico_sottotitolo')
				:cssText(args.StileSopraTitolo or '')
				:wikitext(args.SopraTitolo)
				:done()
			:tag('br'):done()
	end
	local subtitle = mw.html.create('')
	if args.SottoTitolo then
		subtitle
			:tag('br'):done()
			:tag('span')
				:addClass('sinottico_sottotitolo')
				:cssText(args.StileSottoTitolo or '')
				:wikitext(args.SottoTitolo)
	end
	if args.TitoloEst then
		root
			:tag('caption')
			:addClass('sinottico_testata')
			:css("font-weight", "bold")
			:cssText(args.StileTitoloEst or '')
			:node(suptitle)
			:wikitext(args.TitoloEst)
			:node(subtitle)
	elseif args.TitoloInt then
		root
			:tag('tr')
			:addClass('sinottico_testata')
			:tag('th')
				:attr('colspan', '2')
				:node(suptitle)
				:cssText(args.StileTitoloInt or '')
				:wikitext(args.TitoloInt)
				:node(subtitle)
	end
end

local function renderImage()
	if not args.Immagine then return end
	local cell_immagine = mw.html.create('td')
	cell_immagine
		:addClass(args.ClasseImmagine or '')
		:attr('colspan', '2')
		:css('text-align', 'center')
		:cssText(args.StileImmagine or '')
		:wikitext(args.Immagine)
	 if args.Didascalia then
		cell_immagine
			:tag('br', {selfClosing = true})
				:done()
			:tag('span')
			:cssText(args.StileDidascalia or '')
			:wikitext(args.Didascalia)
	end
	root:tag('tr'):node(cell_immagine)
end


local function renderRows()
	local rownums = getArgNums('Valore', 'GruppoOpzionale',  'Gruppo')
	for k, num in ipairs(rownums) do
		local skip = false
		if args['GruppoOpzionale' .. num] ~= nil then
			skip = true
			for j = k+1, #rownums do
				if args['Gruppo' .. rownums[j]] ~= nil or args['GruppoOpzionale' .. rownums[j]]~=nil then break end
				if args['Valore' .. rownums[j]] ~= nil then
					skip = false
					break
				end
			end
		end
		if not skip and args['GruppoOpzionale' .. num] ~= '$fine' then
			addRow({
				gruppo = args['GruppoOpzionale' .. num] or args['Gruppo' .. num],
				nome = args['Nome' .. num],
				valore = args['Valore' .. num],
				classe = args['Classe' .. num],
				stile = args['GruppoStile' .. num]
			})
		end
	end
end

local function renderLastRow()
	if not args.Ultima then return end
	root
		:tag('tr')
			:tag('td')
				:attr('colspan', '2')
				:addClass('sinottico_piede')
				:cssText(args.StileUltima or '')
				:wikitext(args.Ultima)
				:newline()
end

local function renderNavBar()
	if not args.NomeTemplate then return end
	root
		:tag('tr')
			:tag('td')
				:addClass('sinottico_piede2 noprint metadata')
				:attr('colspan', '2')
				:wikitext(mw.getCurrentFrame():expandTemplate({
					title = 'Link sinottico',
					args = args.LinkWikidata and
							{ args.NomeTemplate } or
							{ args.NomeTemplate, nowd = 1 }
				}))
end

local function _infobox()
	-- Crea l'albero html che rappresenta la tabella del sinottico e restituisce il markup
	if args.CreaTable == 'no' then
		root = mw.html.create('')
	else
		root = mw.html.create('table')
		root
			:addClass('sinottico')
			:cssText(args.StileTabella or '')
			:attr('summary', args.Summary or 'Tabella sinottica che riassume i principali dati del soggetto')
	end
	renderTitle()
	renderImage()
	renderRows()
	renderLastRow()
	renderNavBar()
	return tostring(root)
end

local function preprocessSingleArg(argName)
	-- Se l'argomento esiste e non è una stringa vuota lo aggiunge alla tabella degli argomenti
	-- Argomenti uguali a stringa vuota sono trattati come nulli come da comportamento
	-- precedente del template {{Infobox}}
	if origArgs[argName] and origArgs[argName] ~= '' then
		args[argName] = origArgs[argName]
	end
end

local function preprocessArgs(prefixTable, step)
	-- Assegna i parametri con i dati prefissi alla tabella args, in ordine e secondo lotti di
	-- dimensione specificata. La prefixTable dovrebbe essere un  array contenente tabelle, ognuna
	-- delle quali con due possibili campi, una stringa "prefisso" e una tabella di "dipendenze". La
	-- funsione esamina tutti i parametri contenenti la stringa prefisso, ma esamina quelli della
	-- tabella dipendenti solo se il prefisso da cui dipendono è presente e non nullo.
	if type(prefixTable) ~= 'table' then
		error("Valore non tabella trovato nella tabella prefissi", 2)
	end
	if type(step) ~= 'number' then
		error("Passo di tipo non valido", 2)
	end

	-- Ottiene gli argmenti senza un suffisso numerico e controlla per input errati.
	for i,v in ipairs(prefixTable) do
		if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
			error('Valori non validi riscontrati per la tabella di prefissi preprocessArgs', 2)
		end
		preprocessSingleArg(v.prefix)
		-- Esamina i parametri dipendenti solo se il parametro prefisso è presente e non nullo.
		if args[v.prefix] and v.depend then
			for j, dependValue in ipairs(v.depend) do
				if type(dependValue) ~= 'string' then
					error('Parametro "dipendente"  non valido riscontrato in preprocessArgs')
				end
				preprocessSingleArg(dependValue)
			end
		end
	end
	if step == 0 then return end
	-- Estrae gli argomenti con un suffisso numerico
	local a = 1 -- Counter variable.
	local moreArgumentsExist = true
	while moreArgumentsExist == true do
		moreArgumentsExist = false
		for i = a, a + step - 1 do
			for j,v in ipairs(prefixTable) do
				local prefixArgName = v.prefix .. tostring(i)
				if origArgs[prefixArgName] then
					moreArgumentsExist = true -- Aggiunge una passata se un parametro è stato trovato, anche se nullo.
					preprocessSingleArg(prefixArgName)
				end
				-- Processa la tavola dei dipendenti  se il parametro da cui dipendono esiste e non è nullo
				if v.depend and args[prefixArgName] then
					for j,dependValue in ipairs(v.depend) do
						local dependArgName = dependValue .. tostring(i)
						preprocessSingleArg(dependArgName)
					end
				end
			end
		end
		a = a + step
	end
end

function p.infobox(frame)
	-- Se chiamata mediante  #invoke, usa gli argomenti passati al template invocante.
	-- Altrimenti a scopo di test assume che gli argomenti siano passati direttamente
	if frame == mw.getCurrentFrame() then
		origArgs = frame:getParent().args
	else
		origArgs = frame.args
	end

	-- Le funzioni Parser considerano la stringa vuota come falsa, così per preservare il
	-- comportamento di {{infobox}} tutti gli argomenti vuoti non vengono memorizzati
	-- nella tabella globale args, così da essere considerati falsi
	-- Nota: args è una variabile globale per il modulo dichiarata al suo inizio
	-- Scandisce i parametri nello stesso ordine in cui lo faceva il vecchio {{infobox}}
	-- così che eventuali istruzioni ref compariranno in posizione e ordine corretto. Parametri che dipendono da
	-- altri parametri sono processati solo se il parametro è presente, così da evitare
	-- la comparsa di riferimenti fantasma in posti inattesi.
	preprocessSingleArg('StileTabella')
	preprocessArgs({
		{prefix='SopraTitolo', depend={'StileSopraTitolo'}}
		}, 0)
	preprocessArgs({
		{prefix='TitoloEst', depend={'StileTitoloEst'}}
		 }, 0)
	preprocessArgs({
		{prefix='TitoloInt', depend={'StileTitoloInt'}}
		}, 0)
	preprocessArgs({
		{prefix='SottoTitolo', depend={'StileSottoTitolo'}}
		}, 0)
	preprocessArgs({
		{prefix='Immagine', depend={'ClasseImmagine', 'StileImmagine',
						'Didascalia', 'StileDidascalia'}}
		}, 0)
	preprocessSingleArg('StileGruppo')
	preprocessSingleArg('StileNome')
	preprocessSingleArg('StileValore')
	preprocessArgs({
		{prefix = 'Gruppo', depend={'GruppoStile'}},
		{prefix = 'GruppoOpzionale', depend={'GruppoStile'}},
		{prefix = 'Valore', depend={'Nome', 'Classe'}},
	}, 50)
	preprocessSingleArg('Ultima')
	preprocessSingleArg('StileUltima')
	preprocessSingleArg('NomeTemplate')
	preprocessSingleArg('LinkWikidata')
	preprocessSingleArg('CreaTable')
	preprocessSingleArg('Summary')
	return _infobox()
end

return p