Module:TournamentResults

local util_args = require('Module:ArgsUtil') local util_cargo = require('Module:CargoUtil') local util_esports = require('Module:EsportsUtil') local util_footnote = require("Module:FootnoteUtil") local util_html = require('Module:HtmlUtil') local util_map = require("Module:MapUtil") local util_math = require('Module:MathUtil') local util_refs = require("Module:RefsUtil") local util_table = require('Module:TableUtil') local util_text = require('Module:TextUtil') local util_toggle = require('Module:ToggleUtil') local util_vars = require('Module:VarsUtil') local i18n = require('Module:i18nUtil') local Currency = require('Module:Currency') local Role = require('Module:Role') local m_team = require('Module:Team')

local Placement = require('Module:Placement')

local StoreTournamentRoster = require('Module:StoreTournamentRoster')

local ROWSPANS = { place = true, prize_display = true, usd = true, eur = true, prizepct = true, qual = true, points = true }

local CLASSES = { place = 'tournament-results-place', team = 'tournament-results-team', player = 'tournament-results-player', roster = 'tournament-results-roster', prize_display = 'tournament-results-prize', }

local CURRENCY_ORDER = { 'USD', 'Euros' }

local LINE_ARG_ORDER = { 'place', 'forcenewplace', 'sameplaces', 'date', 'prize', 'otherprize', 'prizepct', 'prizeunit', 'points', 'qual', 'quallink', 'qual2', 'qual2link', 'team', 'nocargo', 'date', 'rosterpage', 'groupstage', 'group', 'lastresult', 'lastopponent', 'lastopponentlink', 'lastopponentteam', 'lastoutcome', 'lastresultpoints', 'player', 'playerlink', 'hide', 'qualified', 'qualfootnote', 'roster', 'norosters' }

local PLAYER_FIELD_LIST = { 'Player', 'Flag', 'Role', 'FootnoteN', 'TeamOrder', 'NoDistribution' }

local SAMEPLACES_COPY_ARGS = { 'prize', 'otherprize', 'prizepct', 'prizeunit', 'points', 'qual', 'quallink', 'qual2', 'qual2link' }

local TOGGLES = { class = 'TRL_toggle%s', }

local PRIZE_TOGGLE_DATA = { order = {}, sep = ' &#8226; ', section = 'prizepool-togglers-currency', all = 'prizepool-currency-all', hiddenclass = 'prizepool-currency-hidden', }

local HAS_TOGGLES = false

local h = {} local p = {} function p.main(frame) i18n.init('TournamentResults') util_footnote.init local args = util_args.merge h.castArgs(args) h.storeRosterCargoIfNeeded(args) local processedArgs = h.getProcessedArgs(args) h.initCurrencyToggle(processedArgs) local cols = h.getCols(args, processedArgs) local linesData = h.getLinesData(args, processedArgs) h.storeCargo(linesData) return h.makeOutput(args, processedArgs, cols, linesData) end

function h.castArgs(args) util_map.arrayInPlace(args, h.castArgRow) end

function h.castArgRow(str) -- use ^^^ as the sep here because ;;; and ::: will be used for Rosters if that's input here local lineArgs = util_args.splitArgs(str, LINE_ARG_ORDER, '^^^', '%%%') lineArgs.roster = util_args.splitArgsArray(lineArgs.roster, PLAYER_FIELD_LIST, ':::', ';;;') lineArgs.forcenewplace = util_args.castAsBool(lineArgs.forcenewplace) return lineArgs end

function h.storeRosterCargoIfNeeded(args) --if not util_args.castAsBool(args.store_rosters) then return end util_map.rowsInPlace(args, h.storeOneRoster) end

function h.storeOneRoster(lineArgs) StoreTournamentRoster.init local playerData = StoreTournamentRoster.getPlayerArrayData(lineArgs.roster, lineArgs) local teamData = StoreTournamentRoster.getTeamData(lineArgs, playerData) StoreTournamentRoster.store(playerData, teamData, lineArgs) end

function h.getProcessedArgs(args) h.checkConversions({ args.usdrate, args.eurorate }) local processedArgs = { prizeunit = args.prizeunit or 'USD', currency = Currency(args.prizeunit) or 'USD', totalprize = util_math.formatNumber(args.totalprize), totalprizenum = args.totalprize and util_math.tonumber(args.totalprize), usdrate = tonumber(args.usdrate), eurorate = tonumber(args.eurorate), phase = args.phase, nocargo = util_args.castAsBool(args.nocargo), preload = args.preload or 'Team', showprize = util_args.castAsBool(args.prize), showusd = util_args.castAsBool(args.usdrate), showeuro = util_args.castAsBool(args.eurorate), showpoints = util_args.castAsBool(args.points), showqual = util_args.castAsBool(args.qual), showteam = util_args.castAsBool(args.showteam), showplayer = util_args.castAsBool(args.showplayer), combinequal = util_args.castAsBool(args.combinequal), showrosters = util_args.castAsBool(args.norosters) }	processedArgs.hasconversion = processedArgs.usdrate or processedArgs.eurorate return processedArgs end

function h.checkConversions(tbl) for _, v in ipairs(tbl) do		if v and not tonumber(v) then error(i18n.print('invalid_currency', i18n.print('conversion_rate'))) end end end function h.initCurrencyToggle(processedArgs) local rates = { USD = processedArgs.usdrate, Euros = processedArgs.eurorate, }	for _, v in ipairs(CURRENCY_ORDER) do		if rates[v] then PRIZE_TOGGLE_DATA.order[#PRIZE_TOGGLE_DATA.order+1] = v		end end if next(PRIZE_TOGGLE_DATA.order) then table.insert(PRIZE_TOGGLE_DATA.order, 1, 'prize') HAS_TOGGLES = true end util_toggle.oflInit(PRIZE_TOGGLE_DATA) end

function h.getCols(args, processedArgs) local cols = { 'place', processedArgs.showprize and 'prize_display', h.pctCol(args), processedArgs.showpoints and 'points', processedArgs.showqual and 'qual', (processedArgs.showteam or processedArgs.preload == 'Team') and 'team', (processedArgs.showplayer or processedArgs.preload == 'Player') and 'player', not processedArgs.showrosters and 'roster', }	util_table.removeFalseEntries(cols) local names = h.getNames(args) cols.displays = {} for i, v in ipairs(cols) do cols.displays[i] = names[v] or i18n.print('col_' .. v)	end return cols end

function h.pctCol(args) if util_args.castAsBool(args.prize) and not util_args.castAsBool(args.nomoney) then return 'prizepct' else return false end end

function h.getNames(args) return { points = args.pointstitle, team = args.teamtitle } end

function h.getLinesData(args, processedArgs) h.determineRowspansAndCopySameplacesArgs(args) local rowData = {} for i, v in ipairs(args) do		rowData[i] = h.processArgRow(v, processedArgs) end return rowData end

function h.determineRowspansAndCopySameplacesArgs(args) local curPlace, curRow, count for _, row in ipairs(args) do		if curPlace ~= row.place or row.forcenewplace then if curRow then curRow.rowspan = count end curRow = row count = 1 curPlace = row.place else count = count + 1 for _, v in ipairs(SAMEPLACES_COPY_ARGS) do				row[v] = curRow[v] end end end if count or 0 > 1 then curRow.rowspan = count end end

function h.processArgRow(lineArgs, processedArgs) local lineDisplay = h.makeDisplayLineFromArgs(lineArgs, processedArgs) lineDisplay.cargo = h.argsToCargoLine(lineArgs, processedArgs, lineDisplay) return lineDisplay end

function h.makeDisplayLineFromArgs(lineArgs, processedArgs) lineArgs.placeObject = Placement(lineArgs.place) local lineDisplay = { place = lineArgs.placeObject:flair, placeraw = lineArgs.place, team = m_team.rightmediumlinked(lineArgs.team), roster = h.getRosterDisplay(lineArgs), player = h.getPlayerDisplay(lineArgs, processedArgs), points = lineArgs.points, qual = h.getQual(lineArgs), qualfootnote = lineArgs.qualfootnote, forcenewplace = lineArgs.forcenewplace, hide = util_args.castAsBool(lineArgs.hide), rowspan = lineArgs.rowspan, }	local prizes = h.getPrizeDisplay(lineArgs, processedArgs) prizes.prize_display = prizes.prize_toggle or prizes.prize util_table.merge(lineDisplay, prizes) if processedArgs.combinequal then lineDisplay.points = lineDisplay.points or lineDisplay.qual or '' end return lineDisplay end

function h.getRosterDisplay(lineArgs) local ret = {} for _, player in ipairs(lineArgs.roster) do		if not player.Role:exists then ret[#ret+1] = util_esports.playerLinked(player.Player) else output = { player.Role:images, util_esports.playerLinked(player.Player) }			ret[#ret+1] = util_table.concat(output, ' ') end end return util_table.concat(ret, ' &#8226; ') end

function h.getPlayerDisplay(lineArgs, processedArgs) local output = { lineArgs.team and m_team.onlyimagelinkedshort(lineArgs.team), util_text.intLink(lineArgs.playerlink, lineArgs.player) }	util_table.removeFalseEntries(output, 2) return util_table.concat(output, ' ') end

function h.getQual(lineArgs) local quals = util_args.numberedArgsToTable(lineArgs, 'qual') or {} lineArgs.qual1link = lineArgs.quallink for i, qual in ipairs(quals) do local link = lineArgs['qual' .. i .. 'link'] quals[i] = util_text.intLink(link, qual) end return util_table.concat(quals,' ') end

function h.getPrizeDisplay(lineArgs, processedArgs) --if processedArgs.showprize and not lineArgs.prize then --	error(i18n.print('error_missingPrize')) --end if not (lineArgs.prize or lineArgs.prizepct) then return { prize = lineArgs.otherprize } end local prizeNum = h.getPrizeNum(lineArgs, processedArgs) if not prizeNum then return { prize = lineArgs.otherprize, prizepct = lineArgs.prizepct .. '%' }	end local prizes = { prize = h.getPrize(prizeNum, lineArgs.otherprize, processedArgs.currency), -- usd_number and eur_number will be stored in Cargo -- and therefore must be pure number values -- below, USD and Euros params are for display usd_number = h.getPrizeConverted(prizeNum, processedArgs.prizeunit, processedArgs.usdrate, 'USD'), eur_number = h.getPrizeConverted(prizeNum, processedArgs.prizeunit, processedArgs.eurorate, 'Euro'), prizepct = h.getPrizePct(prizeNum, lineArgs, processedArgs), }	prizes.USD = h.getPrize(prizes.usd_number, lineArgs.otherprize, Currency('usd')) prizes.Euros = h.getPrize(prizes.eur_number, lineArgs.otherprize, Currency('euro')) prizes.prize_toggle = h.getPrizeToggle(prizes, processedArgs.prizeunit) return prizes end

function h.getPrizeNum(lineArgs, processedArgs) local prize = lineArgs.prize if prize then if not util_math.tonumber(prize) then error(i18n.print('invalid_currency',i18n.print('prize'))) end return util_math.tonumber(prize) end local prizepct = lineArgs.prizepct local totalprize = processedArgs.totalprizenum if totalprize then return util_math.roundnum(prizepct / 100 * totalprize, .01) end return nil end

function h.getPrize(prizeNum, otherprize, prizeunit) -- concatenate the number along with the otherprize -- this is generic and can be used for "real" prize or a converted currency local fullPrize = { prizeunit:short(prizeNum), otherprize }	return util_table.concat(fullPrize, ' + ',nil,2) end

function h.getPrizeConverted(prize, prizeunit, rate, unitToGet) if prizeunit == unitToGet then return prize elseif prize and rate then return util_math.roundnum(prize * tonumber(rate), .01) else return nil end end

function h.getPrizePct(prizeNum, lineArgs, processedArgs) if lineArgs.prizepct then return lineArgs.prizepct .. '%'	elseif processedArgs.totalprizenum then return util_math.roundnum(prizeNum / processedArgs.totalprizenum * 100, .01) .. '%'	else return nil end end

function h.getPrizeToggle(prizes, prizeunit) if not HAS_TOGGLES then return nil end local tbl = mw.html.create for _, v in ipairs(PRIZE_TOGGLE_DATA.order) do		util_toggle.oflCellClasses(tbl:tag('span'):wikitext(prizes[v]), PRIZE_TOGGLE_DATA, v)	end return tostring(tbl) end

function h.argsToCargoLine(lineArgs, processedArgs, lineDisplay) if processedArgs.nocargo or lineArgs.nocargo then return end local cargoData = { Event = util_vars.getVar('Event Name'), Tier = util_vars.getVar('Event Tier'), Date = lineArgs.date or util_vars.getVar('Event Date'), Place = h.validateAndGetPlace(lineArgs), Prize = lineArgs.prize, PrizeUnit = processedArgs.prizeunit, Prize_Markup = lineArgs.prize and processedArgs.currency:short(lineArgs.prize), Prize_USD = lineDisplay.usd_number, Prize_Euro = lineDisplay.eur_number, PrizeOther = lineArgs.otherprize, Team = lineArgs.team and m_team.teamlinkname(lineArgs.team), UniqueLine = mw.title.getCurrentTitle.text .. '_' .. util_vars.setGlobalIndex('TRL_line'), Phase = processedArgs.phase, OverviewPage = mw.title.getCurrentTitle.text, ForceNewPlace = lineArgs.forcenewplace, }	local extraCargoData if processedArgs.preload == 'Player' then extraCargoData = h.getPlayerSpecificCargo(lineArgs, processedArgs, lineDisplay) elseif processedArgs.preload == 'Team' then extraCargoData = h.getTeamSpecificCargo(lineArgs, processedArgs, lineDisplay) end util_table.merge(cargoData, extraCargoData) -- return a table containing the table because maybe we want 2 dif cargo tables -- storing per line, e.g. TournamentResults & TournamentRosters return { cargoData } end

function h.validateAndGetPlace(lineArgs) if not lineArgs.place then error(i18n.print('error_missingPlace')) end return util_math.deserialize(lineArgs.place) or lineArgs.place end

function h.getPlayerSpecificCargo(lineArgs, processedArgs, lineDisplay) local playerData = { _table = 'TournamentResults1v1', Player = lineArgs.player, PlayerLink = lineArgs.playerlink or lineArgs.player, }	local lastResult = h.playerLastResultCargo(lineArgs) util_table.merge(playerData, lastResult) return playerData end

function h.playerLastResultCargo(lineArgs) local lastResult = { LastResult = lineArgs.lastresult, LastOpponent = lineArgs.lastopponent, LastOpponentLink = lineArgs.lastopponentlink or lineArgs.lastopponent, LastOutcome = lineArgs.lastoutcome and util_esports.outcomes[lineArgs.lastoutcome:lower], LastOpponentTeam = lineArgs.lastopponentteam and m_team.teamlinkname(lineArgs.lastopponentteam), OverviewPage = mw.title.getCurrentTitle.text, }	return lastResult end

function h.getTeamSpecificCargo(lineArgs, processedArgs, lineDisplay) local teamData = { _table = 'TournamentResults', Place_Number = lineArgs.placeObject:number, Qualified = h.determineQualified(lineArgs), RosterPage = lineArgs.rosterpage or mw.title.getCurrentTitle.text, }	local teamlink = lineArgs.team and m_team.teamlinkname(lineArgs.team) or '' teamData.PageAndTeam = teamData.RosterPage .. '_' .. teamlink .. '_' .. util_vars.setGlobalIndex('PAT_line') local lastResult = h.teamLastResultCargo(lineArgs) util_table.merge(teamData, lastResult) return teamData end

function h.determineQualified(lineArgs) if util_args.castAsBool(lineArgs.qualified) then return 'Yes' end return lineArgs.place == 'Q' and 'Yes' end

function h.teamLastResultCargo(lineArgs) if lineArgs.groupstage then return h.lastResultGroupstage(lineArgs) elseif lineArgs.lastresultpoints then return h.lastResultPoints(lineArgs) else return h.lastResultDefault(lineArgs) end end

function h.lastResultGroupstage(lineArgs) return { LastResult = lineArgs.lastresult or lineArgs.groupstage, GroupName = lineArgs.group or 'Group Stage', LastOpponent_Markup = m_team.rightshortlinked('group stage') } end

function h.lastResultPoints(lineArgs) return { LastResult = lineArgs.lastresultpoints, LastOpponent_Markup = m_team.rightshortlinked('points') } end

function h.lastResultDefault(lineArgs) local lastResult = { LastResult = lineArgs.lastresult, LastOutcome = util_esports.getOutcomeName(lineArgs.lastoutcome) }	if lineArgs.lastopponent then lastResult.LastTeam = m_team.teamlinkname(lineArgs.lastopponent) lastResult.LastOpponent_Markup = m_team.rightshortlinked(lineArgs.lastopponent) end return lastResult end

-- cargo function h.storeCargo(linesData) if h.doWeStoreCargo then for _, line in ipairs(linesData) do			local cargoData = line.cargo if cargoData then for _, tbl in ipairs(cargoData) do					util_cargo.store(tbl) end end end end end

function h.doWeStoreCargo return mw.title.getCurrentTitle.nsText == '' end

-- make output function h.makeOutput(args, processedArgs, cols, linesData) h.initializeToggleData local output = mw.html.create output:wikitext(h.introSentence(args, processedArgs)) h.printCurrencyToggles(output) h.printTable(output, cols, linesData) h.creditCurrencyRates(output, args, processedArgs) util_footnote.printTexts(output) return output end

function h.initializeToggleData local i = util_vars.setGlobalIndex('TRL_i') TOGGLES.class = TOGGLES.class:format(i) end

function h.introSentence(args, processedArgs) if not args.totalprize then return '' end local tempdate = util_args.nilToFalse(args.tempprizedate) local sentenceParts = { processedArgs.currency:long(processedArgs.totalprize), tempdate and i18n.print('temp_date', tempdate), i18n.print('intro'), }	return util_table.concat(sentenceParts, ' '), util_refs.ref(args.prize_ref) end

function h.printCurrencyToggles(output) if not HAS_TOGGLES then return end local div = output:tag('div'):addClass('toggle-button') util_toggle.printOptionFromListTogglers(div, PRIZE_TOGGLE_DATA) end

function h.printTable(output, cols, linesData) local tbl = output:tag('table') :addClass('wikitable2') :addClass('tournament-results') :addClass('zebra-manual') h.printStartCols(tbl, cols) local hidden = h.printLines(tbl, cols, linesData) if hidden then h.endHiddenResults(tbl, #cols) end end

function h.printStartCols(tbl, cols) local tr = tbl:tag('tr') for i, v in ipairs(cols) do		tr:tag('th') :wikitext(cols.displays[i]) end end

function h.printLines(tbl, cols, linesData) local hidden = false local rowspanLeft = 0 local odd = false for _, line in ipairs(linesData) do		if line.hide then hidden = true h.printShowButton(tbl, #cols) end local tr = tbl:tag('tr') if hidden then util_toggle.sepCellClasses(tr, TOGGLES) end local isFullRow = h.printOneLine(tr, cols, line, rowspanLeft, odd) if rowspanLeft == 1 then odd = not odd end if odd then tr:addClass('zebra-manual-odd') end if line.rowspan then rowspanLeft = line.rowspan else rowspanLeft = rowspanLeft - 1 end end return hidden end

function h.printShowButton(tbl, colspan) local tr = tbl:tag('tr') local th = tr:tag('th') :attr('colspan', colspan) util_toggle.printSepToggler(th, TOGGLES) util_toggle.sepCellClasses(tr, TOGGLES, true) end

function h.printOneLine(tr, cols, lineDisplay, rowspanLeft, odd) local isFullRow = false for _, col in ipairs(cols) do		if rowspanLeft > 1 and ROWSPANS[col] then -- pass else local td = tr:tag('td') :wikitext(lineDisplay[col]) :addClass(CLASSES[col]) util_footnote.tag(td, lineDisplay[col .. 'footnote']) if ROWSPANS[col] then td:attr('rowspan', lineDisplay.rowspan) end end end end

function h.endHiddenResults(tbl, colspan) local tr = tbl:tag('tr') local th = tr:tag('th') :attr('colspan', colspan) util_toggle.sepCellClasses(tr, TOGGLES, false) util_toggle.printSepToggler(th, TOGGLES, true) end

function h.creditCurrencyRates(output, args, processedArgs) if not (args.conversionsource and processedArgs.hasconversion) then return nil end local source = args.conversionsource:lower local date = args.conversiondate local rates = { USD = processedArgs.usdrate, Euros = processedArgs.eurorate, }	local originalUnit = processedArgs.currency output:wikitext(i18n.print('credit_' .. source,date or i18n.print('unknown_date'))) local ul = output:tag('ul') for _, v in ipairs(CURRENCY_ORDER) do		if rates[v] then util_vars.log(v) ul:tag('li') :wikitext(originalUnit:long(1)) :wikitext(' = ') :wikitext(Currency(v):long(rates[v])) end end return output end

return p