# Module:TeamBracket

---
-- This module will implement  
---

local p = {}  
local args  
local rounds  
local padding  

local function getArgs  
local parent = :getParent;  
local args = parent.args;  
for k,v in pairs(.args) do  
args[k] = v  
end  
return args;  
end  

function getSeeds  
local seeds = {1, 2}  
local count = 2  
local before = false  
for r = 2, rounds do  
local max = math.pow(2, r)  
for i = 1, count do  
local pos = i * 2  
if before then pos = pos - 1 end  
table.insert(seeds, pos, max - seeds[i * 2 - 1] + 1)  
before = not before  
end  
count = count * 2  
end  
return seeds  
end  

function addTableRow(tbl)  
return tbl:tag('tr')  
end  

function addBlank(row, width)  
local cell = row:tag('td')  
:css('-width', '0')  
:css('-style', 'solid')  
:css('-color', 'black')  
if width then  
cell:css('width', width)  
end  
return cell  
end  

function addPath(rows, index, round, top, )  
local prop = top and '-bottom-width' or '-top-width'  
if and round == 1 then  
addBlank(rows[index]):css('height', '')  
addBlank(rows[index + 1]):css('height', '')  
return nil  
else  
local cell = addBlank(rows[index]):attr('rowspan', '2')  
if or round < rounds and not then  
cell:css(prop, '')  
end  
return cell  
end  
end  

function getWidth(param, default)  
local arg = args[param .. '-width']  
if not arg or string.len(arg) == 0 then  
arg = default  
end  
if tonumber(arg) ~= nil then  
arg = arg .. 'px'  
end  
return arg  
end  

function getTeamArg(round, type, team)  
return args[getTeamArgName(round, type, team)]  
end  

function getTeamArgName(round, type, team)  
return string.format('RD%d-%s' .. padding, round, type, team)  
end  

function getRoundName(round)  
local name = args['RD' .. round]  
if name and string.len(name) > 0 then  
return name  
end  
local roundFromLast = rounds - round + 1  
if roundFromLast == 1 then  
return "Finals"  
elseif roundFromLast == 2 then  
return "Semifinals"  
elseif roundFromLast == 3 then  
return "Quarterfinals"  
else  
return "Round of " .. math.pow(2, roundFromLast)  
end  
end  

function renderTeam(row, round, team)  
local seedArg = getTeamArg(round, 'seed', team)  
-- seed value for the paired team  
local pairSeedArg = getTeamArg(round, 'seed',  
team % 2 == 0 and team - 1 or team + 1)  
-- show seed if seed is defined for either or both  
local showSeed = seedArg and string.len(seedArg) > 0  
or pairSeedArg and string.len(pairSeedArg) > 0  
if showSeed then  
row:tag('td')  
:attr('rowspan', '2')  
:css('text-align', '')  
:css('background-color', '#f2f2f2')  
:css('', ' solid #aaa')  
:wikitext(seedArg)  
:newline  
end  
local teamArg = getTeamArg(round, 'team', team)  
if not teamArg or string.len(teamArg) == 0 then  
teamArg = '&nbsp;'  
end  
local teamCell = row:tag('td')  
:attr('rowspan', '2')  
:css('background-color', '#f9f9f9')  
:css('', ' solid #aaa')  
:css('padding', '0 ')  
:wikitext(teamArg)  
:newline  
if not showSeed then  
teamCell:attr('colspan', '2')  
end  
row:tag('td')  
:attr('rowspan', '2')  
:css('text-align', '')  
:css('', ' solid #aaa')  
:css('background-color', '#f9f9f9')  
:wikitext(getTeamArg(round, 'score', team))  
:newline  
end  

function renderRound(rows, count, r)  
local teams = math.pow(2, rounds - r + 1)  
local step = count / teams  
local top = true  
local open = false  
local team = 1  
for i = 1, count, step do  
local offset, height, blank  
-- leave room for groups for teams other than first and last  
if team == 1 or team == teams then  
offset = top and i or i + 2  
height = step - 2  
else  
offset = top and i + 1 or i + 2  
height = step - 3  
end  
if height > 0 then  
blank = addBlank(rows[offset])  
:attr('colspan', '5')  
:attr('rowspan', height)  
end  
-- add bracket  
local j = top and i + step - 2 or i  
addPath(rows, j, r, top, true)  
renderTeam(rows[j], r, team)  
local = addPath(rows, j, r, top, false)  
if not top then open = not open end  
if open and r < rounds then  
if blank then blank:css('--width', '') end  
:css('--width', '')  
end  
team = team + 1  
top = not top  
end  
end  

function renderGroups(rows, count, round)  
local roundFromLast = rounds - round + 1  
local groups = math.pow(2, roundFromLast - 2)  
local step = count / groups  
local group = 1  
for i = step / 2, count, step do  
local name = 'RD' .. round .. '-group' .. group  
addBlank(rows[i]):css('height', '')  
addBlank(rows[i + 1]):css('height', '')  
addBlank(rows[i])  
:attr('rowspan', '2')  
:attr('colspan', 5 * round - 1)  
:css('text-align', '')  
:css('--width', '')  
:wikitext(args[name])  
:newline  
group = group + 1  
end  
end  

function renderTree(tbl)  
-- create 3 rows for every team  
local count = math.pow(2, rounds) * 3  
local rows = {}  
for i = 1, count do  
rows[i] = addTableRow(tbl)  
end  
-- fill rows with groups  
for r = 1, rounds - 1 do  
renderGroups(rows, count, r)  
end  
-- fill rows with bracket  
for r = 1, rounds do  
renderRound(rows, count, r)  
end  
end  

function renderHeading(tbl)  
local titleRow = addTableRow(tbl)  
local widthRow = addTableRow(tbl)  
for r = 1, rounds do  
addBlank(titleRow)  
addBlank(widthRow, r > 1 and '' or nil)  
titleRow:tag('td')  
:attr('colspan', '3')  
:css('text-align', '')  
:css('', ' solid #aaa')  
:css('background-color', '#f2f2f2')  
:wikitext(getRoundName(r))  
:newline  
addBlank(widthRow, getWidth('seed', '')):wikitext('&nbsp;')  
addBlank(widthRow, getWidth('team', '')):wikitext('&nbsp;')  
addBlank(widthRow, getWidth('score', '')):wikitext('&nbsp;')  
addBlank(titleRow)  
addBlank(widthRow, r < rounds and '' or nil)  
end  
end  

function p.teamBracket  
args = getArgs  
rounds = tonumber(args.rounds) or 2  
local teams = math.pow(2, rounds)  
padding = '%0' .. (teams < 10 and 1 or 2) .. 'd'  

-- set default seeds for round 1  
local seeds = getSeeds  
local argname;  
for i = 1, table.getn(seeds) do  
argname = getTeamArgName(1, 'seed', i)  
if not args[argname] then  
args[argname] = seeds[i]  
end  
end  

local tbl = mw.html.create('table')  
:css('-style', '')  
:css('font-size', '90%')  
:css('margin', '1em 2em 1em 1em')  
:css('-collapse', 'separate')  
:css('-spacing', '0')  

renderHeading(tbl)  
renderTree(tbl)  
return tostring(tbl)  
end  

return p