local getArgs = require('Module:Arguments').getArgs
local errorCategory = 'Categoria:Errors reported by Module Lineage'
local mwHtml = getmetatable( mw.html.create ).__index

function mwHtml:attrIf
 if cond then
 return self:attr
 else
 return self
 end
end

function mwHtml:cssIf
 if cond then
 return self:css
 else
 return self
 end
end

function mwHtml:wikitextIf
 if cond then
 return self:wikitext( value1 )
 elseif value2 ~= nil then
 return self:wikitext( value2 )
 else
 	return self
 end
end

local p = {}
local pers = {}
local tabella = {}

local function errhandler(msg)
	local cat = mw.title.getCurrentTitle.namespace == 0 and errorCategory or ''
	return string.format
end

local function dividi(dati)
	local n = 1
	local resto = 0
	local nx,px
	while (dati[n]) do n = n + 1 end
	n = n-1
	for m = 4, n, 4 do
		nx = tonumber(dati[m-3])
		px = tonumber(dati[m-2])
		if nx then
			if px then
				if pers[nx] then
					error(string.format)
				else
					pers[nx] = { padre = px, testo = dati[m-1], nota = dati[m], id = -1, x = -1, y = -1, sp = 0, figli = {} }
				end
			else
				error(string.format)
			end
		else
			error(string.format)
		end
		resto = n-m
	end
	if resto > 0 then
		error(string.format('Erroneous number of data %d (elementi in più: %d)',n))
	end
end

local function organizza
	local nn = 1
	pers[pid].y = y
	if (not tabella[y]) then tabella[y] = {} end
	table.insert
	for i, v in pairs(pers[pid].figli) do
		pers[v].id = i
		nn = nn + organizza
	end
	return nn
end

local function limSx
	if (dt[pers[pid].y]) then
		dt[pers[pid].y] = math.min
	else
		dt[pers[pid].y] = pers[pid].x + delta
	end
	for _, v in pairs(pers[pid].figli) do
		dt = limSx
	end
	return dt
end

local function limDx
	if (dt[pers[pid].y]) then
		dt[pers[pid].y] = math.max
	else
		dt[pers[pid].y] = pers[pid].x + delta
	end
	for _, v in pairs(pers[pid].figli) do
		dt = limDx
	end
	return dt
end

local function riallinea
	local distanza = n2 - n1
	local vrf = 0
	local pos, inizio, passo
	if (distanza > 1) then
		inizio = pers[pers[pid2].figli[n1]].x
		passo = (pers[pers[pid2].figli[n2]].x - inizio)/distanza
		for cc=1, (distanza-1) do
			pos = inizio + math.floor(cc*passo)
			if (pos - pers[pers[pid2].figli[n1+cc]].x > 0) then
				pers[pers[pid2].figli[n1+cc]].x = pos
				pers[pers[pid2].figli[n1+cc]].sp = pos
			end
		end
		vrf = 1
	end
	return vrf
end

local function verifica(pid)
	local tSx
	local tDx
	local sposta = 0

	local fine = pers[pid].id
	local frt2, n
	
	for frt=1, (fine-1) do
		frt2 = pers[pers[pid].padre].figli[frt]
		tDx = limDx
		tSx = limSx
		n = pers[pid].y
		while tSx[n] and tDx[n] do
			if (tSx[n] - tDx[n] + sposta < 2) then
				sposta = 2 + tDx[n] - tSx[n]
			end
			n = n + 1
		end
		if (sposta > 0) then
			pers[pid].x = pers[pid].x + sposta
			pers[pid].sp = pers[pid].sp + sposta
			if (riallinea == 1) then verifica(pid) end
			sposta = 0
		end
	end
end

local function calcolaX1(pid)
	for _, v in pairs(pers[pid].figli) do
		calcolaX1(v)
	end
	local tt = #pers[pid].figli
	if (tt == 0) then
		if (pers[pid].padre == -1 or pers[pid].id == 1) then
			pers[pid].x = 0
		else
			pers[pid].x = pers[pers[pers[pid].padre].figli[pers[pid].id - 1]].x + 2
		end
	elseif (tt == 1) then
		if (pers[pid].padre == -1 or pers[pid].id == 1) then
			pers[pid].x = pers[pers[pid].figli[1]].x
		else
			pers[pid].x = pers[pers[pers[pid].padre].figli[pers[pid].id - 1]].x + 2
			pers[pid].sp = pers[pid].x - pers[pers[pid].figli[1]].x
			verifica(pid)
		end
	else
		local media = math.floor((pers[pers[pid].figli[1]].x + pers[pers[pid].figli[tt]].x)/2)
		if (pers[pid].padre == -1 or pers[pid].id == 1) then
			pers[pid].x = media
		else
			pers[pid].x = pers[pers[pers[pid].padre].figli[pers[pid].id - 1]].x + 2
			pers[pid].sp = pers[pid].x - media
			verifica(pid)
		end
	end
end

local function calcolaX2(pid)
	local sposta = 0
	local tt = limSx
	for _, v in pairs(tt) do
		if (v+sposta<0) then
			sposta = -v
		end
	end
	if (sposta > 0) then
		pers[pid].x = pers[pid].x + sposta
		pers[pid].sp = pers[pid].sp + sposta
	end
end

local function calcolaX3
	pers[pid].x = pers[pid].x + sposta
	for _, v in pairs(pers[pid].figli) do
		calcolaX3
	end
end

local function massimoXY
	if (pers[pid].x > t[1]) then t[1] = pers[pid].x end
	if (pers[pid].y > t[2]) then t[2] = pers[pid].y end
	for _, v in pairs(pers[pid].figli) do
		t = massimoXY
	end
	return t
end

local function mostraX
	local posx = {}
	local n1
	local stx
	local riga = {}
	local xx, xp
	local stileDiv = { ['width'] = largo..'px', ['padding'] = '3px', ['background'] = '#FFF', ['border'] = '1px solid #C8CCD1' }
	local stileTabella = { ['border-collapse'] = 'separate', ['text-align'] = 'center', ['font-size'] = '95%', ['line-height'] = '105%', ['margin'] = '10px auto !important', }
	local xy = massimoXY
	local lg = math.floor(100/(xy[1]+2))
	if (lg == 0) then lg = 1 end

	local bDiv = mw.html.create('div')
	if (allinea == 'right') then
		bDiv:css(stileDiv):addClass('floatright')
		stileTabella['margin'] = '0px auto !important'
	end
	local bTabella = mw.html.create('table')
	for n=1,xy[2] do
		local riga1 = mw.html.create('tr')
		local riga2 = mw.html.create('tr')
		local riga3 = mw.html.create('tr')
		posx[1] = 0; posx[2] = 0; posx[3] = 0
		n1 = 0
		if (n > 1) then riga1:css end
		if (n < xy[2]) then riga3:css end
		for _, v in pairs(tabella[n]) do
			xx = pers[v].x
			xp = pers[v].padre
			
			if n == 1 then
				for m=1,(xy[1]+2) do
					riga1:node(mw.html.create('td'):css)
				end
			else
				riga1:node(mw.html.create('td')
				)
				n1 = xp
				posx[1] = xx + 1
			end

			if xx-posx[2] > 0 then
				riga2:node(mw.html.create('td')
				)
			end
			riga2:node(mw.html.create('td')
			)
			posx[2] = xx + 2

			if n  0 then
				riga3:node(mw.html.create('td')
					)
				posx[3] = xx + 1
			end
		end

		bTabella:node(riga1):node(riga2):node(riga3)
	end

	bDiv:node(bTabella)
	if (allinea == 'right' and dida ~= '') then
		bDiv:node(mw.html.create('p')
		)
	end
	return tostring(bDiv)
end

local function calcolaY
	if (pers[pid].y > t) then t = pers[pid].y end
	for _, v in pairs(pers[pid].figli) do
		t = calcolaY
		pers[pid].sp = pers[pid].sp + 1 + pers[v].sp
	end
	return t
end

local function mostraY(pid)
	local bTabella = mw.html.create('table')

local function mostraY2
	if (pers[pid].padre > -1) then
		local riga1 = mw.html.create('tr')
		local riga2 = mw.html.create('tr')
		local spd = pers[pers[pid].padre].sp
		if (pers[pid].id == 1 and pers[pers[pid].padre].padre > -1) then
			riga1:node(mw.html.create('td')
			riga1:node(mw.html.create('td')
			)
		end
		riga1
		riga2
		bTabella:node(riga1):node(riga2)
	else
		bTabella:node(
			mw.html.create('tr')
				)
		)
	end
	if (pers[pid].sp > 0) then
		for _, v in pairs(pers[pid].figli) do
			mostraY2
		end
	end
end

	mostraY2(pid,calcolaY)
	return tostring(bTabella)
end

function p._lineage(args)
	local capo = -1
	local n1, n2
	local lato = args['align'] or 'center'
	local larg = args['width'] or '300'
	local tipo = args['show'] or 'h'
	local dida = args['caption'] or ''
	dividi(args)
	n1 = 0
	for i, v in pairs(pers) do
		n1 = n1+1
		if (v.padre == -1) then
			if (capo == -1) then
				capo = i
			else
				error(string.format('Duplicated progenitor ',capo,i))
			end
		else
			if (v.padre == i) then
				error(string.format)
			elseif (pers[v.padre]) then
				table.insert
			else
				error(string.format)
			end
		end
	end

	if (capo == -1) then
		error('Progenitor not found')
	else
		n2 = organizza
		if (n1 == n2) then
			if (tipo == 'v') then
				return mostraY(capo)
			elseif (tipo == 'h') then
				calcolaX1(capo)
				calcolaX2(capo)
				calcolaX3
				return mostraX
			end
		else
			error('Some elements are not linked to the progenitor')
		end
	end
end

function p.lineage(frame)
	local args = getArgs(frame, {
		valueFunc = function 
			if type(key) == "number" then
				if value == nil then
					return nil
				else
					value = mw.text.trim(value)
				end
			else
				if value == '' then return nil end
 end
			return value
		end
	return p._lineage(args)
end