Questo modulo serve al {{Autore}} per generare l'incipit e le categorie delle pagine in cui è utilizzato, integrando informazioni da Wikidata quando possibile.


-- elemento su Wikidata
local item = mw.wikibase.getEntityObject()
local c = require('Modulo:Common')
local d = require('Modulo:Date')
local controlloAutorita = require('Modulo:Controllo di autorità')
local p = {}

-- lingua predefinita
local lang = mw.language.getContentLanguage()

local attrs = {
	nome = 'Nome',
	cognome = 'Cognome',
	professioneNazionalita = 'Professione e nazionalità'
}

function p.autodetect(frame)
	local aut = frame:getParent().args[1]
	local t = mw.title.getCurrentTitle().text
	if aut == nil or aut == '' then
		local m = string.match(t,'^Libri di (.+)$')
		if m == nil then
			m = string.match(t,'^Testi di (.+)$')
		end
		if m == nil then
			m = string.match(t,'in cui è citato (.+)$')
		end
		if m ~= nil then
			return m
		end
	end
	return aut
end

function p.dato(frame)
	local page = mw.title.new(frame:getParent().args[1], 102)
	local c = string.gsub(page:getContent(), '^(.*%{%{%s*[Aa]utore)(\n*%|.-%}%}).*$', '{{DatoAutore/dato|dato='..frame:getParent().args[2]..'%2')
	return frame:preprocess(c)
end

function p.prendiDato(frame)
	return frame:getParent().args[attrs[frame:getParent().args.dato]]
end

-- controllo di autorita'
function cda()
	if item then return controlloAutorita.box(item) end
end

-- stringa da restituire
local output = ''
local outputCat = '' --le categorie le metto qui, per tenerle separate e metterle poi in fondo al testo generato

function add(str)
	output = output..str
end

-- aggiunge una categoria (solo in ns Autore)
function addCat(name)
	if c.isNSAutore() then
		outputCat = outputCat .. c.category(mw.text.trim(name))
	end
end

-- richiama un template che ha il compito di aggiungere una o più categorie (solo in ns Autore)
function addTmp(title, args)
	if c.isNSAutore() then
		outputCat = outputCat .. c.template(title, args)
	end
end

--ritorna true se autore umano, false altrimenti
function p.isHuman()
	return c.instanceof(5)
end

--data una stringa di solo testo contenente giono e mese, ritorna il link a quel giorno e mese
function linkGiornoMese(giornoMese)
	if giornoMese then return c.template('DataGiorno', {giornoMese} )
	else return '' end
end

--data una stringa di solo testo contenente l'anno, ritorna il link a quell'anno
function linkAnno(anno)
	if anno then return c.template('Autore/Anno', {anno} )
	else return '' end
end

--data una stringa di solo testo contenente il secolo, ritorna il link a quel secolo
function linkSecolo(secolo)
	local stringa = ''
	if secolo then 
		for sec in string.gmatch(secolo, '([^/]+)') do
			if stringa ~= '' then
				stringa = stringa .. '/'
			end
			local catname = 'Autori del '
			if sec == 'Antichità' then
				catname = 'Autori dell\''
			end
			stringa = stringa .. c.category(catname..sec, sec, true)
		end
	end
	return stringa
end

-- crea un collegamento a una categoria (non vuota) relativa all'autore
function catLink(cat, icon)
	local fcat = mw.ustring.format(cat, nomeAutore)
	if c.pagesInCat(fcat) > 0 then
		add('<div>[[File:'..(icon or 'Nuvola filesystems folder open.png')..'|20px]] \'\''
			..c.category(fcat, mw.ustring.format(cat, (args.Nome or '')..' '..(args.Cognome or '')), true)
			..' ('..c.pagesInCat(fcat)..')\'\'</div>')
	end
end

-- l'autore è maschio o femmina?
function p.sesso()
	return c.getLabelFromValue(c.getClaimValue(c.getSingleClaimByProperty('P21')))
end

-- ritorna l'immagine che rappresenta l'autore (la prima, se ce ne sono più di una)
function p.immagine()
	if p.isHuman() then
		return c.getSingleClaimValueByProperty('P18') -- immagine
		or c.getSingleClaimValueByProperty('P94') -- coat of arms image
		or c.getSingleClaimValueByProperty('P41') -- bandiera 
	else
		return c.getSingleClaimValueByProperty('P41') -- bandiera 
		or c.getSingleClaimValueByProperty('P94') -- coat of arms image
		or c.getSingleClaimValueByProperty('P18') -- immagine
	end
end

function p.attivita(full)
	local list = {}
	for str in mw.text.gsplit(full, '/', true) do
		table.insert(list, lang:lcfirst(str))
	end
	return mw.text.listToText(list)
end

--ritorna una lista di note <ref>testo</ref> leggendo P854 (Reference URL) e P248 (stated in)
function getRefList(references)
	refList = ''
	local n = 0;
	for i, r in pairs(references or {}) do
		if n < 5 and r.snaks then
			local refURL = r.snaks.P854
			local refStatedIn = r.snaks.P248
			local refLink, refText, refLabel

			if refStatedIn and not c.empty(refStatedIn) then
				source = 'Q'..refStatedIn[1].datavalue.value["numeric-id"]
				refLabel = mw.wikibase.label(source)
				if refLabel ~= '0' then
					refText = refLabel
				end
				if source == 'Q1128537' then
					dbiLink = c.getSingleClaimValueByProperty('P1986')
					if dbiLink then
						refText = '[http://www.treccani.it/enciclopedia/' .. dbiLink .. '_(Dizionario-Biografico) ' .. mw.wikibase.label(source) .. ']'
					end
				elseif source == 'Q30237471' then
					dbfLink = c.getSingleClaimValueByProperty('P7203')
					if dbfLink then
						refText = '[http://www.dizionariobiograficodeifriulani.it/' .. dbfLink .. ' ' .. mw.wikibase.label(source) .. ']'
					end
				elseif source == 'Q36578' then
					gndLink = c.getSingleClaimValueByProperty('P227')
					if gndLink then
						refText = '[http://d-nb.info/gnd/' .. gndLink .. ' ' .. mw.wikibase.label(source) .. ']'
					end
				elseif source == 'Q19938912' then
					bnfLink = c.getSingleClaimValueByProperty('P268')
					if bnfLink then
						refText = '[http://catalogue.bnf.fr/ark:/12148/cb' .. bnfLink .. ' Bibliothèque nationale de France]'
					end
				elseif source == 'Q576951' then
					sbnLink = c.getSingleClaimValueByProperty('P396')
					if sbnLink then
						refText = '[https://opac.sbn.it/nome/' .. sbnLink .. ' Servizio Bibliotecario Nazionale]'
					end
				elseif source == 'Q1201876' then
					olLink = c.getSingleClaimValueByProperty('P648')
					if olLink then
						refText = '[https://openlibrary.org/works/' .. olLink .. ' OpenLibrary]'
					end
				elseif source == 'Q5375741' then
					britLink = c.getSingleClaimValueByProperty('P1417')
					if britLink then
						refText = '[https://www.britannica.com/' .. britLink .. ' Encyclopædia Britannica]'
					end
				elseif source == 'Q237227' then
					brockLink = c.getSingleClaimValueByProperty('P5019')
					if brockLink then
						refText = '[https://brockhaus.de/ecs/enzy/article/' .. brockLink .. ' Brockhaus Enzyklopädie]'
					end
				elseif source == 'Q2629164' then
					isfdbLink = c.getSingleClaimValueByProperty('P1233')
					if isfdbLink then
						refText = '[http://www.isfdb.org/cgi-bin/ea.cgi?' .. isfdbLink .. ' Internet Speculative Fiction Database]'
					end
				elseif source == 'Q23023088' then
					vegeLink = c.getSingleClaimValueByProperty('P2191')
					if vegeLink then
						refText = '[https://www.fantascienza.com/catalogo/NILF' .. vegeLink .. ' Catalogo Vegetti della letteratura fantastica]'
					end
				end

				--cerco l'url della fonte in "described by source" (P1343)
				describedBySource = c.getClaimByPropertyAndValue('P1343', source)
				if describedBySource and describedBySource.qualifiers then
					sourceURL = describedBySource.qualifiers.P854
					if sourceURL and not c.empty(sourceURL) then
						refURL = sourceURL
					end
				end
			end

			if refURL and not c.empty(refURL) then
				if refLabel == nil then
					refLabel = 'Fonte'
				end
				refLink =  refURL[1].datavalue.value
				refText = '['..refLink..' '..refLabel..']'
			end
			
			if refText then
				n = n + 1
				refList = refList .. mw.getCurrentFrame():extensionTag( 'ref', refText, { name = refText } )
			end
		end
	end
	return refList
end

--ritorna le 4 componenti della data formattate, più la stringa finale con i link e l'eventuale "circa"
local function getFormattedDate(value, prima, dopo, isEsatta, refList)
	local data = {
		dayMonth = '',
		decade = '',
		year = '',
		century = '',
		stringa = '',
		esatta = isEsatta,
		calendar = nil,
		precision = nil
	}
 
 	date = d.getDate(value)
 	if date then
 		data.calendar = date.calendar
 		data.precision = date.precision
 		
		if date.precision >= 9 and date.precision <= 11 then
			data.year = d.formatYear(date)
		end
        
		--precision: 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day
		if date.precision == 7 then
			data.stringa = linkSecolo(d.formatCentury(date))									-- "XVI secolo"
		elseif date.precision == 8 then
			data.decade = d.formatDecade(date)
			data.stringa = data.decade .. ' del ' .. linkSecolo(d.formatCentury(date))			-- "anni 50 del XVI secolo"
		elseif date.precision == 9 then
			data.stringa = linkAnno(d.formatYear(date))											-- "1556"; "24 a.C."
		elseif date.precision == 10 then
			data.dayMonth = d.formatMonth(date)
			data.stringa = linkGiornoMese(data.dayMonth) .. ' ' .. linkAnno(d.formatYear(date))		-- agosto 1556
        elseif date.precision == 11 then
			data.dayMonth = d.formatDayMonth(date)
			data.stringa = linkGiornoMese(data.dayMonth) .. ' ' .. linkAnno(d.formatYear(date))		-- 1° agosto 1556
		end

		--mostra il tipo di calendario (solo per precisioni almeno di mese, e solo per il periodo in cui alcuni usavano ancora il giuliano)
		if date.precision >= 10 and date.year and date.year >= 1582 and date.year <= 1924 then
			data.stringa = data.stringa .. '<sup>' .. d.linkCalendar(value.calendarmodel) .. '</sup>'
		end
		
		--la frasetta prima e dopo la data
		if prima then data.stringa = prima .. ' ' .. data.stringa end
		if dopo  then data.stringa = data.stringa .. ' ' .. dopo end
		
		--aggiungo le note (TODO: per il momento escludo il ns Opera per evitare le note "inutili", da vedere come sistemare meglio)
		if c.notEmpty(data.stringa) and c.isNSAutore() then
			data.stringa = data.stringa .. getRefList(refList)
		end
		
		--categoria di controllo per date < 15 Oct 1582 ma segnate come GREGORIANE
		if date.precision >= 10 and date.year and date.year <= 1582 and date.calendar == 'gregoriano' then
			if date.year < 1582 or tonumber(date.month) < 10 or 
				(date.precision == 11 and tonumber(date.month) == 10 and tonumber(date.day) < 15) then
				addCat('Autori con date fino al 1582 segnate come gregoriane')
			end
		end
	end
    return data
end

--ritorna la lista di tutte le date per la proprietà indicata (P569=nascita, P570=morte), con la stringa finale
function getAllDates(property)
	local list = {}		--lista di tutte le date
	local stringa		--stringa con il testo finale con i link, e concatenato da "o" oppure "tra il e il"
	
	claims = c.sortClaimsByDate(c.getClaimsByProperty(property) or {})
	for index, claim in pairs(claims) do
		local val = c.getClaimValue(claim)
		local preStringa = ''
		
		local circa = c.getLabelFromValue(c.getQualifierValueFromClaim(claim, 'P1480'));
		local ante = c.getQualifierValueFromClaim(claim, 'P1326')
		local post = c.getQualifierValueFromClaim(claim, 'P1319')
		
		if val then
			preStringa = getFormattedDate(val, nil, circa, (circa == nil), nil).stringa .. ', '
		end
		
		if ante and post then
			--caso "tra il"
			local list = {}
			table.insert(list, getFormattedDate(post, nil, nil, true, nil))
			table.insert(list, getFormattedDate(ante, nil, nil, true, claim.references))
			stringa = preStringa .. 'tra il ' .. list[1].stringa .. ' e il ' .. list[2].stringa

			--in questo caso ritorniamo direttamente questa coppia di date con la stringa "tra il"
			return list, stringa
		elseif ante ~= nil and post == nil then
			--caso ante
			table.insert(list, getFormattedDate(ante, preStringa .. 'prima del', nil, false, claim.references))
		elseif ante == nil and post ~= nil then
			--caso post
			table.insert(list, getFormattedDate(post, preStringa .. 'dopo il', nil, false, claim.references))
		elseif val then
			--caso normale
			table.insert(list, getFormattedDate(val, nil, circa, (circa == nil), claim.references))
		end
	end

	--se ci sono 2 date con stesso anno ma una più precisa dell'altra (cioè anche giorno/mese), allora nella stringa mostriamo solo quella più precisa
	if c.size(list) == 2 and
		list[1].precision >= 9 and list[2].precision >= 9 and list[1].year == list[2].year and list[1].calendar == list[2].calendar and list[1].precision ~= list[2].precision then
			if list[1].precision > list[2].precision then
				stringa = list[1].stringa
			else
				stringa = list[2].stringa
			end
	else
		--a questo punto va costruita la stringa finale con gli "o" tra le varie date
		local stringaList = {}
		for i, e in pairs(list) do 
			if c.notEmpty(e.stringa) then table.insert(stringaList, e.stringa) end
		end
		stringa = mw.text.listToText(stringaList, ' o ', ' o ')
	end
	
	--default se non ci sono valori
	if stringa == '' then stringa = '...' end
	
	return list, stringa
end

--ritorna tutte le date di nascita (o di fondazione/creazione)
function getBirthDates()
	local dates, stringa = getAllDates('P569') --data di nascita
	if c.empty(dates) then
		dates, stringa = getAllDates('P571') --data di fondazione
	end
	return dates, stringa
end

--ritorna tutte le date di morte (o di scioglimento/chiusura)
function getDeathDates()
	local dates, stringa = getAllDates('P570') --data di morte
	if c.empty(dates) then
		dates, stringa = getAllDates('P576') --data di scioglimento
	end
	return dates, stringa
end

--ritorna le date del floruit
function getFloruitDates()
	--property floruit
	local dates, stringa = getAllDates('P1317')
	if c.size(dates) == 2 then
		stringa = 'tra il '..dates[1].stringa..' e il '..dates[2].stringa
	else
		--properties work period start/end
		dateStart, stringaStart = getAllDates('P2031')
		dateEnd, stringaEnd = getAllDates('P2032')
		if c.size(dateStart) == 1 and c.size(dateEnd) == 1 then
			dates = {}
			table.insert(dates, dateStart[1])
			table.insert(dates, dateEnd[1])
			stringa = 'tra il '..stringaStart..' e il '..stringaEnd
		end
	end
	return dates, '\'\'[[:w:Floruit|floruit]]\'\' '..stringa
end

--data una lista di date, ritorna la data più precisa possibile che si possa individuare con certezza, oppure nil
--es: se abbiamo una sola data, senza "circa", ritorna quella
--se abbiamo 2 date, senza "circa", con stesso anno e giorno diverso: ritorna la data col solo anno
function getDataCerta(dates)
	--se c'è una data incerta ritorna nil
	for i, d in pairs(dates) do
		if not d.esatta then return nil	end
	end
	
	--se c'è una sola data che arriva almeno all'anno, ritorna quella
	if c.size(dates) == 1 and dates[1].year ~= '' then
		--se la precisione è inferiore al giorno, ritorna solo l'anno
		if dates[1].precision < 11 then dates[1].dayMonth = nil end
		return dates[1]
	elseif c.size(dates) > 1 then
		--se le diverse date non concordano almeno sull'anno, non c'è una data certa
		local anno = dates[1].year
		for i, d in pairs(dates) do
			if anno ~= d.year then return nil end
		end
		
		local dataCerta = dates[1]
		
		--se le date sono 2, una gregoriana e l'altra giuliana, ritorna la gregoriana
		if c.size(dates) == 2 and dates[1].calendar ~= dates[2].calendar then
			if dates[2].calendar == 'gregoriano' then dataCerta = dates[2] end
			--se la precisione è inferiore al giorno, ritorna solo l'anno
			if dataCerta.precision < 11 then dataCerta.dayMonth = nil end
			return dataCerta
		end
		
		--se ho più date, ritorna solo l'anno
		dataCerta.dayMonth = nil
		return dataCerta
	end
end

--cerca di calcolare i secoli in cui l'autore era attivo, sulla base delle date di nascita/morte o del floruit
function p.getSecoloAttivita()
	--cerca le date di nascita e morte esatte
	local startDates = d.getDatesFromProperty('P569', 'asc')
	local endDates = d.getDatesFromProperty('P570', 'desc')
	
	--oppure cerca le date di fondazione / cessazione
	if c.empty(startDates) then startDates = d.getDatesFromProperty('P571', 'asc') end
	if c.empty(endDates) then endDates = d.getDatesFromProperty('P576', 'desc') end
	local floruitDates = d.getDatesFromProperty('P1317')
	
	--se non ci sono cerca "ante" e "post"
	if c.empty(startDates) then
		ap = d.getAnteOrPostFromProperty('P569')
		if ap then table.insert(startDates, ap) end
	end
	if c.empty(endDates) then
		ap = d.getAnteOrPostFromProperty('P570')
		if ap then table.insert(endDates, ap) end
	end
	if c.empty(startDates) then
		ap = d.getAnteOrPostFromProperty('P571')
		if ap then table.insert(startDates, ap) end
	end
	if c.empty(endDates) then
		ap = d.getAnteOrPostFromProperty('P576')
		if ap then table.insert(endDates, ap) end
	end
	
	--se ancora non trovi nulla cerca work period start/end
	if c.empty(startDates) then startDates = d.getDatesFromProperty('P2031', 'asc') end
	if c.empty(endDates) then endDates = d.getDatesFromProperty('P2032', 'desc') end
	
	mw.log('calcolo il secolo di attivita...')
	mw.log('start: ' .. c.printElement(startDates))
	mw.log('end: ' .. c.printElement(endDates))
	mw.log('floruit: ' .. c.printElement(floruitDates))
	
	local startDate = c.first(startDates)
	local endDate = c.first(endDates)
	if not startDate and endDate then
		startDate = c.deepcopy(endDate)
		startDate = d.addYears(startDate, -25) --ipotizziamo che sia vissuto almeno 25 anni 
	elseif not endDate and startDate then
		endDate = c.deepcopy(startDate)
		--gli autori senza data di morte e nati nel XX secolo si suppongono viventi nel XXI
		if not p.isHuman() or (startDate and startDate.century and startDate.century == 20) then 
			endDate.century = 21
			endDate.year = 2050
		else
			endDate = d.addYears(endDate, 25) --ipotizziamo che sia vissuto almeno 25 anni 
		end
	end
	
	if c.empty(startDate) and not c.empty(floruitDates) then 
		startDate = floruitDates[1]
	else
		if p.isHuman() and startDate and startDate.precision and startDate.precision >= 9 and startDate.year then 
			--supponiamo che un autore non sia "attivo" prima dei 15 anni di eta' + 5 anni di "tolleranza". 
			--(Es. se ha pubblicato dal 1896 al 1950 lo consideriamo solo nel XX secolo)
			startDate = d.addYears(startDate, 20)
		end
	end
	
	if c.empty(endDate) and not c.empty(floruitDates) then
		if #floruitDates > 1 then endDate = floruitDates[2]
		else endDate = floruitDates[1] end
	else
		if p.isHuman() and endDate and endDate.precision and endDate.precision >= 9 and endDate.year then 
			--5 anni di "tolleranza" (se un autore è morto nel 1901 non ha senso considerarlo "del XX secolo")
			endDate = d.addYears(endDate, -5)
		end
	end
	
	local centuries = {}
	if not c.empty(startDate) and not c.empty(endDate) and endDate.century and startDate.century then
		mw.log('start common: ' .. c.printElement(startDate))
		mw.log('end common: ' .. c.printElement(endDate))
		mw.log('secoli: ' .. tostring(startDate.century) .. '-' .. tostring(endDate.century))
		mw.log('secolo di attivita: ' .. tostring(startDate.century))
		
		-- se aggiungendo la tolleranza alla nascita siamo finiti nel secolo successivo alla morte, lo scartiamo
		if startDate.century > endDate.century then
			startDate = endDate
		end
		
		table.insert(centuries, d.formatCentury(startDate))
			
		while #centuries < 10 and (endDate.century ~= startDate.century or endDate.ac ~= startDate.ac) do
			if startDate.ac then
				startDate.century = startDate.century - 1
				if startDate.century == 0 then 
					startDate.ac = false
					startDate.century = 1
				end
			else
				startDate.century = startDate.century + 1
			end
			mw.log('secolo di attivita: ' .. tostring(startDate.century))
			table.insert(centuries, d.formatCentury(startDate))
		end
	end
	return centuries
end

function getSitelinksOrLabelsFromPropertyValues(propertyId, siteId, langCode)
	local claims = c.getClaimsByProperty(propertyId) or {}
	local sitelinksOrLabels = {}
	local found = false
	for _, claim in ipairs(claims) do
		if not found and claim and claim.mainsnak and claim.mainsnak.datavalue then
			local qid = 'Q'..claim.mainsnak.datavalue.value['numeric-id']
			local entity = mw.wikibase.getEntity(qid)
			
			-- check if entity is a "birth house", "museum", "arrondissement", i.e. something which is not a "town"
		local s = c.set{ 'Q19979289', 'Q33506', 'Q1307276', 'Q3947', 'Q16560', 'Q3950', 'Q2087181', 'Q702842', 'Q27686', 'Q23413', 'Q79007', 'Q751876', 'Q2116450', 'Q615980', 'Q2026833', 'Q211690', 'Q1907114', 'Q46124' }
			if entity.claims then
				local instOf = entity.claims["P31"]
				if instOf then
					for i, cl in pairs(instOf) do
						local pl = 'Q'..cl.mainsnak.datavalue.value['numeric-id']
						if s[pl] then
							local cla = c.first(entity.claims["P276"])					-- location
							if not cla then cla = c.first(entity.claims["P131"]) end	-- located in the administrative territorial entity
							if not cla then cla = c.first(entity.claims["P3842"]) end	-- located in present-day administrative territorial entity
							if cla then
								local value = c.getClaimValue(cla)
								if value and value["numeric-id"] then
									entity = mw.wikibase.getEntity('Q'..value["numeric-id"])
									found = true;
									break;
								end
							end
						end
					end
				end
			end
			
			local sitelinkOrLabel = entity:getSitelink(siteId)
			if not sitelinkOrLabel then
				local itLabel = entity:getLabel(langCode)
				if itLabel then
					langCode = 'it'
					sitelinkOrLabel = itLabel
					isLink = false
				end
			end
			if not sitelinkOrLabel then
				sitelinkOrLabel = entity:getSitelink('enwiki')
				langCode = 'en'
			end
			if not sitelinkOrLabel then
				sitelinkOrLabel = entity:getSitelink('frwiki')
				langCode = 'fr'
			end
			if not sitelinkOrLabel then
				sitelinkOrLabel = entity:getSitelink('eswiki')
				langCode = 'es'
			end
			if not sitelinkOrLabel then
				sitelinkOrLabel = entity:getSitelink('dewiki')
				langCode = 'de'
			end
			local isLink = true
			if not sitelinkOrLabel then
				langCode = 'it'
				sitelinkOrLabel = entity:getLabel(langCode) or entity:getLabel('en') or entity:getLabel('fr') or entity:getLabel('de')
				isLink = false
				if not entity:getLabel(langCode) then
					addCat('Etichetta mancante su Wikidata')
				end
			end
			if sitelinkOrLabel then
				table.insert(sitelinksOrLabels, {text = sitelinkOrLabel, isSitelink = isLink, lang = langCode})
			end
		end
	end
	return sitelinksOrLabels
end

function getPlaceOfBirth()
	local places = getSitelinksOrLabelsFromPropertyValues('P19', 'itwiki', 'it')
	if c.size(places) > 1 then
		addCat('Autori con luogo di nascita incerto')
	elseif c.size(places) == 1 and places[1].isSitelink then
		addCat('Nati a '..places[1].text)
	end
	for k, v in pairs(places) do
		if v.isSitelink then
			places[k] = c.link(v.text, v.text:gsub(' %(%D+%)', ''), v.lang .. ':w')
		else
			places[k] = v.text
			addCat('Autori con luogo di nascita o morte non presente in Wikipedia')
		end
	end
	return c.concat(places, ' o ')
end

function getPlaceOfDeath()
	places = getSitelinksOrLabelsFromPropertyValues('P20', 'itwiki', 'it')
	if c.size(places) > 1 then
		addCat('Autori con luogo di morte incerto')
	elseif c.size(places) == 1 and places[1].isSitelink then
		addCat('Morti a '..places[1].text)
	end
	for k, v in pairs(places) do
		if v.isSitelink then
			places[k] = c.link(v.text, v.text:gsub(' %(%D+%)', ''), v.lang .. ':w')
		else
			places[k] = v.text
			addCat('Autori con luogo di nascita o morte non presente in Wikipedia')
		end
	end
	return c.concat(places, ' o ')
end

--funzione che recupera i dati dell'autore, richiamabile anche con un item qualunque
function p.getDatiAutore(autoreItem)
	item = autoreItem or item
	c.setItem(item)
	local dati = {}
	
	--DATI ANAGRAFICI da Wikidata
	dati.nomeCognome = c.getLabel()
	dati.isHuman = p.isHuman()
	dati.gender = p.sesso()
	dati.immagine = p.immagine()
	dati.birthDates, dati.birthStringa = getBirthDates()
	dati.deathDates, dati.deathStringa = getDeathDates()
	dati.birthPlaceList = c.getLabelsFromPropertyValues('P19')
	dati.deathPlaceList = c.getLabelsFromPropertyValues('P20')
	dati.birthPlace = c.concat(dati.birthPlaceList, ' o ')
	dati.deathPlace = c.concat(dati.deathPlaceList, ' o ')
	dati.floruitDates, dati.floruitStringa = getFloruitDates()
	
	--pseudonimo P742
	dati.pseudonimi = c.getClaimValuesByProperty('P742')
	table.sort(dati.pseudonimi)
	dati.numeroPseudonimi = c.size(dati.pseudonimi)
	dati.stringaPseudonimo = c.concat(dati.pseudonimi, "''', '''", "''' e '''")
	
	--nome reale (birth name) P1477
	dati.nomiReali = c.getClaimValuesByProperty('P1477')
	table.sort(dati.nomiReali)
	dati.stringaNomeReale = c.concat(dati.nomiReali, "''' o '''")
	
	--alias (da cui vanno esclusi i precedenti campi e il nome "principale")
	dati.alias = c.sublist(c.getLabelAndAliases(), 1, 5) --max 5 alias
	c.subtractTable(dati.alias, dati.pseudonimi)
	c.subtractTable(dati.alias, dati.nomiReali)
	c.subtractTable(dati.alias, {dati.nomeCognome})
	table.sort(dati.alias)
	dati.stringaAlias = c.concat(dati.alias, "''', '''", "''' e '''")
	
	--anni di nascita e morte esatti
	dataNascitaCerta = getDataCerta(dati.birthDates)
	dataMorteCerta = getDataCerta(dati.deathDates)
	if dataNascitaCerta then
		dati.annoNascita = dataNascitaCerta.year
		dati.ricorrenzaNascita = dataNascitaCerta.dayMonth
	end
	if dataMorteCerta then
		dati.annoMorte = dataMorteCerta.year
		dati.ricorrenzaMorte = dataMorteCerta.dayMonth
	end
	
	--link al Diz. Biogr. degli Italiani
	dati.dbiLink = c.getSingleClaimValueByProperty('P1986')
	
	return dati
end

function p.autore(frame)
	if frame == nil or frame:getParent() == nil then
		error('Nessun frame rilevato')
	end

	-- parametri passati al template Autore
	args = c.getParameters(frame)

	if args.Wikidata then
		item = mw.wikibase.getEntityObject(args.Wikidata)
		c.setItem(item)
	end
	
	naz = (args['Nazionalità'] or ''):gsub(" naturalizzato ", "/"):gsub(" naturalizzata ", "/"):gsub(",", ""):gsub("-", "/")

	-- nome dell'autore: Nome, Cognome e Disambigua
	local nomeCognome = mw.text.trim((args.Nome or '')..' '..(args.Cognome or ''))
	if nomeCognome == '' then error('Nome autore non valido') end
	
	-- nome UNIVOCO dell'autore: Nome, Cognome e Disambigua
	nomeAutore = mw.text.trim(nomeCognome..' '..(args.Disambigua or ''))

	--cognome, nome (per ordinamenti)
	local cognonome = args.Cognome or ''
	if cognonome ~= '' then cognonome = cognonome ..' , ' end
	cognonome = c.stripAccents( cognonome .. (args.Nome or '') )
	add(c.template('DEFAULTSORT', {cognonome} ))

	local spanDati = mw.html.create('span'):attr('id', 'dati')
	for k, v in pairs(attrs) do
		spanDati:attr('data-'..k, (args[v] or ''))
	end
	add(tostring(spanDati))

	-- DATI ANAGRAFICI
	local secoloAttivita = p.getSecoloAttivita()
	local secolo = c.concat(secoloAttivita, '/')
	mw.log('Secolo di attivita da wikidata: ' .. secolo)
	
	dati = p.getDatiAutore()
	mw.log(c.printElement(dati))
	
	--DATI ANAGRAFICI da Wikidata
	local birthPlace = getPlaceOfBirth()
	local deathPlace = getPlaceOfDeath()
	
	--pseudonimo P742
	local pseudonimi = c.getClaimValuesByProperty('P742')
	c.subtractTable(pseudonimi, {nomeCognome})
	table.sort(pseudonimi)
	local numeroPseudonimi = c.size(pseudonimi)
	local stringaPseudonimo = c.concat(pseudonimi, "''', '''", "''' e '''")
	
	--nome reale (birth name) P1477
	local nomiReali = c.getClaimValuesByProperty('P1477')
	table.sort(nomiReali)
	local stringaNomeReale = c.concat(nomiReali, "''' o '''")
	
	--alias (da cui vanno esclusi i precedenti campi e il nome "principale")
	local alias = dati.alias
	if (dati.nomeCognome ~= nomeCognome) then
		table.insert(alias, dati.nomeCognome)
	end
	c.subtractTable(alias, pseudonimi)
	c.subtractTable(alias, nomiReali)
	c.subtractTable(alias, {nomeCognome})
	table.sort(alias)
	local stringaAlias = c.concat(alias, "''', '''", "''' e '''")
	
	--inizio CATEGORIE
	
	-- CAT: AUTORI NATI/MORTI NELL'ANNO
	if dati.annoNascita and dati.annoNascita ~= '' then 
		addCat('Nati nel '..dati.annoNascita)
	end
	if c.size(dati.birthDates) > 1 then
		local data1 = dati.birthDates[1]
		local data2 = dati.birthDates[2]
		if c.size(dati.birthDates) > 2 or data1.calendar == data2.calendar then
			if data1.precision >= 9 and data2.precision >= 9 and data1.year == data2.year and data1.precision ~= data2.precision then
				addCat('Autori con date di nascita aventi diversa precisione')
			else
				addCat('Autori con data di nascita incerta')
			end
		end
	end
	if dati.annoMorte and dati.annoMorte ~= '' then 
		addCat('Morti nel '..dati.annoMorte) 
	end
	if c.size(dati.deathDates) > 1 then
		local data1 = dati.deathDates[1]
		local data2 = dati.deathDates[2]
		if c.size(dati.deathDates) > 2 or dati.deathDates[1].calendar == dati.deathDates[2].calendar then
			if data1.precision >= 9 and data2.precision >= 9 and data1.year == data2.year and data1.precision ~= data2.precision then
				addCat('Autori con date di morte aventi diversa precisione')
			else
				addCat('Autori con data di morte incerta')
			end
		end
	end

	if p.isHuman() and dati.ricorrenzaNascita and dati.ricorrenzaNascita ~= '' then
		if dati.ricorrenzaNascita:sub(1, 2) == '8 ' or dati.ricorrenzaNascita:sub(1, 3) == '11 ' then
			addCat("Nati l'"..dati.ricorrenzaNascita)
		else
			addCat('Nati il '..dati.ricorrenzaNascita)
		end
	end
	if p.isHuman() and dati.ricorrenzaMorte and dati.ricorrenzaMorte ~= '' then
		if dati.ricorrenzaMorte:sub(1, 2) == '8 ' or dati.ricorrenzaMorte:sub(1, 3) == '11 ' then
			addCat("Morti l'"..dati.ricorrenzaMorte)
		else
			addCat('Morti il '..dati.ricorrenzaMorte)
		end
	end
		
	-- CAT: AUTORI-INIZIALE
	addCat('Autori')
	local primaLettera = mw.ustring.upper(c.template('Prima lettera', { (args.Cognome or '')..(args.Nome or '') } ))
	addCat('Autori-'..primaLettera)
	
	if dati.gender and dati.gender == 'femmina' then
		addCat('Autrici')
	end
			
	-- CAT: AUTORI OMONIMI (cioè che usano il campo Disambigua per distinguersi da omonimi)
	if args.Disambigua then addCat('Autori omonimi') end

	-- CAT: AUTORI DEL SECOLO
	if secolo == '' then addCat('Autori senza secolo indicato')
	else
		if lang:ucfirst(secolo) == 'Antichità' then addCat('Autori dell\'Antichità')
		else addTmp('Autore/CategorieSecolo', { secolo, cognonome }) end

		-- CAT: AUTORI PER NAZIONALITÀ E SECOLO
		for n in string.gmatch(naz, "([^/]*)") do
			local plur = c.template('Autore/PluraleNazionalità', { n } )
			if plur and plur ~= '' then
				if lang:ucfirst(secolo) == 'Antichità' then
					addCat('Autori '..plur.." dell'Antichità")
				else
					addTmp('Autore/CategorieSecolo', { secolo, cognonome, plur })
				end
			end
		end
	end

	-- CAT: AUTORI PER ATTIVITÀ
	local att = args['Attività']
	if att then
		addTmp('Autore/CategorieAttività', { att, cognonome }) 

		-- CAT: AUTORI PER ATTIVITÀ E SECOLO
		if lang:ucfirst(secolo) == 'Antichità' then
			addTmp('Autore/CategorieAttività', { att, cognonome, 'dell\'Antichità' })
		else
			addTmp('Autore/CategorieAttivitàSecolo', { att, secolo, cognonome })
		end
	else
		addCat('Autori senza attività')
	end 

	-- CAT: AUTORI PER NAZIONALITÀ
	if naz and naz ~= '' then
		mw.log("nazionalità: "..naz)
		for n in string.gmatch(naz, "([^/]*)") do
			if n and n ~= '' then
				local plur = c.template('Autore/PluraleNazionalità', { mw.text.trim(n) } )
				mw.log("plurale nazionalità: "..plur)
				if plur and plur ~= '' then
					addCat('Autori '..plur)
					-- AUTORI PER ATTIVITÀ + NAZIONALITÀ
					if att then addTmp('Autore/CategorieAttività', { att, cognonome, plur }) end
				else
					addCat('Autori di nazionalità non censita')
				end
			end
		end
	else
		addCat('Autori senza nazionalità')
	end

	-- CAT: AUTORI CON/SENZA OPERE
	if (c.pagesInCat('Testi di '..nomeAutore) + c.pagesInCat('Traduzioni di '..nomeAutore)) > 0 then
		addCat('Autori con opere su Wikisource')
	else
		addCat('Autori senza opere su Wikisource')
	end
	
	-- CAT: AUTORI DI TESTI MUSICALI
	if c.pagesInCat('Testi musicati da '..nomeAutore) > 0 then
		addCat('Autori di testi musicali')
	end

	-- CAT: AUTORI CITATI IN OPERE PUBBLICATE
	if c.pagesInCat('Testi in cui è citato '..nomeAutore) > 0 then
		addCat('Autori citati in opere pubblicate')
	elseif c.pagesInCat('Pagine in cui è citato '..nomeAutore) > 0 then
		addCat('Autori citati in pagine non transcluse')
	end

	-- CAT: AUTORI SENZA DATI BIOGRAFICI 
	if ((args['Professione e nazionalità'] or '')..
	    (args['Attività'] or '')..
	    (args['Nazionalità'] or '') ) == '' then
		addCat('Autori senza dati biografici')
	end

	-- CAT: AUTORI VIVENTI
	if dati.annoMorte == nil and tonumber(dati.annoNascita) ~= nil and tonumber(dati.annoNascita) >= 1900 then
		addCat('Autori viventi')
	end

	local wp = c.wikipedia()
	local com = c.commons()
	local wq = c.wikiquote()

	-- CAT: AUTORI SENZA VOCE SUI VARI PROGETTI
	if (wp or '') == '' then 
		addCat('Autori senza voce su Wikipedia')
		if (naz and (naz:lower() == 'italiano' or naz:lower() == 'italiana')) then
			addCat('Autori italiani senza voce su Wikipedia')
		end
	elseif (wp and wp ~= 'it') then
		addCat('Autori con voce su Wikipedia non in italiano')
		if (naz and (naz:lower() == 'italiano' or naz:lower() == 'italiana')) then
			addCat('Autori italiani con voce su Wikipedia non in italiano')
		end
	end
	if (wq or '') == '' then addCat('Autori senza voce su Wikiquote') end
	if (com or '') == '' then addCat('Autori senza voce su Commons') end

	if c.sitelink('vecwikisource') then
		addCat('Autori presenti sul Wikisource veneto')
	end

	-- AUTORI SENZA ELEMENTO SU WIKIDATA
	if not item then
		add(c.template('Pagina non collegata a Wikidata', {}))
		addCat('Autori non collegati a Wikidata')
	end

	if not dati.gender and p.isHuman() then
		addCat('Autori senza proprietà sesso su Wikidata')
	end
	
	if nomeAutore ~= mw.title.getCurrentTitle().text then
		addCat('Nome autore non corretto')
	end
	--FINE CATEGORIE
	
	-- INIZIO BOX	
	add('<div class="boxAutore">');
	
	-- IMMAGINE AUTORE con CATEGORIE
	if dati.immagine then
		add('[[File:'..dati.immagine..'|140px|right|<!--'..(args.Nome or '')..' '..(args.Cognome or '')..'-->]]')
		addCat('Autori con immagine')
	else
		addCat('Autori senza immagine')
	end
	
	--INIZIO TESTO "Nome Cognome (1900-2000), scrittore italiano"

	-- NOME E COGNOME
	add(tostring(mw.html.create('b'):css('white-space', 'nowrap'):wikitext(nomeCognome)))

	--date di nascita e morte
	local datiAnagraficiMancanti = false
    if dati.birthStringa == '...' and dati.deathStringa == '...' then
		-- CAT: AUTORI SENZA DATI ANAGRAFICI 
		addCat('Autori senza dati anagrafici')
		datiAnagraficiMancanti = true
		
		if secolo and secolo ~= '' and c.empty(dati.floruitDates) then
			if not dati.gender or secolo == 'Antichità' then
				add('&nbsp;('..linkSecolo(secolo)..')')
			end
		end
		if (birthPlace and birthPlace ~= '') or (deathPlace and deathPlace ~= '') then
			if birthPlace and birthPlace ~= ''  then dati.birthStringa = birthPlace .. ', ' .. dati.birthStringa end
			if deathPlace and deathPlace ~= '' then dati.deathStringa = deathPlace .. ', ' .. dati.deathStringa end
			add('&nbsp;('..dati.birthStringa..'&nbsp;–&nbsp;'..dati.deathStringa..')')
		end
	elseif dati.birthStringa == dati.deathStringa and (not birthPlace or birthPlace == '') and (not deathPlace or deathPlace == '') then
		add('&nbsp;('..dati.birthStringa..')')
	else
		if birthPlace and birthPlace ~= ''  then dati.birthStringa = birthPlace .. ', ' .. dati.birthStringa end
		if deathPlace and deathPlace ~= '' then dati.deathStringa = deathPlace .. ', ' .. dati.deathStringa end
		add('&nbsp;('..dati.birthStringa..'&nbsp;–&nbsp;'..dati.deathStringa..')')
    end
	
	--floruit (solo in mancanza di date certe)
	if not c.empty(dati.floruitDates) and ((not dati.ricorrenzaNascita or dati.ricorrenzaNascita == '') or (not dati.ricorrenzaMorte or dati.ricorrenzaMorte == '')) then
		add(', '..dati.floruitStringa)
	elseif datiAnagraficiMancanti and secolo and secolo ~= '' then
		if dati.gender and secolo ~= 'Antichità' then
			add(', '..'\'\'[[:w:Floruit|floruit]]\'\' '..linkSecolo(secolo))
		end
	end

	local oa = 'o/a'
	if dati.gender then
		if dati.gender == 'maschio' then oa = 'o'
		elseif dati.gender == 'femmina' then oa = 'a' end
	end

	-- EVENTUALE ALTRO NOME
	if stringaAlias ~= '' then
		add(', not'..oa..' anche come \'\'\''..stringaAlias..'\'\'\'')
	end
	
	-- EVENTUALE PSEUDONIMO
	if stringaPseudonimo ~= '' then
		add(', not'..oa..' anche con ')
		if numeroPseudonimi > 1 then add('gli pseudonimi')
		else add('lo pseudonimo') end
		add(' di \'\'\''..stringaPseudonimo..'\'\'\'')
		addCat('Autori conosciuti con uno pseudonimo')
	end

	-- EVENTUALE NOME REALE
	if stringaNomeReale ~= '' and stringaNomeReale ~= nomeCognome then
		add(', nat'..oa..' \'\'\''..stringaNomeReale..'\'\'\'')
	end

	-- DATI BIOGRAFICI
	if args['Professione e nazionalità'] then
		add(', '..lang:lcfirst(args['Professione e nazionalità']))
	elseif args['Attività'] then
		add(', '..p.attivita(args['Attività']))
		if args['Nazionalità'] then add(' '..args['Nazionalità']) end
	end
	add('.')
	--fine della frase iniziale

	-- INTERPROGETTO
	add(tostring(mw.html.create('div'):css('margin-top', '15px'):wikitext(c.template('Interprogetto'))))

	if args.Nome ~= 'Anonimo' then
		-- LINK ALLA CATEGORIA TESTI DI ...
		catLink('Testi di %s')
		catLink('Testi curati da %s')

		-- LINK ALLA CATEGORIA TESTI MUSICATI DA...
		catLink('Testi musicati da %s', 'Nuvola filesystems folder sound.png')
	end

	-- LINK ALLA CATEGORIA TRADUZIONI DI ...
	catLink('Traduzioni di %s')

	-- LINK ALLA CATEGORIA TESTI IN CUI È CITATO ...
	catLink('Testi in cui è citato %s')

	-- LINK ALLA CATEGORIA PAGINE IN CUI È CITATO ...
	if c.pagesInCat('Testi in cui è citato '..nomeAutore) == 0 then
		catLink('Pagine in cui è citato %s')
	end

	--link a ricerca (per cercare altre citazioni)
	add('<div>[[File:Nuvola_apps_xmag.png|24px]][[Speciale:Ricerca/'..nomeAutore..'|Cerca citazioni su '..nomeAutore..'...]]</div>')
	
	-- CHIUSURA BOX
	add('</div>')

	-- SCHEDE DI AUTORITÀ
	local authcon = cda()
	if authcon == nil or authcon == '' then
		addCat('Autori senza schede di autorità')
	else
		add(authcon)
	end
	
	if dati.dbiLink then
		addCat('Autori presenti sul Dizionario Biografico degli Italiani')
		add(tostring(mw.html.create('div')
			:addClass('controlloAutorita')
			:tag('div')
				:addClass('controlloAutoritaTitle')
				:wikitext('Collegamenti esterni')
				:done()
			:tag('div')
				:wikitext('<div>[http://www.treccani.it/enciclopedia/' .. dati.dbiLink .. '_(Dizionario-Biografico) Dizionario Biografico degli Italiani]</div>')
			:allDone()))
	end

	if c.wikinews() then addCat('Autori con pagina su Wikinews') end
	if c.wikispecies() then addCat('Autori con pagina su Wikispecies') end

	mw.log(output..outputCat)
	return output..outputCat
end

p['attività'] = function(frame)
	return p.attivita(frame.args[1])
end

function p.autoreLinkDBI(frame)
	local page = frame.args[1]
	mw.log('Pagina: '..page)
	
	item = mw.wikibase.getEntity(mw.wikibase.getEntityIdForTitle(page))
	c.setItem(item)
	
	local name = c.getLabel()
	mw.log('Nome: '..name)
	local output = "[["..page..'|'..name.."]]"

	lang, sitelink = c.wikipedia()
	mw.log('WP Link: '..lang..':'..sitelink)
	if lang and sitelink then
		output = output .. ' [[:'..lang..':'..sitelink..']]'
	end
	
	dbiLink = c.getSingleClaimValueByProperty('P1986')
	mw.log('DBI: '..c.printElement(dbiLink))
	if dbiLink then
		output = output .. ' ([http://www.treccani.it/enciclopedia/' .. dbiLink .. '_(Dizionario-Biografico) DBI])'
	end
	return output .. ' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; '..frame:extensionTag('nowiki', output)
end

return p