Modulo in Lua per gestire le funzioni di {{Infobox}}


-- 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 .. '\n'
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.stilegruppoN or args.StileGruppo or nil)
					: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(rowArgs.stilenomeN or args.StileNome or nil)
					:wikitext(rowArgs.nome)
			dataCell = row:tag('td')
		else
			dataCell = row:tag('td')
				:addClass('sinottico_testo_centrale')
				:attr('colspan', 2)
		end
		dataCell
			:addClass(rowArgs.classe or nil)
			:cssText(rowArgs.stilevaloreN or args.StileValore or nil)
			: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 nil)
				: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 nil)
				:wikitext(args.SottoTitolo)
	end
	if args.TitoloEst then
		root
			:tag('caption')
			:addClass('sinottico_testata')
			:cssText(args.StileTitoloEst or nil)
			: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 nil)
				: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('sinottico_testo_centrale ' .. (args.ClasseImmagine or ''))
		:attr('colspan', '2')
		:cssText(args.StileImmagine or nil)
		:wikitext(args.Immagine)
	 if args.Didascalia then
		cell_immagine
			:tag('br', {selfClosing = true})
				:done()
			:tag('span')
			:cssText(args.StileDidascalia or nil)
			:wikitext(args.Didascalia)
	end
	root:tag('tr'):node(cell_immagine)
end


local function renderRows()
	local rownums = getArgNums('Valore', 'GruppoOpzionale',  'Gruppo', 'Nodo')
	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 or args['Nodo' .. rownums[j]] ~= nil then
					skip = false
					break
				end
			end
		end
		if args['Nodo' .. num] ~= nil then
			root:wikitext(args['Nodo' .. num])
			skip = true
		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],
				stilegruppoN = args['GruppoStile' .. num],
				stilenomeN = args['NomeStile' .. num],
				stilevaloreN = args['ValoreStile' .. 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 nil)
				:wikitext(args.Ultima)
				:newline()
end

local function renderNavBar()
	if not args.NomeTemplate then return end
	root
		:tag('tr')
			:tag('td')
				:addClass('sinottico_piede2 noprint nomobile 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' or args.Posizione == 'corpo' or args.Posizione == 'coda' then
		root = mw.html.create('')
	else
		root = mw.html.create('table')
		root
			:addClass('infobox sinottico')
			:cssText(args.StileTabella or nil)
			:attr('summary', args.Summary or 'Tabella sinottica che riassume i principali dati del soggetto')
	end
	renderTitle()
	renderImage()
	renderRows()
	renderLastRow()
	renderNavBar()
	
	local res = tostring(root)
	if args.Posizione == 'coda' then
		res = res .. '</table>';
	elseif args.Posizione == 'testa' then
		res = mw.ustring.gsub( res, '</table>$', '')
	end
	return res
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 = 'Nodo'},
		{prefix = 'Gruppo', depend={'GruppoStile'}},
		{prefix = 'GruppoOpzionale', depend={'GruppoStile'}},
		{prefix = 'Valore', depend={'Nome', 'Classe', 'NomeStile', 'ValoreStile'}},
	}, 50)
	preprocessSingleArg('Ultima')
	preprocessSingleArg('StileUltima')
	preprocessSingleArg('NomeTemplate')
	preprocessSingleArg('LinkWikidata')
	preprocessSingleArg('CreaTable')
	preprocessSingleArg('Posizione')
	preprocessSingleArg('Summary')
	return _infobox()
end

return p