Documentation[créer] [purger]
local p = {}


--
-- Les modules et sous-modules dont on aura besoin
--
local wd = require "Module:Wikidata"
local mathmod = require "Module:math"
local chartes = require "Module:Chartes"
local demographie = require "Module:Démographie"


-- sous-modules 
p.intro = require("Module:Population de France/Introductions")


local texts = {
	["no item"] = "élément Wikidata non trouvé pour le $param",
	["no datatable"] = "page de table de donnée trouvée pour l'élément $param",
	["invalid datatable"] = "la page de données $param n'a pas pu être correctement analysée",
	["missing param"] = "paramètre manquant : $param",
	["error"] = "erreur dans Module:Population",
	["errorcat"] = "Page avec un problème sur les donnnées démographiques",
	["decrease"] = "en diminution de $num par rapport à $date",
	["increase"] = "en augmentation de $num par rapport à $date",
	["stable"] = "en stagnation par rapport à $date",
	["cat"] = "Article avec module Population de France",
	["graph title"] =  "Histogramme de l'évolution démographique",
	["table title"] = "Évolution de la population",
	["source file"] = "Fichier utilisé : [[commons:Data:$1]]."
}


-- Fonctions d'erreur
local function err(errtype,param)
	local cat, msg
	
	-- message à afficher sur la page
	if errtype == "no item" then
		msg = texts["no item"]
	elseif errtype == "no datatable" then
		msg = texts["no datatable"]
	elseif errtype then
		msg = texts[errtype] or errtype
	else
		msg = texts["error"]
	end
	
	-- catégorie de maintenance à ajouter à la page
	-- une seule catégorie d'erreur. Plus simple et suffisant ?
	cat = texts["errorcat"]
	if cat then
		cat = "[[Category:" .. cat .. "]]"
	end
	if msg then
		msg = '<span style="color:red">' .. mw.ustring.gsub(msg, "$param", (param or "")) .. "</span>"
	end
	return (msg or "") .. (cat or "")
end


local function sources(data, country) -- TODO
	-- todo
end


local function sources_graphique(data, country)
	-- todo
end


-- Récupération des données Commmons, à partir d'un identifiant Wikidata
local function getTable(qid)
	if (not qid) then
		return nil
	end
	
	local filename = wd.formatStatements{entity = qid, property = 'P4179', numval = 1}
	local file = mw.ext.data.get(filename)
	
	if (not filename) or (not file) then
		return nil
	end

	return filename, file
end

local function keepVal(val, fields, params)
	return true
end

local function filterVals(data, fields, filters)
	local year, month, day
	for i, datapoint in pairs(data.data) do
		if frequency == 'yearly' then 
			local f = string.gmatch(str, '%d+') -- analyse des dates ISO utliliser [[Module:Date complexe]] ?
			local y, m, d = f(), f(), f()
			if y and year then
				if (y - year == 0) then
					table.remove(data, i)
					i = i - 1 if true then return error("hello") end
					break
				end
			end
			if (not keepVal(val, fields, params)) then
				table.remove(data, i)
				i = i - 1 
			end
		end
	end
	return data
end


-- essai de récupération du nom de l'entité géographique et de son identifiant Wikidata, en fonction des infos disponibles
local function getIdAndName(id, name)
	if (not id) then -- si pas d'id Wikidata, prendre celui de la page active
		id = mw.wikibase.getEntityIdForTitle(name or  mw.title.getCurrentTitle().subpageText)
	end
	if (not name) then
		name = wd.getLabel(name) or mw.title.getCurrentTitle().subpageText
	end
	return id, name
end

-- Cherche le numéro de colonne du fichier Commons pour les différentes infos nécessaires
local function analyseData(data)
	local fields = data.schema.fields
	local fieldnums = {}

	for num, j in ipairs(fields) do
		if string.find(fields[num].name, "P585") then
			fieldnums["date"] = num
		elseif string.find(fields[num].name, "P1082") then
			fieldnums["population"] = num
		elseif string.find(fields[num].name, "P459") then
			fieldnums["method"] = num
		elseif string.find(fields[num].name, "P1013") then
			fieldnums["criterion"] = num
		elseif string.find(fields[num].name, "S248") then
			fieldnums["source"] = num
		elseif string.find(fields[num].name, "S854") then
			fieldnums["sourceurl"] = num
		end
	end
	
	-- exits if no field for population or date
	if not (fieldnums["population"] and fieldnums["date"]) then
		return error()
	end
	return fieldnums
end

-- extrait l'année d'une date en format pseudoISO, à améliorer
local function getYear(data, fields)
	local d = data[fields.date]
	d = string.gsub(d, "+", "")
	return string.sub(d, 1, 4)
end

local function notes(data, vnom, country)
	return nil
end

local function clean(val)
	if (val == nil) then
		return val
	end
	if (type(val) ~= "string") then
		return val
	end
	if ( mw.text.trim(val) == "") then
		return nil
	end
	return val
end
	
local function getData(id, name)
	local id, name =  getIdAndName(id, name)
	if (not id) then
		return error(), "no item", name
	end
	
	local datafile, data = getTable(id, name)
	
	if not (datafile and data) then
		return error(), "no datable", id
	end
	
	local fields = analyseData (data)
	
	if (not fields) or (fields == nil) then
		return error(), "invalid datatable", datafile
	end
	
	return name, id, datafile, data, fields
end


function p.tableau_m(param)
	local dataloaded, name, id, datafile, data, fields = pcall(getData, param["id"], param["nom"])
	
	if not dataloaded then
		return err(name, id) -- retourne une erreur avec les deux paramètres retournés en cas d'erreur par la fonction getData
	end

	local tableFormat = {
	-- on prépare les paramètres à fournir au module Démographie
	["largeur_tableau"] = "48em",
	["taille_police"] = "95%",
	["marge_interlignes"] = "10px",
	["hauteur_lignes"] = "110%",
	["population_fond"] = "#F9F9F9",
	["style_notes"] = "centré",
	}


	-- couleur (on passe par Chartes avec un "truc" pour commune nouvelle)
	local dest = tableFormat
	if (data["division"] == "commune nouvelle") then
		dest["annees_fond"] = chartes.charte_m("geographie", "secondaire", "commune", true)
	else
		dest["annees_fond"] = chartes.charte_m("geographie", "secondaire", data["division"], true)
	end

	-- le titre du tableau
	dest["titre"] = (param["titre"] or texts["table title"])


	-- sélection des données
	local wantedvals = filterVals(data, fields, params)
	
	-- passage des paramètres au tableau
	for i, datapoint in pairs(wantedvals.data) do
		local year = tonumber(getYear(datapoint, fields))
		dest[year] = datapoint[fields["population"]]
	end
	
	-- on effectue l'appel terminal et on retourne (et on ajoute la catégorie)
	return demographie.demographie_m(dest) .. "[[Category:" .. texts["cat"] .. "]]"
end


-- fonction "wrapper" pour la précédente, pour appel depuis un modèle
function p.tableau(frame)
	local pframe = frame:getParent()
	local param = {}
	param["nom"] = clean(frame.args["nom"] or pframe.args["nom"] or nil)
	param["id"] = clean(frame.args["id"] or pframe.args["id"] or nil)
	param["titre"] = clean(frame.args["titre"] or pframe.args["titre"] or nil)

	-- on appelle la vraie fonction
	return p.tableau_m(param)
end

--  Fonction créant un Wikitexte présentant les données de population 
function p.introduction_m(param)
	if true then return
		"todo : textes d'intro "
	end
	local resu
	
	-- le nom de la cible (param. nom ou titre page)
	local id, name =  getIdAndName(param["id"], param["nom"])
	if not id then return
		err("no item", name)
	end
	local datafile, data = getTable(id, name)

	local success, fields = pcall(analyseData, data)
	if (not success) or (fields == nil) then
		return err("invalid datatable", datafile)
	end

	local country = wd.formatStatements{entity = id, property = "P17", numval = 1, displayformat = "raw"}
	if country == "Q142" then
		return require "Module:Population/France".introduction(param, id)
	end
	return nil
end

-- fonction "wrapper" pour la fonction précédente, pour appel depuis un modèle
function p.introduction(frame)
	local pframe = frame:getParent()
	local param = {}
	param["nom"] = clean(frame.args["nom"] or pframe.args["nom"] or nil)
	param["id"] = clean(frame.args["id"] or pframe.args["id"] or nil)

	return p.introduction_m(param)
end

-- fonction "wrapper" pour la fonction précédente, pour appel depuis un modèle
function p.graphique(frame)
	local pframe = frame:getParent()
	local param = {}
	param["nom"] = clean(frame.args["nom"] or pframe.args["nom"] or nil)
	param["id"] = clean(frame.args["id"] or pframe.args["nom"] or nil)
	param["titre"] = clean(frame.args["titre"] or pframe.args["titre"] or nil)
	param["seuil"] = clean(frame.args["seuil"] or pframe.args["seuil"] or nil)

	return p.graphique_m(param)
end

--[[
  Retourne une phrase décrivant la variation de population de la commune
    courante (ou "nom") ou un pictograme si "type" = "picto"
--]]
function p.variation_texte_m(param)

	local dataloaded, name, id, datafile, data, fields = pcall(getData, param["id"], param["nom"])
	if not dataloaded then
		return err(name, id) -- retourne une erreur avec les deux paramètres retournés en cas d'erreur par la fonction getData
	end
	
	local pop = data.data[#data.data][fields.population]
	local year = getYear( data.data[#data.data], fields)
	
	local prevyear, prevpop
	for i, j in ipairs(data.data) do
		local d = getYear( data.data[#data.data +1 -i], fields)
		if (year - d) == 5 then
			prevyear = d
			prevpop = data.data[#data.data +1 -i][fields.population]
			break
		end
	end
	
	if not (prevpop and prevyear) then
		return err("invalid datatable", datafile)
	end
	local pctdiff = mathmod._round(100 * (pop - prevpop)/prevpop, 2)
	local txt
	if pctdiff > 0 then
		txt = texts["increase"]
	elseif pctdiff < 0 then
		txt = texts["decrease"]
		pctdiff = - pctdiff
	else
		txt = texts["stagnation"]
	end
	
	txt = mw.ustring.gsub(txt, "$date", year)
	txt = mw.ustring.gsub(txt, "$num", mw.language.getContentLanguage():formatNum(pctdiff) .. "%")
	
	if (param["type"] ~= "picto") then
		return txt
	end
	-- on crée le pictogramme
	if (pctdiff > 0) then
		return "[[Fichier:Increase2.svg|11px|link=|class=noviewer|" .. txt .. "]]"
	elseif (pctdiff < 0) then
		return "[[Fichier:Decrease2.svg|11px|link=|class=noviewer|" .. txt .. "]]"
	else -- stagnation
		return "[[fichier:steady.svg|10px|link=|class=noviewer|" .. txt .. "]]"
	end
end

-- fonction "wrapper" pour la fonction précédente, pour appel depuis un modèle
function p.variation_texte(frame)
	local pframe = frame:getParent()
	local param = {}
	param["nom"] = clean(frame.args["nom"] or pframe.args["nom"] or nil)
	param["type"] = frame.args["type"] or pframe.args["type"] or nil

	return p.variation_texte_m(param)
end

function p.graphique_m(params)
	local resu

	local dataloaded, name, id, datafile, data, fields = pcall(getData, params["id"], params["nom"])
	
	if not dataloaded then
		return err(name, id) -- retourne une erreur avec les deux paramètres retournés en cas d'erreur par la fonction getData
	end
	
	local years, pops = {}, {}


	-- filtre de données
	params.frequency = params.frequency or 'yearly'-- fréquence maximale des données
	local wantedvals = filterVals(data, fields, params)
	for i, datapoint in pairs(wantedvals.data) do
		local year = tonumber(getYear(datapoint, fields))
		local pop = datapoint[fields["population"]]
		table.insert(years, year)
		table.insert(pops, pop)
	end
	

	-- rendu du module par [[Template:Graph:Chart]] à Lua-iser
	years, pops = table.concat(years, ","), table.concat(pops, ",")
	local graph = mw.getCurrentFrame():expandTemplate({
		title = "Graph:Chart",
		args =  {
			x = years,
			y = pops,
			width = params.width or "600",
			height = params.height or "340",
			colors = params.colors or "green",
			linewidth = params.linewidth,
			showSymbols = params.showSymbols,
			symbolsShape = params.symbolsShape or "cross",
			yAxisMin= params.yAxisMin or 0,
			yGrid = years
		}}
	)

	local sourcestr = mw.ustring.gsub(texts["source file"] , "$1", datafile) 
	sourcestr = sourcestr .. " (TODO : mise en forme des sources)"

	-- rendu final : div html comprenant le graph lui même + le titre et les sources
	local out = 
	mw.html.create("div")
		:tag('div')
			:css{['text-align'] = 'center'}
			:wikitext(params.titre or texts["graph title"])
			:done()	
		:tag('div')
			:wikitext(graph)
			:done()
		:tag('div')
			:wikitext(sourcestr)
			:done()
		:allDone()
	return tostring(out)
end

-- fonction "wrapper" pour la fonction précédente, pour appel depuis un modèle
function p.graphique_test(frame)
	--local pframe = frame:getParent()
	return p.graphique_m(frame.args)
end

return p