# 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