Modulo:Fumetto e animazione/sandbox

--[[
Questo modulo è in appoggio al template Fumetto e animazione per gestirne
le funzioni di categorizzazione automatica
]]
local p = {}
local cfg = mw.loadData("Modulo:Fumetto e animazione/Configurazione/sandbox")
local getArgs = require('Module:Arguments').getArgs
local mDelink = require('Modulo:Delink')
local error_category = 'Errori di compilazione del template Fumetto e animazione'

local function build_reverse_alias(table_name)
	local reverse_alias = {}
	for alias, name in pairs(cfg['alias_' .. table_name]) do
		if not reverse_alias[name] then reverse_alias[name] = {} end
		table.insert(reverse_alias[name], alias)
	end
	return reverse_alias
end

-- ========================================================================
-- Sostituisce name con il suo alias se presente nella tabella alias
-- quindi ritorna il valore corrispondente dalla tabella values
-- ========================================================================
local function get_category(name, table_name, delink)
	if name and delink then
		-- Rimuove i wikilink e restituisce la parte a sinistra della barra in caso di pipe trick
		name = name:match('^%[%[(.-)|.+%]%]') or name:match('^%[%[(.-)%]%]') or name
	end
	name = cfg['alias_' .. table_name][name] or name
	return cfg[table_name][name]
end

-- ========================================================================
-- Rimpiazza le parentesi quadre nella stringa con i corrispondenti codici
-- ascii
-- ========================================================================
local function replace_braces(s)
	local new_s = mw.ustring.gsub(s, "%[", "[")
	new_s = mw.ustring.gsub(new_s, "%]", "]")
	new_s = mw.ustring.gsub(new_s, "%(", "(")
	new_s = mw.ustring.gsub(new_s, "%)", ")")
	new_s = mw.ustring.gsub(new_s, " ", " ")
	return new_s
end

-- ========================================================================
-- Appende una tabella a un'altra tabella
-- ========================================================================
local function append_table(t1, t2)
	for _,el in ipairs(t2) do
		t1[#t1+1] = el
	end
end

-- ========================================================================
-- Restituisce l'aggettivo plurale maschile o femminile del Paese
-- specificato usando il template AggNaz
-- ========================================================================
local function get_adj(paese, genere)
	local adj
	local title = mw.title.new('Template:AggNaz/' .. paese)
	adj = (title and title.exists) and
		mw.getCurrentFrame():expandTemplate {
			title = title.text,
			args = { genere }
		} or nil
	
	return adj
end

-- ========================================================================
-- Restituisce il tipo di opera se riconosciuto nella configurazione
-- ========================================================================
local function get_media(args, alias)
	local key_tipo = alias and 'prefisso_tipo' or 'tipo'
	local key_sottotipo = alias and 'prefisso_sottotipo' or 'sottotipo'
	local ret = cfg[key_sottotipo][args.tipo] and
		cfg[key_sottotipo][args.tipo][args.sottotipo] or
		cfg[key_tipo][args.tipo]
	return ret and ret:lower() or nil
end

-- ========================================================================
-- Restituisce true se l'opera corrisponde a uno dei media indicati
-- ========================================================================
local function media_in_array(args, ...)
	for _, media in ipairs({ ... }) do
		media = media:gsub('^%l', string.upper)
		if cfg.tipo[args.tipo] == media or
				cfg.sottotipo[args.tipo] == media or
				cfg.sottotipo[args.tipo][args.sottotipo] == media then
			return true
		end
	end
	return false
end

-- ========================================================================
-- Individua e restituisce un anno, se presente nella stringa indicata
-- ========================================================================
local function get_year(str)
	if str then
		return str:match('%d%d%d%d')
	end
end

-- ========================================================================
-- Ritorna una lista di categorie per la sequenza di parametri con
-- nome base 'base_name' consultando la tabella 'table'.
-- 'alias_table' è una tabella di nomi alternativi per i valori dei
-- parametri, 'max_index' il numero massimo dell'indice del parametro
-- da controllare
-- ========================================================================
local function get_categories(args, table_name, lowercase)
	local categories = {}
	local base_name = table_name:match('[^_]')
	local anno = get_year(args['data inizio']) or get_year(args['data fine'])
	local index = 1
	while true do
		local adj
		local name_value = args[base_name .. ' ' .. tostring(index)] or index == 1 and args[base_name]
		if not name_value then break end
		name_value = mDelink._main({ name_value })
		if lowercase then name_value = mw.ustring.lower(name_value) end
		if table_name == 'paese_TV' then
			adj = get_adj(name_value, 'fp')
			if adj and (adj ~= 'giapponesi' or args[base_name .. ' 2']) then
				categories[#categories+1] = 'Serie televisive d\'animazione ' .. adj
			end
		elseif table_name == 'paese_film_DTV' then
			adj = get_adj(name_value, 'mp')
			if adj then
				categories[#categories+1] = 'Film d\'animazione ' .. adj
				if anno then
					categories[#categories+1] = 'Film ' .. adj .. ' del ' .. anno
				end
			end
		elseif table_name == 'paese_webserie' then
			adj = get_adj(name_value, 'fp')
			if adj then
				categories[#categories+1] = 'Webserie ' .. adj
			end
		elseif table_name == 'paese_film_TV' then
			adj = get_adj(name_value, 'mp')
			if adj then
				categories[#categories+1] = 'Film d\'animazione ' .. adj .. ' per la televisione'
			end
		elseif table_name == 'paese_fumetto' then
			if name_value == 'Belgio' or name_value == 'Francia' then
				categories[#categories+1] = 'Fumetti franco-belgi'
			else
				adj = get_adj(name_value, 'mp')
				if adj and adj ~= 'giapponesi' and
						adj ~= 'cinesi' and adj ~= 'coreani' then
					categories[#categories+1] = 'Fumetti ' .. adj
				end
			end
		elseif table_name == 'etichetta_sottotipo' or table_name == 'editore_sottotipo' then
			categories[#categories+1] = get_category(args.sottotipo .. ' ' .. name_value, table_name)
		else
			local category = get_category(name_value, table_name)
			if category then
				categories[#categories+1] = category
			end
		end
		index = index + 1
	end
	return categories
end

----------------------------------------------------------------------------------
-- Categorizza tranne che per genere
----------------------------------------------------------------------------------
function p.categorie(frame)
	local args = getArgs(frame, {parentOnly = true})
	local media = get_media(args)
	local isMainspace = mw.title.getCurrentTitle().namespace == 0
	local catEnabled = args.categorie ~= 'no'
	if not (media and isMainspace and catEnabled) then return end
	local categories = {}
	local year = get_year(args['data inizio']) or get_year(args['data fine'])
	-- Categoria di errore quando data inizio/data fine sono compilati
	-- ma non si rilevano le quattro cifre dell'anno in entrambi
	if (args['data inizio'] or args['data fine']) and not year then
		categories[#categories+1] = error_category
	end
	-- Categorizzazioni specifiche
	if media == "serie televisive d'animazione" then
		append_table(categories, get_categories(args, 'studio_cartoneTV'))
		if counter == #categories then
			append_table(categories, get_categories(args, 'paese_TV'))
			append_table(categories, get_categories(args, 'studio'))
		end
	elseif media_in_array(args, 'anime', 'cartone') then
		if media == "film d'animazione direct-to-video" then
			categories[#categories+1] = tipo
			append_table(categories, get_categories(args, 'paese_film_DTV'))
		elseif media == "webserie d'animazione" then
			append_table(categories, get_categories(args, 'paese_webserie'))
		elseif media == "film d'animazione per la televisione" then
			append_table(categories, get_categories(args, 'paese_film_TV'))
		end
		append_table(categories, get_categories(args, 'studio'))
	elseif args.tipo == 'manga' then
		counter = #categories
		append_table(categories, get_categories(args, 'editore_manga'))
		if counter == #categories then
			append_table(categories, get_categories(args, 'editore'))
		end
	elseif media_in_array(args, 'manhwa', 'manhua') then
		append_table(categories, get_categories(args, 'editore'))
	elseif args.tipo == 'light novel' then
		categories[#categories+1] = tipo
		if (args['lingua originale'] or ''):match('giapponese') then
			categories[#categories+1] = 'Romanzi in giapponese'
		end
		counter = #categories
		append_table(categories, get_categories(args, 'etichetta'))
		if counter == #categories then
			append_table(categories, get_categories(args, 'editore'))
		end
	elseif args.tipo == 'fumetto' then
		counter = #categories
		if args.sottotipo then
			append_table(categories, get_categories(args, 'etichetta_sottotipo'))
		end
		if counter == #categories then
			local etichetta = {}
			etichetta = get_category(mDelink._main({ args.etichetta }), 'etichetta')
			if etichetta then
				append_table(categories, get_categories(args, 'etichetta'))
			end
			counter = #categories
			if args.sottotipo then
				append_table(categories, get_categories(args, 'editore_sottotipo'))
			end
			if counter == #categories then
				if args.sottotipo then
					categories[#categories+1] = tipo
				end
				if etichetta == nil then
					counter = #categories
					append_table(categories, get_categories(args, 'editore'))
					if counter == #categories then
						append_table(categories, get_categories(args, 'paese_fumetto'))
					end
				end
			end
		end
	end
	local emittente = get_category(args.streaming, 'streaming', true) or
		args.streaming == nil and get_category(args.rete, 'rete', true)
	if emittente and media_in_array('anime', 'cartone') then
		local pre_emittente = get_media(args, true)
		if pre_emittente then
			if pre_emittente:find('^Webserie') and not args.streaming then
				categories[#categories+1] = error_category
			else
				categories[#categories+1] = pre_emittente .. emittente
			end
		end
	end
	if media_in_array(args, 'manga', 'manhwa') then
		append_table(categories, get_categories(args, 'target', true))
	end
	-- Categorizzazione per tipo delle opere non suddivise per anno
	if media_in_array(args, 'manhua', 'manhwa', 'original net anime', "webserie d'animazione") then
		categories[#categories+1] = media
	-- Categorizzazione per anno delle opere previste
	elseif year and args.sottotipo ~= 'film direct-to-video' then
		categories[#categories+1] = string.format('%s del %s', get_media(args, true) or media, year)
	end
	for index, cat in ipairs(categories) do
		categories[index] = '[[Categoria:' .. cat .. ']]'
	end
	return table.concat(categories)
end

----------------------------------------------------------------------------------
-- Gestisce categorie e wikilink dei generi
----------------------------------------------------------------------------------
function p.generi(frame)
	local args = getArgs(frame, {parentOnly = not frame.args[1]})
	local genre = frame.args[1]
	if genre == '' then return end
	local current_page = mw.title.getCurrentTitle()
	local current_namespace = current_page.namespace
	local category
	local result = {}
	for name, alias_name in pairs(cfg['alias_genere']) do
		local piped = mw.ustring.match(genre, '%[%[[^%[]*|%s*' .. name .. '%s*%]%]')
		local pattern = (piped and '%[%[[^%[]*|%s*' or '%[%[%s*') .. name .. '%s*%]%]%a*'
		genre = mw.ustring.gsub( genre, pattern, '[[' .. alias_name .. ']]' )
	end
	for name, correct_name in pairs(cfg['genere']) do
		if current_namespace == 0 and args.categorie ~= 'no' then
			local tipo = {
				['anime'] = correct_name[1],
				['fumetto'] = correct_name[2],
				['light novel'] = correct_name[3],
				['manga'] = correct_name[1],
				['manhua'] = correct_name[2],
				['manhwa'] = correct_name[2]
			}
			tipo = tipo[args.tipo]
			tipo = tipo and tipo[1] and tipo[1] .. ']][[Categoria:' .. tipo[2] or tipo
			category = tipo and tipo ~= '' and '[[Categoria:' .. tipo .. ']]'
		end
		local piped = mw.ustring.match(genre, '%[%[[^%[]*|%s*' .. name .. '%s*%]%]')
		local pattern = (piped and '%[%[[^%[]*|%s*' or '%[%[%s*') .. name .. '%s*%]%]%a*'
		genre = mw.ustring.gsub( genre, pattern, correct_name[4] .. (category or '') )
		-- Categoria d'errore in caso di genere senza corrispondenza
		local list = mw.text.split( genre, ',' )
		for i = 1, #list do
			local s = replace_braces(list[i])
			local p = replace_braces(correct_name[4])
			if mw.ustring.match( s, p ) then
				result[i] = ''
			elseif result[i] == nil and current_namespace == 0 then
				result[i] = '[[Categoria:' .. error_category .. ']]'
			end
		end
	end
	genre = genre .. table.concat(result)
	return genre
end
----------------------------------------------------------------------------------
-- Ritorna la configurazione della tabella per aziende e target
----------------------------------------------------------------------------------
function p.tabella_configurazione(frame)
	local args = getArgs(frame)
	local table_name = args[1]
	if not(table_name) then return '' end
	local reverse_alias = build_reverse_alias(table_name)
	local root = mw.html.create('table')
	root
		:addClass('wikitable mw-collapsible mw-collapsed sortable')
		:tag('tr')
			:tag('th'):wikitext(table_name):done()
			:tag('th'):wikitext('Alias'):done()
			:tag('th'):wikitext('Categoria'):done()
	for name, cat_name in pairs(cfg[table_name]) do
		local name_code = table_name == 'genere' and
				replace_braces(cat_name[4]) or
				'[[' .. name .. ']]'
		local cat_code = {}
		if cat_name[1] then
			for i, cat in ipairs(cat_name) do
				if i > 3 then break end
				cat = cat[1] and
						cat[1] .. ']]<br />[[:Categoria:' .. cat[2] or cat
				if cat ~= '' then
					cat_code[#cat_code+1] = '[[:Categoria:' .. cat .. ']]'
				end
			end
			cat_code = table.concat(cat_code, '<br />')
		else
			cat_code = '[[:Categoria:' .. cat_name .. ']]'
		end
		local alias_code = '&nbsp;'
		if reverse_alias[name] then
			for i,alias in ipairs(reverse_alias[name]) do
				reverse_alias[name][i] = '&#91;&#91;' .. alias .. '&#93;&#93;'
			end
			alias_code = table.concat(reverse_alias[name], '<br />')
		end
		root:tag('tr')
			:tag('td'):wikitext(name_code):done()
			:tag('td'):wikitext(alias_code):done()
			:tag('td'):wikitext(cat_code):done()
	end
	return tostring(root)
end
return p