Documentation for this module may be created at ماڊيول:TeamBracket/doc

--
-- This module will implement {{TeamBracket}}
--
 
local p = {}
local HtmlBuilder = require('Module:HtmlBuilder')
local args
local rounds
local padding

local function getArgs(frame)
	local parent = frame:getParent();
	local args = parent.args;
	for k,v in pairs(frame.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('border-width', '0')
		.css('border-style', 'solid')
		.css('border-color', 'black')
	if width then
		cell.css('width', width)
	end
	return cell
end

function addPath(rows, index, round, top, left)
	local prop = top and 'border-bottom-width' or 'border-top-width'
	if left and round == 1 then
		addBlank(rows[index]).css('height', '7px')
		addBlank(rows[index + 1]).css('height', '7px')
		return nil
	else
		local cell = addBlank(rows[index]).attr('rowspan', '2')
		if left or round < rounds and not left then
			cell.css(prop, '2px')
		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', 'center')
			.css('background-color', '#f2f2f2')
			.css('border', '1px 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('border', '1px solid #aaa')
		.css('padding', '0 2px')
		.wikitext(teamArg)
		.newline()
	if not showSeed then
		teamCell.attr('colspan', '2')
	end
	row.tag('td')
		.attr('rowspan', '2')
		.css('text-align', 'center')
		.css('border', '1px 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 right = 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('border-left-width', '2px') end
right.css('border-left-width', '2px')
		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', '7px')
		addBlank(rows[i + 1]).css('height', '7px')
		addBlank(rows[i])
			.attr('rowspan', '2')
			.attr('colspan', 5 * round - 1)
			.css('text-align', 'center')
			.css('border-left-width', '2px')
			.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 '5px' or nil)
		titleRow.tag('td')
			.attr('colspan', '3')
			.css('text-align', 'center')
			.css('border', '1px solid #aaa')
			.css('background-color', '#f2f2f2')
			.wikitext(getRoundName(r))
			.newline()
		addBlank(widthRow, getWidth('seed', '25px')).wikitext('&nbsp;')
		addBlank(widthRow, getWidth('team', '150px')).wikitext('&nbsp;')
		addBlank(widthRow, getWidth('score', '25px')).wikitext('&nbsp;')
		addBlank(titleRow)
		addBlank(widthRow, r < rounds and '5px' or nil)
	end
end

function p.teamBracket(frame)
	args = getArgs(frame)
	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 = HtmlBuilder.create('table')
		.css('border-style', 'none')
		.css('font-size', '90%')
		.css('margin', '1em 2em 1em 1em')
		.css('border-collapse', 'separate')
		.css('border-spacing', '0')
 
	renderHeading(tbl)
	renderTree(tbl)
	return tostring(tbl)
end
 
return p