---
-- This module implements many bracket templates
---  
local p = {}
local args = {}
local rows = {}
local mask = {}
local rounds
local maxround
local legs = {}
local compact
local byes
local hideSeeds
local showSeeds
local hideHeadings
local showThird
local offsetThird
local compactFinal
local sepwidth
local aggsep
local aggregate
local boldwinner
local hideomittedscores
local RD1seedmap = {}
local tcats = ''  
local function isnotblank(s)
return s and s ~= ''
end  
local function isblank(s)
return (not s) or (s == '')
end  
local function sumScores(s1, s2)
s1 = mw.ustring.gsub(s1 or '', '^[\'%s]*([%d%.]*).-$', '%1')
s2 = mw.ustring.gsub(s2 or '', '^[\'%s]*([%d%.]*).-$', '%1')
if s1 ~= * and s2 ~= * then
return tonumber(s1) + tonumber(s2)
end
return s1
end  
local function scoreCompare(s1,s2,highwin)
local ps1 = mw.ustring.gsub(s1 or '', '^[\'%s]*([%d%.]*)[\'%s]*%([\'%s]*([%d%.]*)[\'%s]*%).-$', '%2')
local ps2 = mw.ustring.gsub(s2 or '', '^[\'%s]*([%d%.]*)[\'%s]*%([\'%s]*([%d%.]*)[\'%s]*%).-$', '%2')
s1 = mw.ustring.gsub(s1 or '', '^[\'%s]*([%d%.]*).-$', '%1')
s2 = mw.ustring.gsub(s2 or '', '^[\'%s]*([%d%.]*).-$', '%1')  
if s1 ~= * and s2 ~= * then
s1 = tonumber(s1)
s2 = tonumber(s2)
if s1 and s2 then
if (s1 == s2) then
ps1 = tonumber(ps1)
ps2 = tonumber(ps2)
if ps1 and ps2 then
s1 = ps1
s2 = ps2
end
end
if highwin then
return ((s1 > s2) and 1) or ((s1 < s2) and 2) or 0
else
return ((s2 > s1) and 1) or ((s2 < s1) and 2) or 0
end
end
end
return 0
end  
local function unboldParenthetical(s)
if s then
s = mw.ustring.gsub(s, '(%(%[%^%[%*%]%]%))', '<span>%1</span>')
end
return s
end  
local function parseArgs
local fargs = .args
local pargs = :getParent.args;  
local r = tonumber(fargs.rounds or *) or tonumber(pargs.rounds or *) or 2
local teams = math.pow(2, r)
local rdstr = 'RD' .. tostring(r)
local rdbstr = 'RD' .. tostring(r) .. 'b'
local rdp1str = 'RD' .. tostring(r+1)  
for i=1,2 do
local targs = (i == 1) and pargs or fargs
for k,v in pairs(targs) do
if type(k) == 'string' then
if k:find('^[R3][Dr][d1-9]b?%-[a-z][a-z]*00*') then
k = mw.ustring.gsub(k, '^([R3][Dr][d1-9]b?%-[a-z][a-z]*)00*', '%1')
if (teams < 10) then
tcats = tcats .. ''
end
end
if k:find('^' .. rdp1str) then
k = mw.ustring.gsub(k, '^' .. rdp1str, '3rd')
tcats = tcats .. ''
elseif k:find('^' .. rdbstr) then
k = mw.ustring.gsub(k, '^' .. rdbstr, '3rd')
elseif k:find('^' .. rdstr .. '%-[a-z][a-z]*3') then
k = mw.ustring.gsub(k, '^' .. rdstr .. '(%-[a-z][a-z]*)3', '3rd%11')
elseif k:find('^' .. rdstr .. '%-[a-z][a-z]*4') then
k = mw.ustring.gsub(k, '^' .. rdstr .. '(%-[a-z][a-z]*)4', '3rd%12')
elseif k:find('^Consol') then
k = mw.ustring.gsub(k, '^Consol', '3rd')
tcats = tcats .. ''
elseif k:find('^group[0-9]') then
tcats = tcats .. ''
end
end
args[k] = v
end
end  
if (args['byes'] and (args['byes'] == 'yes' or args['byes'] == 'y')) then
tcats = tcats .. ''
end
end  
local function parseSeedmap(s)
s = mw.text.split((s or '0') .. '/', '[%s]*/[%s]*')
local teams = math.pow(2, rounds)
for r=1,teams do
RD1seedmap[r] = 1
end
for r=1,#s do
if tonumber(s[r] or 'x') then
RD1seedmap[tonumber(s[r])] = 0
end
end
local c = 1
for r=1,teams do
if RD1seedmap[r] > 0 then
RD1seedmap[r] = c
c = c + 1
end
end
end  
local function parseLegs(s)
s = mw.text.split((s or '1') .. '/', '[%s]*/[%s]*')
if aggregate == 'n' or aggregate == 'no' or aggregate == '0' then
aggregate = ''
end
local n = showThird and (rounds + 1) or (rounds)
local lastlegs = nil
for r=1,n do
if s[r] ~= nil and s[r] ~= '' and tonumber(s[r]) then
legs[r] = tonumber(s[r])
elseif lastlegs then
legs[r] = lastlegs
else
legs[r] = 1
end
lastlegs = legs[r]
if legs[r] > 1 and aggregate ~= '' then
legs[r] = legs[r] + 1
end
end
end  
local 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  
local function addTableRow(tbl)
return tbl:tag('tr')
end  
local function addBlank(i, css, rowspan, colspan)
local row = rows[i]
rowspan = rowspan or 1
local jmax = i + rowspan - 1
for j = i, jmax do
if rows[j] == nil then
rowspan = rowspan - 1
elseif row == nil then
row = rows[j]
end
end
local cell = row and row:tag('td') or mw.html.create('td')
if rowspan and rowspan > 1 then
cell:attr('rowspan', rowspan)
end
if colspan and colspan > 1 then
cell:attr('colspan', colspan)
end
if css then
cell:css(css)
end
return cell
end  
local function addBorders(cell, topcell, seedorteam, extrasep)
if sepwidth > 1 then topcell = true end
if seedorteam then
cell:css('', ' solid #aaa')
:css('-top-width', topcell and '' or '0')
else
cell:css('-color', '#aaa')
:css('-style', 'solid')
:css('-top-width', topcell and '' or '0')
:css('--width', (extrasep and *) or ((sepwidth > 1) and *) or '0')
:css('--width', '')
:css('-bottom-width', '')
end
end  
local function addHeading(row, r, text, pad)
pad = (pad == nil or pad < 0) and 0 or pad
row:tag('td')
:attr('colspan', tonumber(hideSeeds and '1' or '2') + legs[r] + pad)
:css('text-align', '')
:css('', ' solid #aaa')
:css('background-color', args['RD-shade'] or '#f2f2f2')
:wikitext(text)
:newline
end  
local function getWidth(param, default)
local arg = args[param .. '-width']
if isblank(arg) then
arg = default
end
if tonumber(arg) ~= nil then
arg = arg .. 'px'
end
return arg
end  
local function getTeamArgName(round, type, team)
if round > rounds then
return string.format('3rd-%s%d', type, team)
else
if (round == 1) then
team = RD1seedmap[team]
if team == 0 then
return 'NIL'
end
end
return string.format('RD%d-%s%d', round, type, team)
end
end  
local function getShadeArg(round, team, s)
local argname = getTeamArgName(round, 'shade', team) .. (s and ('-' .. s) or '')
local value = args[argname]
if isblank(value) then
return '#f9f9f9'
end
return value
end  
local function getScoreArg(round, team, s)
local argname = getTeamArgName(round, 'score', team) .. (s and ('-' .. s) or '')
local value = args[argname]
return value
end  
local function getTeamArg(round, type, team)
local argname = getTeamArgName(round, type, team)
local value = args[argname]
if isblank(value) then
return ''
end
if mw.ustring.find(value, '[%s]*<[%s/]*[Bb][Rr][%s/]*>[%s ]*&[Nn][Bb][Ss][Pp];[%s]*') then
tcats = tcats .. ''
end
return mw.ustring.gsub(value, '[%s]*<[%s/]*[Bb][Rr][%s/]*>[%s ]*&[Nn][Bb][Ss][Pp];[%s]*', '
')
end  
local function isHidden(r, team)
return isblank( getTeamArg(r, 'team', team) )
end  
local function getRoundName(round)
local name = args['RD' .. round]
if isnotblank(name) 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  
local function addPath(index, round, top, , w)
local prop = top and '-bottom-width' or '-top-width'
if and round == 1 then
if compact then
addBlank(index)
else
addBlank(index, {['height'] = ''})
addBlank(index+1, {['height'] = ''})
end
return nil
else
local cell = addBlank(index,
{['-width'] = '0',
['-style'] = 'solid',
['-color'] = 'black'}, (not compact) and 2 or 1)
if or round < maxround and not then
cell:css(prop, w or '')
end
return cell
end
end  
local function renderTeam(row, round, team, top, otherbye, pad)
pad = (pad == nil or pad < 0) and 0 or pad
tcs = pad + 1
local seedCell
local seedArg = getTeamArg(round, 'seed', team)
-- seed value for the paired team
local otherteam = team % 2 == 0 and team-1 or team+1
local pairSeedArg = otherbye and ''
or getTeamArg(round, 'seed', otherteam)
-- show seed if seed is defined for either or both
local showSeed = showSeeds
or isnotblank(seedArg)
or isnotblank(pairSeedArg)
if showSeed and (not hideSeeds) then
seedCell = row:tag('td')
:css('text-align', '')
:css('background-color', '#f2f2f2')