Module:Crossbox

local util_args = require('Module:ArgsUtil') local util_cargo = require('Module:CargoUtil') local util_esports = require('Module:EsportsUtil') local util_form = require('Module:FormUtil') local util_html = require('Module:HtmlUtil') local util_map = require('Module:MapUtil') local util_math = require('Module:MathUtil') local util_table = require('Module:TableUtil') local util_text = require('Module:TextUtil') local util_toggle = require('Module:ToggleUtil') local util_tournament = require('Module:TournamentUtil') local util_vars = require('Module:VarsUtil') local lang = mw.getLanguage('en')

local i18n = require('Module:I18nUtil') local m_region = require('Module:Region') local m_team = require('Module:Team')

local SHOW_LINKS = true

local sep = '%s*,%s*' local CLASSES = { init = 'crossbox-cell', [1] = 'crossbox-win crossbox-cell', [0] = 'crossbox-draw crossbox-cell', [-1] = 'crossbox-loss crossbox-cell' }

local TOTALCLASSES = { init = 'crossbox-total', [1] = 'crossbox-total crossbox-total-win', [0] = 'crossbox-total crossbox-total-draw', [-1] = 'crossbox-total crossbox-total-loss' }

local TOGGLES = { order = { 'match', 'game' }, }

local FORM_INFO = { form = 'TeamHeadToHeadSchedule', template = 'TH2HS' }

local h = {} local p = {} function p.fromCargo(frame) i18n.init('Crossbox') local args = util_args.merge if not args.crossboxtype then error('Must specify |crossboxtype=') end local crossboxtype = lang:lc(args.crossboxtype) local overviewPage = util_esports.getOverviewPage(args.page) local data = h.getData(overviewPage, args) if util_args.castAsBool(args.initializeall) then h.addZeroes(data) end return h.makeOutput(data, crossboxtype, util_args.castAsBool(args.groups)) end

function p.fromArgs(frame) i18n.init('Crossbox') local args = util_args.merge h.setConstants(args) if not args.crossboxtype then error('Must specify |crossboxtype=') end local crossboxtype = lang:lc(args.crossboxtype) local data = h.getDataFromArgs(args) return h.makeOutput(data, crossboxtype, util_args.castAsBool(args.groups)) end

function h.setConstants(args) if util_args.castAsBool(args.nolinks) then SHOW_LINKS = false end end

-- CARGO DATA COLLECTION

function h.getData(overviewPage, args) local query = h.makeQuery(overviewPage, args) local result = util_cargo.queryAndCast(query) if #result == 0 then return {} end local data = h.parseResult(result) local groupresult if util_args.castAsBool(args.groups) or args.onlygroup then groupresult = util_tournament.getGroups(overviewPage) if #groupresult == 0 then args.groups = false else h.addGroupDataToTeams(data, groupresult) end end local teamlist = h.getTeamOrder(args.teamlist, groupresult, args.onlygroup) h.sortData(data, teamlist) return data end

function h.makeQuery(page, args) local query = { tables = 'MatchSchedule', fields = { 'Team1Final=Team1', 'Team2Final=Team2', 'Winner', 'Team1Score', 'Team2Score' },		groupBy = 'UniqueMatch', where = h.makeWhere(page, args), limit = 9999, types = { Team1Score = 'number', Team2Score = 'number' }	}	return query end

function h.makeWhere(page, args) local tbl = { ('OverviewPage="%s"'):format(page), }	if lang:lc(args.tiebreakers or '') == 'only' then tbl[#tbl+1] = '(IsTiebreaker = "1")' elseif not util_args.castAsBool(args.tiebreakers) then tbl[#tbl+1] = '(IsTiebreaker != "1" OR IsTiebreaker IS NULL)' end if args.excludetabs then for v in util_text.gsplit(args.excludetabs, sep) do			tbl[#tbl+1] = ('Tab != "%s"'):format(v) end end if args.onlytabs then local onlyrounds_tbl = {} for v in util_text.gsplit(args.onlytabs, sep) do			onlyrounds_tbl[#onlyrounds_tbl+1] = ('Tab = "%s"'):format(v) end tbl[#tbl+1] = ('(%s)'):format(util_table.concat(onlyrounds_tbl, ' OR ')) end return util_table.concat(tbl, ' AND ') end

function h.parseResult(result) local teams = {} for _, row in ipairs(result) do		local team1, team2 = row.Team1, row.Team2 h.initializeMatchup(teams, team1, team2) h.addResults(teams, row, team1, team2) end return teams end

function h.initializeMatchup(teams, team1, team2) if not team1 or not team2 then return end h.initializeTeam(teams, team1) h.initializeTeam(teams, team2) if teams[team1][team2] then return end h.addMatchupZeroes(teams[team1], team1, team2) h.addMatchupZeroes(teams[team2], team2, team1) return end

function h.addMatchupZeroes(team1tbl, team1, team2) team1tbl[team2] = { wgames = 0, lgames = 0, wins = 0, losses = 0, ties = 0, team2 = team2, link = h.getLink(team1, team2) } end

function h.getLink(team1, team2) if not SHOW_LINKS then return nil end return util_form.makeTableCellFilterLink(FORM_INFO, {team1, team2}, 'crossbox-match-link') end

function h.initializeTeam(tbl, team) if tbl[team] then return end tbl[#tbl+1] = team tbl[team] = { wgames = 0, lgames = 0, wins = 0, losses = 0, ties = 0, team = team } return end

function h.addResults(teams, row, team1, team2) if not team1 or not team2 then return end local results = { [team1] = h.getResults('1', '2', row), [team2] = h.getResults('2', '1', row) }	local opponents = { [team1] = team2, [team2] = team1 } for team, data in pairs(results) do		for k, v in pairs(data) do			teams[team][opponents[team]][k] = teams[team][opponents[team]][k] + v			teams[team][k] = teams[team][k] + v		end end return end

function h.getResults(n, vs, row) return { wins = row.Winner == n and 1 or 0, losses = row.Winner == vs and 1 or 0, ties = row.Winner == '0' and 1 or 0, wgames = row['Team' .. n .. 'Score'], lgames = row['Team' .. vs .. 'Score'] } end

function h.addGroupDataToTeams(data, groupresult) local dict = util_cargo.makeConstDict(groupresult, 'Team', 'GroupDisplay') for _, team in ipairs(data) do		data[team].group = dict[team] end end

function h.getTeamOrder(teamlist, groupresult, onlygroup) if teamlist then return util_map.split(teamlist, sep, m_team.teamlinkname) elseif groupresult then if onlygroup then for k, row in ipairs(groupresult) do				if row.GroupName ~= onlygroup then groupresult[k] = false end end util_table.removeFalseEntries(groupresult) end return util_table.arrayFromField(groupresult, 'Team') else return nil end end

function h.sortData(tbl, teamlist) if teamlist then local onlyteams = util_table.hash(teamlist) for k, v in ipairs(tbl) do			if not onlyteams[v] then tbl[k] = false end end util_table.removeFalseEntries(tbl) end if teamlist then util_table.sortByKeyOrder(tbl, teamlist) else table.sort(tbl, function(a, b)			return lang:lc(a) < lang:lc(b)		end		) end return end

-- ARGS DATA COLLECTION

function h.getDataFromArgs(args) local teams = util_args.numberedArgsToTable(args, 'team') local regions = util_args.numberedArgsToTable(args, 'region') or {} local groups = args.groups and util_text.split(args.groups, sep) or {} util_map.inPlace(teams, m_team.teamlinkname) for i1, team1 in ipairs(teams) do		teams[team1] = { wgames = 0, lgames = 0, wins = 0, losses = 0, ties = 0, group = groups[i1], region = regions[i1], team = team1 }		for i2, team2 in ipairs(teams) do			local link = h.getLink(team1, team2) teams[team1][team2] = { team2 = team2, link = link } h.addSeriesScore(teams[team1][team2], teams[team1], args[('t%st%sscore'):format(i1, i2)]) h.addGameScore(teams[team1][team2], teams[team1], args[('t%st%sgames'):format(i1, i2)]) end end return teams end

function h.addSeriesScore(teamtbl, totaltbl, arg) if not arg then return end local numbers = util_text.split(arg, '%s*-%s*') if #numbers < 2 then error(('A series arg with value "%s" has incorrect formatting, must be "x - x" or "x - x - x"'):format(arg)) end if #numbers == 3 then teamtbl.wins = tonumber(numbers[1] or '') or 0 teamtbl.ties = tonumber(numbers[2] or '') or 0 teamtbl.losses = tonumber(numbers[3] or '') or 0 teamtbl.wgames = 2 * teamtbl.wins + teamtbl.ties teamtbl.lgames = 2 * teamtbl.losses + teamtbl.ties totaltbl.wgames = totaltbl.wgames + teamtbl.wgames totaltbl.lgames = totaltbl.lgames + teamtbl.lgames elseif #numbers == 2 then teamtbl.wins = tonumber(numbers[1] or '') or 0 teamtbl.losses = tonumber(numbers[2] or '') or 0 teamtbl.ties = 0 end totaltbl.wins = totaltbl.wins + teamtbl.wins totaltbl.losses = totaltbl.losses + teamtbl.losses totaltbl.ties = totaltbl.ties + teamtbl.ties return end

function h.addGameScore(teamtbl, totaltbl, arg) if not arg then return end local w, l = arg:match('(%d+)%s*-%s*(%d+)') if not w or not l then error(('A game arg with value "%s" has incorrect formatting, must be "x - x"'):format(arg)) end teamtbl.wgames = tonumber(w or '') or 0 teamtbl.lgames = tonumber(l or '') or 0 totaltbl.wgames = totaltbl.wgames + teamtbl.wgames totaltbl.lgames = totaltbl.lgames + teamtbl.lgames return end

function h.addZeroes(data) for _, team in ipairs(data) do		for _, team2 in ipairs(data) do			if not data[team][team2] then h.addMatchupZeroes(data[team], team2) end end end end

-- DISPLAY

function h.makeOutput(data, crossboxtype, usegroups) local output = mw.html.create if crossboxtype ~= 'bo1' then h.printToggler(output) end h.printTable(output, data, crossboxtype, usegroups) return output end

function h.printToggler(tbl) local div = tbl:tag('div') :addClass('toggle-button') util_toggle.printOptionFromListTogglers(div, TOGGLES) util_html.clear(tbl) return end

function h.printTable(output, data, crossboxtype, usegroups) local f = h.getCellFunction(crossboxtype) local div = output:tag('div') :addClass('crossbox-outer') local tbl = div:tag('table') :addClass('wikitable2') :addClass('plainlinks') :addClass('crossbox') h.printHeading(tbl, data, crossboxtype, usegroups) for _, team in ipairs(data) do		local tr = h.printRow(tbl, team, data, f, usegroups) end return output end

function h.getCellFunction(bestof) if bestof == 'bo1' then return h.printCellBO1 elseif bestof == 'bo2' then return h.printCellBO2 else return h.printCellBO3 end end

function h.printHeading(tbl, data, crossboxtype, usegroups) local tr = tbl:tag('tr') tr:tag('td') :addClass('crossbox-mirror') :addClass('crossbox-firstcol') if usegroups then tr:tag('td') :addClass('crossbox-mirror') end for _, team in ipairs(data) do		local th = h.printTeamCell(tr, team, data[team]) th:addClass('crossbox-teamvs') :attr('data-crossbox-highlight-vs', team) end tr:tag('th'):wikitext(i18n.print('total')) tr:tag('th'):wikitext(i18n.print('wr')) return end

function h.printTeamCell(tr, team, teamData) local th = tr:tag('th') :addClass('crossbox-cell') if teamData.region then th:wikitext(m_region.onlyimage(teamData.region)) end th:wikitext(m_team.onlyimage(team,{size=45})) return th end

function h.printRow(tbl, team, data, f, usegroups) local tr = tbl:tag('tr') util_esports.addTeamHighlighter(tr, team) if usegroups then tr:tag('th') :wikitext(data[team].group) :addClass('crossbox-firstcol') end local th = h.printTeamCell(tr, team, data[team]) if not usegroups then th:addClass('crossbox-firstcol') end for _, v in ipairs(data) do		h.printCell(tr, data, team, v, f)	end f(tr, data[team], TOTALCLASSES) f(tr, data[team], TOTALCLASSES, true) return tr end

function h.printCell(tr, data, team1, team2, f)	if team1 == team2 then tr:tag('td') :addClass('crossbox-mirror') :addClass('crossbox-cell') :attr('data-crossbox-highlight-vs', team2) return elseif not data[team1][team2] then tr:tag('td') :addClass('crossbox-cell') :attr('data-crossbox-highlight-vs', team2) return else return f(tr, data[team1][team2], CLASSES) end end

function h.printCellBO1(tr, teamdata, classes, isPercent) local w, l = teamdata.wins, teamdata.losses local td = h.printRecordCell(tr, teamdata.team2) h.addColorClass(td, w, l, 0, classes) local div = h.printRecordDiv(td) if w and l then if not isPercent then div:wikitext(('%s - %s'):format(w, l)) div:wikitext(teamdata.link) else h.printPercent(div, w, l)		end end return end

function h.printCellBO2(tr, teamdata, classes, isPercent) local w, l, t = teamdata.wins, teamdata.losses, teamdata.ties local tdMatch = h.printRecordCell(tr, teamdata.team2, 'match') h.addColorClass(tdMatch, w, l, t, classes) local divSeries = h.printRecordDiv(tdMatch) wg, lg = teamdata.wgames, teamdata.lgames local tdGame = h.printRecordCell(tr, teamdata.team2, 'game') h.addColorClass(tdGame, wg, lg, 0, classes) local divGame = h.printRecordDiv(tdGame) if not isPercent then divSeries:wikitext(('%s - %s - %s'):format(w, t, l), teamdata.link) divGame:wikitext(('%s - %s'):format(wg, lg), teamdata.link) else divSeries:wikitext('-') h.printPercent(divGame, wg, lg) end end

function h.printCellBO3(tr, teamdata, classes, isPercent) local w, l = teamdata.wins, teamdata.losses local tdMatch = h.printRecordCell(tr, teamdata.team2, 'match') h.addColorClass(tdMatch, w, l, 0, classes) local divMatch = h.printRecordDiv(tdMatch) wg, lg = teamdata.wgames, teamdata.lgames local tdGame = h.printRecordCell(tr, teamdata.team2, 'game') h.addColorClass(tdGame, wg, lg, 0, classes) local divGame = h.printRecordDiv(tdGame) if not isPercent then divMatch:wikitext(('%s - %s'):format(w, l), teamdata.link) divGame:wikitext(('%s - %s'):format(wg, lg), teamdata.link) else h.printPercent(divMatch, w, l)		h.printPercent(divGame, wg, lg) end end

function h.printRecordCell(tr, team2, cellType) local td = tr:tag('td') :attr('data-crossbox-highlight-vs', team2) if cellType then util_toggle.oflCellClasses(td, TOGGLES, cellType) end return td end

function h.addColorClass(td, w, l, t, lookup) w = w or 0 l = l or 0 t = t or 0 if w + l + t == 0 then td:addClass(lookup.init) else td:addClass(lookup[util_math.sign(w-l)]) end end

function h.printRecordDiv(td) local div = td:tag('div') :addClass('table-cell-container') return div end

function h.printPercent(div, w, l)	div:wikitext(util_esports.winrate(w, l, .01), '%') end

return p