-- Modulo per implementare il template Cronologia valutazioni
-- copiato senza vergogna da modulo:Navbox


-- Configurazione
local cfg = mw.loadData("Modulo:Cronologia valutazioni/Configurazione")

local function dump(t, ...)
	local args = {...}
	for _, s in ipairs(args) do
		table.insert(t, s)
	end
end

local errors = {}
local we_got_error = false
local debug = false

-------------------------------------------------------------------------------
--                           Funzioni di utilità
-------------------------------------------------------------------------------

-- Ritorna true se il nome dell'argomento è valido
local function isValidArg(name, validArgs)
	local ret = cfg.HistoryBoxArgs[name] ~= nil

	if not ret then
		local _, _, base_name, id = mw.ustring.find(name, "^(%a+)(%d+)$")
		if cfg.HistoryBoxNumberedArgs[base_name] and id then ret = tonumber(id) end
	end
	return ret
end

-- Ritorna gli argomenti passati al modulo, scartando quelli senza nome,
-- quelli contenenti stringhe vuote e i non riconosciuti.
local function getArgs(frame, isSubgroup)
	local ret = {}

	for k, v in pairs(frame:getParent().args) do
		if type(k) == "string" and v ~= "" then
			if isValidArg(k) then
				ret[k] = v
			else
				dump(errors, "Argomento non riconosciuto: '" .. k .. "'")
			end
		end
	end
	return ret
end

-- Ritorna gli ID degli argomenti azioneN presenti
local function getListIds(args)
	local ret = {}

	for k, v in pairs(args) do
		local id = k:match("^azione(%d+)$")
		if id then table.insert(ret, tonumber(id)) end
	end
	table.sort(ret)
	return ret
end

-- Verica se si verifica una condizione di errore
-- errors_check: lista di array con le condizioni da controllare, questi hanno la struttura:
----- condition: il valore (vero o falso) da controllare
----- checks: lista di stati da controllare
----- msg: messaggio di errore se gli stati hanno tutti il valore uguale a condition
-- restituisce nil se non ci sono errori, altrimenti una stringa con un errore per riga
local function checkErrors(errors_check, status, errors_msg)
	if errors_check == nil then return end
	for _, check in ipairs(errors_check) do
		local check_result = true
		for _, par_to_test in ipairs(check.checks) do
			check_result = check_result and (status[par_to_test] == check.condition)
		end
		if check_result then errors_msg[#errors_msg+1] = check.msg  end
	end
end

local function wrap_errors(errors_msg)
	if  #errors_msg > 0 then
		local errors = {}
		for _, e in ipairs(errors_msg) do
			errors[#errors+1] =  '<div class="error">' .. e .. '</div>'
		end
		if #errors > 0 then we_got_error = true end
		return table.concat(errors)
	end
	return ''
end

-------------------------------------------------------------------------------
--                             Classe HistoryBox
-------------------------------------------------------------------------------

local HistoryBox = {}

function HistoryBox:new(args)
	local self = {}
	setmetatable(self, { __index = HistoryBox,
						 __tostring = function(t) return self:__tostring() end })
	self.args = args
	-- costruzione table
	self.status = {}
	self.actions = {}
	self.counters = {}
	self.categories = {}
	self.avviso_sotto = "" -- da implementare
	for k,v in pairs(cfg.status_toggle) do
		self.status[k] = v
	end
	self.current_title = mw.title.getCurrentTitle()
	self.name = self.args['debug_name'] or self.current_title.text
	self.processedArgs = {}
	self.listIds = getListIds(self.args)
	self:__PreprocessArgs()
	self:__SetupBaseNode()
	self:__infoDiv()
	if #self.listIds ~= 1 then
		self:__RowContent() end
	if  self.current_title.namespace == 1 or debug then
		self:__SetCategories()
	end
	return self
end

function HistoryBox:__tostring()
	categorie = table.concat(self.categories)
	if debug and categorie ~= '' and self.current_title.namespace ~= 1 then
		categorie = '<br>Categorie: ' .. table.concat(self.categories, ", ") 
	end
	ret = tostring(self.BaseNode) .. self.avviso_sotto .. wrap_errors(errors) .. categorie
	if we_got_error and not(self.args['no_categoria_errore']) then
		ret = ret .. '[[Categoria:Errori di compilazione del template Procedure]]'
	end
	return ret
end

function HistoryBox:__PreprocessArgs()
	local currentTitle = mw.title.getCurrentTitle()
	for _, Id in ipairs(self.listIds) do
		local row = { }
		local action = mw.ustring.upper(self.args['azione' .. Id] or '')
		action = cfg.action_alias[action] or action
		local row_action = cfg.actions[action]
		if row_action == nil then
			dump(errors, "Non riconosciuta 'azione" .. Id .. " = " .. action .. "'")
		else
			-- leggo i dati della riga
			local date = self.args['data' .. Id]
			local collegamento = self.args['collegamento' .. Id]
			local codice = self.args['codice' .. Id]
			local name = self.args['nome' .. Id] or self.name
			local commento = self.args['commento' .. Id]
			if commento then
				row['comment'] = "'''commento''': ''" .. commento .. "''" end
			local errors_msg = {}
			local error_msg = checkErrors(row_action['check_errors'], self.status, errors_msg)
			local msg, link_label
			-- verifico che l'esito esista e lo gestisco
			local result = mw.ustring.upper(self.args['esito' .. Id] or '')
			if result == '' and row_action['default_result'] then
				result = row_action['default_result']
			elseif row_action['result_alias'] then
				result = row_action.result_alias[result] or result
			end
			local row_result = row_action.valid_result[result]
			if row_result then
				-- controllo se le condizioni sono tutte soddisfatte
				checkErrors(row_result['check_errors'], self.status, errors_msg)
				-- setto le variabili relative all'azione
				if row_result['set'] then
					for _,to_set in ipairs(row_result.set) do
						self.status[to_set[2]] = to_set[1]
					end
				end
				msg = row_result['msg']
				link_label = row_result['link_label']
				row['icon'] = row_result['icon']
			else
				if not(row_action['no_result']) then
					local default_result = row_action['default_result'] or 'xxx'
					local list_result = {}
					for kr,vr in pairs(row_action.valid_result) do
						if kr == default_result then
							list_result[#list_result+1] = "<b>" .. mw.ustring.lower(kr) .. "</b>"
						else
							list_result[#list_result+1] = mw.ustring.lower(kr)
						end
					end
					dump(errors, "Esito '" .. result .. "' non riconosciuto, esiti validi: " .. table.concat(list_result, "/"))
				end
				row['result'] = '-'
			end
			msg = msg or row_action['msg']
			link_label = link_label or row_action['link_label']
			row['icon'] = row['icon'] or row_action['icon']
			
			-- analizzo casi specifici
			-- per le rimozioni da riconoscimento qualità è necessario indagare quale fosse il riconoscimento per cambiare i giusti "status"
			if action == "RRQ" then
				if result == "RIMOSSA" then
					if self.status.is_vetrina then
						self.status.is_vetrina = false
						self.status.removed_vetrina = true
						row['icon'] = 'VetrinaEsclusa'
					elseif self.status.is_vdq then
						self.status.is_vdq = false
						self.status.removed_vdq = true
						row['icon'] = 'VdqEsclusa'
					end
				elseif result == "MANTENUTA" or result == "ANNULLATA" then
					if self.status.is_vetrina then
						row['icon'] = 'Vetrina'
					elseif self.status.is_vdq then
						row['icon'] = 'Vdq'
					end
				end
			end
			-- per le valutazioni VdQ scadute generalmente non c'è pagina a cui linkare
			if action == "OLDVDQ" and result == "SCADUTA" then
				collegamento = collegamento or "no"
			end
			-- per le procedure di cancellazione aggiungo in automatico dettagli sul tipo di procedura
			if action == "PDC" then
				if (result == "MANTENUTA" or result=="CANCELLATA") then
					local vote_yes =  self.args['si' .. Id]
					local vote_no = self.args['no' .. Id]
					if vote_yes and vote_no then
						msg = msg..' in seguito a voto della comunità con risultato <span style="color:green;">' .. vote_yes .. '</span> a <span style="color:red;">' .. vote_no .. '</span>.'
						link_label = "votazione"
					else
						msg = msg..' in seguito a decisione consensuale.'
					end
				end
			end
			-- per le vecchie valutazioni per la vetrina si distinguono le votazioni dalle valutazioni in base alla data
			if (action == "OLDSRQ" or action == "OLDRRQ") and date then
				_, _, _, month, year = string.find(date, "^(%d?%d?) ?(%a*) ?(%d%d%d%d)$")
				year = tonumber(year)
				if year <= 2008 or (year == 2009 and (month == 'gennaio' or month == 'febbraio')) then
					link_label = "votazione"
				end
			end
			
			-- genero i dati di output rimanenti
			-- link alla versione
			if codice then
				date = date or "versione " .. codice
				row['date'] = '[' .. tostring(mw.uri.fullUrl( name, {oldid=codice} )) .. " " .. date .. "]"
			else
				row['date'] = date or "-"
			end
			-- aggiorno il counter associato all'azione
			if collegamento ~= "no" then
				if self.counters[row_action.counter] then
					self.counters[row_action.counter] = self.counters[row_action.counter] + 1
				else
					self.counters[row_action.counter] = 1
				end
			end
			-- Tenta di generare il link di default se collegamento non è definito 
			if collegamento == nil then
				if row_action['default_position'] then
					if row_action['use_date_for_position'] then
						if date then
							_, _, _, month, year = string.find(date, "^(%d?%d?) ?(%a+) (%d%d%d%d)$")
							if month and year and tonumber(year) >= 2012 then
								month = string.lower(month)
								month = string.gsub(month, "^%l", string.upper)
								collegamento = mw.message.newRawMessage( row_action.default_position,
								                                         { name or self.name, month, year}
								                                        ):plain()
							elseif action == "LSC" then
						 		row['consiglio']= "Le procedure prima del 2012 non venivano archiviate, perciò possono essere trovate solo nella [http://it.wikipedia.org/w/index.php?title=Wikipedia:Lo_sapevi_che/Valutazione&action=history cronologia] della pagina di valutazione."
							end
						-- per le valutazioni LSC non c'è collegamento automatico senza data, perciò il template aiuta l'utente a trovare la valutazione manualmente
						else
						 	if action == "LSC" then
						 		row['consiglio']= "La procedura si può trovare nell'[[Wikipedia:Lo sapevi che/Valutazione/Archivio|archivio]], oppure nella [http://it.wikipedia.org/w/index.php?title=Wikipedia:Lo_sapevi_che/Valutazione&action=history cronologia] della pagina di valutazione."
					 		end
						end
					else
						if self.counters[row_action.counter] == 1 then
							collegamento = mw.message.newRawMessage( row_action.default_position[1],
							                                         { name or self.name} ):plain()
						else
							collegamento = mw.message.newRawMessage( row_action.default_position[2],
							                                         { name or self.name,
							                                           self.counters[row_action.counter]
							                                         } ):plain()
						end
					end
				end
			elseif collegamento == "no" then
				collegamento = nil
			end
			row['msg'] = msg
			row['collegamento'] = collegamento
			link_label = link_label or "discussione"
			row['label'] = link_label
			if #errors_msg>0 then
				row['errors_msg'] = wrap_errors(errors_msg)
			end
			table.insert(self.processedArgs, row)
		end
	end
end

function HistoryBox:__SetupBaseNode()
	self.BaseNode = mw.html.create("h2")
		:cssText("border: none; margin-left: 8%; margin-bottom: 0.4em")
		:done()
	:tag("table")
		:addClass("cronoval")
		:addClass("noprint") -- serve a qualcosa? tanto va nelle discussioni
		:cssText("margin: 5px 10%; border: thin solid #a7d7f9; background-color: #EAF7ED;")
	if #self.listIds ~= 1 then
		self.BaseNode:addClass("mw-collapsible") end
end

function HistoryBox:__infoDiv()
	local stato_descriptor, commento, consiglio, icon = self:__getSummaryInfo()
	self.BaseNode:tag("tr")
		:addClass("cronoval_h")
		:tag("td")
			:addClass("avviso-immagine")
			:cssText("width:52px;")
			:wikitext("[[File:" .. (cfg.icons[icon] or cfg.icons['default']) .. "|40px]]")
			:done()
		:tag("td")
			:addClass("avviso-testo")
			:wikitext(stato_descriptor)
			:tag ("div")
				:cssText ("font-size: 90%;")
				:wikitext (commento)
				:done ()
			:tag ("div")
			   	:wikitext (consiglio)
end

function HistoryBox:__getSummaryInfo()
	local ret = {}
	if #self.processedArgs == 1 then
		local row = self.processedArgs[1]
		if row['date'] ~= "-" then
			ret = {"In data "..row['date']} end
		ret[#ret + 1] = mw.message.newRawMessage (row['msg'], {" la voce <b>"..mw.title.subjectNsText.."</b>"}):plain()
		local ret_consiglio= ""
		if row['collegamento'] then
			ret_consiglio= "Consulta la '''[["..row['collegamento'].."|pagina della "..row['label'].."]]''' per eventuali pareri e suggerimenti."
		elseif row['consiglio'] then
			ret_consiglio= row['consiglio'] end
		return table.concat(ret), row['comment'], ret_consiglio, row['icon']
	else
		local s = self.status
		local c = self.counters
		c['Vetrina'] = (c['OLDSRQ'] or 0) + (c['Vetrina'] or 0)
		local incipit_done = true
		local icon
		ret[1] = '<b>$1</b>'
		if s.is_vetrina then
			ret[#ret+1] = ' è una [[Wikipedia:Vetrina|voce in vetrina]]'
			icon = 'Vetrina'
		elseif s.is_vdq then
			ret[#ret+1] = ' è una [[Wikipedia:Voci di qualità|voce di qualità]]'
			icon = 'Vdq'
			if s.removed_vetrina then
				 ret[#ret+1] = ', in precedenza è stata in vetrina'
			end
		elseif s.removed_vetrina then
			ret[#ret+1] = ' è  stata una [[Wikipedia:Vetrina|voce in vetrina]]'
			icon = 'VetrinaEsclusa'
		elseif s.removed_vdq then
			ret[#ret+1] = ' è  stata una [[Wikipedia:Voci di qualità|voce di qualità]]'
			icon = 'VdqEsclusa'
		elseif c['SRQ'] then
			ret[#ret+1] = ' è stata segnalata per un [[Wikipedia:Riconoscimenti di qualità/Segnalazioni|riconoscimento di qualità]]'
			if c['SRQ'] + c['Vetrina'] + (c['Vdq'] or 0) == 1 then
				ret[#ret+1] = ', ma non ha superato la valutazione'
			else
				ret[#ret+1] = ', ma non ha superato le valutazioni'
			end
			icon = 'QualitaEsclusa'
		elseif c['Vetrina'] >= 1 and c['Vdq'] then
			ret[#ret+1] = ' non ha superato le valutazioni per essere giudicata una [[Wikipedia:Vetrina|voce in vetrina]] o [[Wikipedia:Vetrina|di qualità]]'
			icon = 'QualitaEsclusa'
		elseif c['Vetrina'] >= 1 then
			ret[#ret+1] = ' non ha superato la valutazione per essere inserita in [[Wikipedia:Vetrina|vetrina]]'
			icon = 'VetrinaEsclusa'
		elseif c['Vdq'] then
			ret[#ret+1] = ' non ha superato la valutazione per essere giudicata una [[Wikipedia:Vetrina|voce di qualità]]'
			icon = 'VdqEsclusa'
		else
			incipit_done = false
		end
		if c['Vaglio'] then
			if incipit_done then
				ret[#ret+1] = '. La voce'
			end
			ret[#ret+1] = ' è stata sottoposta a '
			if c['Vaglio'] > 1 then
				ret[#ret+1] = 'più procedure di [[Wikipedia:Vaglio|vaglio]]'
			else
				ret[#ret+1] = 'una procedura di [[Wikipedia:Vaglio|vaglio]]'
			end
			incipit_done = true
		end
		if c['Lo sapevi che'] then
			if incipit_done then
				ret[#ret+1] = '. La voce'
			end
			if s.was_LSC then
				ret[#ret+1] = ' è comparsa nella rubrica [[Wikipedia:Lo sapevi che|Lo sapevi che]]'
				icon = icon or "LoSapeviChe"
			else
				ret[#ret+1] = ' è stata proposta per la rubrica [[Wikipedia:Lo sapevi che|Lo sapevi che]], ma è stata respinta'
			end
			incipit_done = true
		end
		if c['Cancellazione'] then
			if incipit_done then
				ret[#ret+1] = '. La voce'
			end
			ret[#ret+1] = ' è stata sottoposta a'
			if c['Cancellazione'] > 1 then
				ret[#ret+1] = ' più procedure di [[Wikipedia:Pagine da cancellare|cancellazione]]'
			else
				ret[#ret+1] = ' una procedura di [[Wikipedia:Pagine da cancellare|cancellazione]]'
			end
			if s.was_deleted then
				ret[#ret+1] = ', ma successivamente è stata ripristinata'
			else
				if c['Cancellazione'] > 1 then
					ret[#ret+1] = ', che non sono state accolte'
				else
					ret[#ret+1] = ', che non è stata accolta'
				end
			end
			incipit_done = true
		end
		if incipit_done then
			ret[#ret+1] = '.'
			return mw.message.newRawMessage(table.concat(ret), {self.name}):plain(), nil,
			       "Consulta le varie procedure di valutazione per eventuali pareri e suggerimenti", (icon or "default")
		end
		return ''
	end
end

function HistoryBox:__RowContent()
	local listIds, altStyle
	-- crea la tabella che contiene i dati collassati
	local dataTableContent =
		self.BaseNode:tag("tr")
			:tag("td")
				:attr("colspan", "2")
				:tag("table")
					:addClass("cronoval_elenco")
					:cssText("clear: both; width: 100% !important; border-collapse: collapse;") -- l'important è per la versione mobile
	for row_number, row in ipairs(self.processedArgs) do
		local row_element = dataTableContent:tag("tr")
			:cssText("background-color: white; border: thin solid #D8D8D8;")
		local icon = ''
		if row['icon'] then
			icon = '[[File:' .. cfg.icons[row.icon] .. "|20px]]"end
		local conclusione= ""
		if row.collegamento then
			conclusione= " '''[[" .. row.collegamento .. "|Vedi " .. row.label .. "]]'''"
		elseif row.consiglio then
			conclusione= " " .. row.consiglio end
		local data_element = row_element
		:tag("td")
			:cssText("width: 22px; padding:0 2px;")
			:wikitext(icon)
			:done()
		:tag("td")
			:addClass("plainlinks")
			:cssText("text-align: right; padding-right: 0.3em; width: 10em; vertical-align: top;")
			:wikitext(row.date..":")
			:done()
		:tag("td")
			:wikitext(mw.message.newRawMessage (row.msg, {"la voce"}):plain() .. conclusione)
		if row['comment'] then
			data_element
				:tag('div')
				:cssText("font-size: 90%;")
				:wikitext(row.comment)
		end
		if  row['errors_msg'] then
			data_element:tag("div"):wikitext(row['errors_msg'])
		end
	end
end

function HistoryBox:__SetCategories()
	-- Aggiunge le categorie per la pagina secondo il suo stato
	if self.status.tried_qualita then
		self.categories[#self.categories+1] = '[[Categoria:Voci escluse da un riconoscimento di qualità]]'
	end
	if self.status.tried_vetrina then
		self.categories[#self.categories+1] = '[[Categoria:Voci escluse dalla vetrina]]'
	end
	if self.status.tried_vdq then
	   self.categories[#self.categories+1] = '[[Categoria:Voci escluse dalle voci di qualità]]'
	end
	if self.status.tried_LSC then
		self.categories[#self.categories+1] = '[[Categoria:Voci scartate nella rubrica Lo sapevi che]]'
	end
	if self.status.removed_vetrina then
		self.categories[#self.categories+1] = '[[Categoria:Voci rimosse dalla vetrina]]'
	end
	if self.status.removed_vdq then
		self.categories[#self.categories+1] = '[[Categoria:Voci rimosse dalle voci di qualità]]'
	end 
	if self.status.scaduta_vdq then
	   self.categories[#self.categories+1] = '[[Categoria:Segnalazioni voci di qualità scadute]]'
	end
	if self.counters['Vaglio'] then
		self.categories[#self.categories+1] = '[[Categoria:Voci vagliate]]'
	end
	if self.status.was_LSC then
		self.categories[#self.categories+1] = '[[Categoria:Voci pubblicate nella rubrica Lo sapevi che]]'
	end
	if self.current_title.namespace ~= 1 then
		for i,_ in ipairs(self.categories) do
			self.categories[i] = self.categories[i]:gsub('%[%[', '')
			self.categories[i] = self.categories[i]:gsub('%]%]', '')
		end
	end
end

function HistoryBox:__Status()
	local ret = {}
	for k,_ in pairs(cfg.status_toggle) do
		ret[#ret+1] = k .. ': ' .. tostring(self.status[k])
	end
	return table.concat(ret, '; ')
end
-------------------------------------------------------------------------------
--                                  API
-------------------------------------------------------------------------------
local p = {}

-- Entry-point per {{HistoryBox}}
function p.historybox(frame)
	return tostring(HistoryBox:new(getArgs(frame)))
end

-- Ritorna la tabella di configurazione
function p.actiontable(frame)
	local tableNode = mw.html.create("table")
		:addClass('wikitable')
		:addClass('sortable')
	tableNode:tag('tr')
		:tag('th'):wikitext('Azione'):done()
		:tag('th'):wikitext('Alias azione'):done()
		:tag('th'):wikitext('Descrizione'):done()
		:tag('th'):wikitext("Esiti<br /><small>In grassetto l'esito di default</small><br /><small>Tra parentesi gli alias</small>"):done()
		:tag('th'):wikitext("Collegamento di default")
	for kaction, vaction in pairs(cfg.actions) do
		local table_row = tableNode:tag('tr')
		table_row:tag('td')
			:wikitext(kaction)
		local alias_action = {}
		for k, v in pairs(cfg.action_alias) do
			if v == kaction then
				alias_action[#alias_action+1] = mw.ustring.lower(k)
			end
		end
		table_row:tag('td')
			:wikitext(table.concat(alias_action, ',<br />'))
		table_row:tag('td')
			:wikitext(vaction['description'] or '&nbsp;')
		local default_result = vaction['default_result'] or 'xxx'
		local td_result = table_row:tag('td')
		if vaction.valid_result then
			local first_result = true
			for kr,vr in pairs(vaction.valid_result) do
				if first_result then
					first_result = false
				else
					td_result:tag('br'):done()
				end
				if kr == default_result then
					td_result:tag("b"):wikitext(mw.ustring.lower(kr))
				else
					td_result:wikitext(mw.ustring.lower(kr))
				end
				if vaction['result_alias'] then
					local result_alias = {}
					for ka, va in pairs(vaction.result_alias) do
						if va == kr then result_alias[#result_alias+1] = mw.ustring.lower(ka) end
					end
					if #result_alias > 0 then td_result:wikitext(" (" .. table.concat(result_alias, ", ") .. ")" ) end
				end
				td_result:wikitext(' --> ' .. (vr['msg'] or '???') )
			end
		else
			td_result = table_row:wikitext("-")
		end
		local default_position = '&nbsp;'
		if vaction['default_position'] then
			if vaction['use_date_for_position'] then
				default_position = mw.message.newRawMessage( vaction.default_position, { '<nome>', '<mese>', '<anno>'}):plain()
			else
				default_position = mw.message.newRawMessage( vaction.default_position[1] .. '<br />' .. vaction.default_position[2],
				                                             { '<name>', '<n>'} ):plain()
			end
		end
		table_row:tag('td'):wikitext(default_position)
	end
	return tostring(tableNode)
end

return p