Module:TeamCard

From TwogPedia
Revision as of 15:26, 22 August 2022 by Couchor (talk | contribs) (Created page with "local p = {} local getArgs = require('Module:Arguments').getArgs local Logic = require('Module:Logic') local Variables = require('Module:Variables') local Flag = require('Module:Flags') local Icon local Team = require('Module:Team') local placementCell = require('Module:Placement')._placement local Custom = require('Module:TeamCard/Custom') local Storage = require('Module:TeamCard/Storage') local Table = require('Module:Table') local args local GLOBALDATE local TABLE_HE...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:TeamCard/doc

local p = {}
local getArgs = require('Module:Arguments').getArgs
local Logic = require('Module:Logic')
local Variables = require('Module:Variables')
local Flag = require('Module:Flags')
local Icon
local Team = require('Module:Team')
local placementCell = require('Module:Placement')._placement
local Custom = require('Module:TeamCard/Custom')
local Storage = require('Module:TeamCard/Storage')
local Table = require('Module:Table')

local args
local GLOBALDATE
local TABLE_HEIGHT, HEIGHT_ROW, HEIGHT_ROW_PLACEMENT = 28, 26, 30
local DEFAULT_ROW_NUMBER, MAX_PLAYERS, EXTRA_ROWS
local ROWCOUNTS = {t1 = 0, t2 = 0, t3 = 0}
local SETCARD2, SETCARD3
local TEAMS = {}
local IMCOUNT, PCOUNT, CCOUNT = 1, 1, 1
local PLAYERS = {}
local OPTIONS = {
	sideTabs = false,
	backgroundColor = true,
}

function p.draw(frame)
	args = getArgs(frame)
	p._parseArgs(args)
	
	if args.iconModule then
		Icon = mw.loadData(args.iconModule)
	end
	
	local wrapper = p._createTable()
	if args.columns then
		wrapper = p._columns(args.columns) .. tostring(wrapper)
	end
	
	local shouldDisableLpdbStorage = Logic.readBool(mw.ext.VariablesLua.var('disable_LPDB_storage'))
	local shouldDisableSmwStorage = Logic.readBool(mw.ext.VariablesLua.var('disable_SMW_storage'))
	if not(shouldDisableLpdbStorage or shouldDisableSmwStorage) and (args.disable_storage or args.nostorage or 'false') ~= 'true' and mw.title.getCurrentTitle():inNamespaces(0) then
		local playerPrize = p._prizePlayers()
		if Custom._saveToLpdb then
			Custom._saveToLpdb(args, TEAMS, PLAYERS, playerPrize)
		else
			Storage.saveToLpdb(args, TEAMS, PLAYERS, playerPrize)
		end
		if args.smwMVP then
			p._smwMVP(PLAYERS)
		end
	end
	
	return wrapper
end

function p._parseArgs(args)
	GLOBALDATE = args.date or Variables.varDefaultMulti('tournament_enddate', 'tournament_startdate', 'tournament_date', 'tournament_edate', 'tournament_sdate') or os.date('%F')
	DEFAULT_ROW_NUMBER = tonumber(args.defaultRowNumber or 5)
	EXTRA_ROWS = tonumber(args.extraRows or 0)
	MAX_PLAYERS = tonumber(args.maxPlayers or 10)
	
	OPTIONS.sideTabs = Logic.readBool(args.sidetabs)
	OPTIONS.backgroundColor = not Logic.readBool(args.nobgcolor)
	
	TEAMS.team1 = p._TeamName(frame, args.team, args.link)
	TEAMS.teamtemplate = p._TeamTemplate(frame, args.team, args.link)
	args.image1 = args.image1 or args.image
	args.imagedark1 = args.imagedark1 or args.imagedark or args.imagedarkmode1 or args.imagedarkmode
	args.image1, args.imagedark1 = p._queryLogo(args.image1, args.imagedark1, TEAMS.team1)
	if args.team2 then
		TEAMS.team2 = p._TeamName(frame, args.team2, args.link2)
		IMCOUNT = 2
		args.image2, args.imagedark2 = p._queryLogo(args.image2, args.imagedark2 or args.imagedarkmode2, TEAMS.team2)
		if args.team3 then
			TEAMS.team3 = p._TeamName(frame, args.team3, args.link3)
			IMCOUNT = 3
			args.image3, args.imagedark3 = p._queryLogo(args.image3, args.imagedark3 or args.imagedarkmode3, TEAMS.team3)
		end
	end
	
	local mapArgumentsByPrefix = function(args, prefix, f)
		local function indexFromKey(key)
			local foundPrefix, index = key:match('^(.-)(%d+)$')
			if foundPrefix == prefix then
				return tonumber(index), foundPrefix
			else
				return nil
			end
		end

		return Table.mapArguments(args, indexFromKey, f)
	end
	
	local t1Count = math.max(Table.size(mapArgumentsByPrefix(args, 'p', function(index, prefix)
		return prefix .. index
	end)), DEFAULT_ROW_NUMBER)
	
	local subCount = Table.size(mapArgumentsByPrefix(args, 's', function(index, prefix)
		return prefix .. index
	end))
		
	for i=1, 3 do
		local abbr
		if i == 1 then
			abbr = ''
		else
			abbr = 't' .. i
			args[abbr .. 'type'] = (args[abbr .. 'title'] and args[abbr .. 'title'] == 'Staff' and 'Staff') or args[abbr .. 'type']
		end
		args[abbr .. 'c1'] = args[abbr .. 'c1'] or args[abbr .. 'c']
		args[abbr .. 'c1link'] = args[abbr .. 'c1link'] or args[abbr .. 'clink']
		args[abbr .. 'c1flag'] = args[abbr .. 'c1flag'] or args[abbr .. 'cflag']
		args[abbr .. 'c1team'] = args[abbr .. 'c1team'] or args[abbr .. 'cteam']
		args[abbr .. 'c1sub'] = args[abbr .. 'c1sub'] or args[abbr .. 'csub']
		args[abbr .. 'c1flag'] = args[abbr .. 'c1flag'] or args[abbr .. 'cflag']
		args[abbr .. 'c1flag_o'] = args[abbr .. 'c1flag_o'] or args[abbr .. 'cflag_o']
		args[abbr .. 'wins'] = args[abbr .. 'c1wins'] or args[abbr .. 'cwins']
		args[abbr .. 'winsc'] = args[abbr .. 'c1winsc'] or args[abbr .. 'cwinsc']
	end
	
	local coachCount = args.c1 and 1 or 0
	
	local t2Used = false
	if args['s1'] or args['s2'] or args['s3'] or args['s4'] or args['s5'] or args['s6'] or args['sc'] then
		local card, count
		if t1Count + subCount + coachCount <= DEFAULT_ROW_NUMBER + EXTRA_ROWS and not args.sc then
			card = ''
			count = t1Count + 1
		else
			card = 't2'
			t2Used = true
			args[card .. 'type'] = 'sub'
			args[card .. 'title'] = 'Substitutes'
			count = 1 -- let's not leave gaps
		end

		for i=1, MAX_PLAYERS do
			if args['s' .. i] then
				args[card .. 'p' .. count] = args['s' .. i]
				args[card .. 'p' .. count .. 'link'] = args['s' .. i .. 'link']
				args[card .. 'p' .. count .. 'flag'] = args['s' .. i .. 'flag']
				args[card .. 'p' .. count .. 'team'] = args['s' .. i .. 'team']
				args[card .. 'p' .. count .. 'sub'] = args['s' .. i .. 'sub']
				args[card .. 'p' .. count .. 'leave'] = args['s' .. i .. 'leave']
				args[card .. 'p' .. count .. 'wins'] = args['s' .. i .. 'wins']
				args[card .. 'p' .. count .. 'winsc'] = args['s' .. i .. 'winsc']
				args[card .. 'p' .. count .. 'preview'] = args['s' .. i .. 'preview']
				args[card .. 'p' .. count .. 'dnp'] = args['s' .. i .. 'dnp']
				args[card .. 'p' .. count .. 'played'] = args['s' .. i .. 'played'] or args['s' .. i .. 'result']
				args[card .. 'p' .. count .. 'pos'] = 'substitute'
				if args.subdnpdefault then
					if not Logic.readBool(args['s' .. i .. 'result']) then
						args[card .. 'p' .. count .. 'dnp'] = 'true'
					end
				end
				count = count + 1
			end
		end
		
		local count = 1
		if args[card .. 'c1'] then
			count = 2
		end
		args[card .. 'c' .. count] = args['sc']
		args[card .. 'c' .. count .. 'link'] = args['sclink']
		args[card .. 'c' .. count .. 'flag'] = args['scflag']
		args[card .. 'c' .. count .. 'team'] = args['scteam']
		args[card .. 'c' .. count .. 'sub'] = args['scsub']
		args[card .. 'c' .. count .. 'flag'] = args['scflag']
		args[card .. 'c' .. count .. 'flag_o'] = args['scflag_o']
		args[card .. 'c' .. count .. 'played'] = args['scplayed'] or args['scresult']
		args[card .. 'c' .. count .. 'wins'] = args['scwins']
		args[card .. 'c' .. count .. 'winsc'] = args['scwinsc']
		
		for i = 2, 5 do
			if args['sc' .. i] then
				count = count + 1
				args[card .. 'c' .. count] = args['sc' .. i]
				args[card .. 'c' .. count .. 'link'] = args['sc' .. i .. 'link']
				args[card .. 'c' .. count .. 'flag'] = args['sc' .. i .. 'flag']
				args[card .. 'c' .. count .. 'team'] = args['sc' .. i .. 'team']
				args[card .. 'c' .. count .. 'sub'] = args['sc' .. i .. 'sub']
				args[card .. 'c' .. count .. 'flag'] = args['sc' .. i .. 'flag']
				args[card .. 'c' .. count .. 'flag_o'] = args['sc' .. i .. 'flag_o']
				args[card .. 'c' .. count .. 'played'] = args['sc' .. i .. 'played'] or args['sc' .. i .. 'result']
				args[card .. 'c' .. count .. 'wins'] = args['sc' .. i .. 'wins']
				args[card .. 'c' .. count .. 'winsc'] = args['sc' .. i .. 'winsc']
			end
		end
	end
	if args['f1'] or args['fc'] then
		local card
		if t2Used or args['t2p1'] then
			card = 't3'
		else
			card = 't2'
		end
		args[card .. 'type'] = 'former'
		args[card .. 'title'] = 'Former'
		for i=1, MAX_PLAYERS do
			if args['f' .. i] then
				args[card .. 'p' .. i] = args['f' .. i]
				args[card .. 'p' .. i .. 'link'] = args['f' .. i .. 'link']
				args[card .. 'p' .. i .. 'flag'] = args['f' .. i .. 'flag']
				args[card .. 'p' .. i .. 'team'] = args['f' .. i .. 'team']
				args[card .. 'p' .. i .. 'sub'] = args['f' .. i .. 'sub']
				args[card .. 'p' .. i .. 'leave'] = args['f' .. i .. 'leave']
				args[card .. 'p' .. i .. 'wins'] = args['f' .. i .. 'wins']
				args[card .. 'p' .. i .. 'winsc'] = args['f' .. i .. 'winsc']
				args[card .. 'p' .. i .. 'preview'] = args['f' .. i .. 'preview']
				args[card .. 'p' .. i .. 'dnp'] = args['f' .. i .. 'dnp']
				args[card .. 'p' .. i .. 'played'] = args['f' .. i .. 'played'] or args['s' .. i .. 'result']
				if args.formerdnpdefault then
					if not Logic.readBool(args['f' .. i .. 'result']) then
						args[card .. 'p' .. i .. 'dnp'] = 'true'
					end
				end
			else
				break
			end
		end
		
		if args.fc then
			local count = 1
			if args[card .. 'c1'] then
				count = 2
			end
			args[card .. 'c' .. count] = args['fc']
			args[card .. 'c' .. count .. 'link'] = args['fclink']
			args[card .. 'c' .. count .. 'flag'] = args['fcflag']
			args[card .. 'c' .. count .. 'team'] = args['fcteam']
			args[card .. 'c' .. count .. 'sub'] = args['fcsub']
			args[card .. 'c' .. count .. 'flag'] = args['fcflag']
			args[card .. 'c' .. count .. 'flag_o'] = args['fcflag_o']
			args[card .. 'c' .. count .. 'played'] = args['fcplayed'] or args['fcresult']
			args[card .. 'c' .. count .. 'wins'] = args['fcwins']
			args[card .. 'c' .. count .. 'winsc'] = args['fcwinsc']

			for i = 2, 5 do
				if args['fc' .. i] then
					count = count + 1
					args[card .. 'c' .. count] = args['fc' .. i]
					args[card .. 'c' .. count .. 'link'] = args['fc' .. i .. 'link']
					args[card .. 'c' .. count .. 'flag'] = args['fc' .. i .. 'flag']
					args[card .. 'c' .. count .. 'team'] = args['fc' .. i .. 'team']
					args[card .. 'c' .. count .. 'sub'] = args['fc' .. i .. 'sub']
					args[card .. 'c' .. count .. 'flag'] = args['fc' .. i .. 'flag']
					args[card .. 'c' .. count .. 'flag_o'] = args['fc' .. i .. 'flag_o']
					args[card .. 'c' .. count .. 'played'] = args['fc' .. i .. 'played'] or args['fc' .. i .. 'result']
					args[card .. 'c' .. count .. 'wins'] = args['fc' .. i .. 'wins']
					args[card .. 'c' .. count .. 'winsc'] = args['fc' .. i .. 'winsc']
				end
			end
		end
	end
	
	SETCARD2 = (args.t2p1 or args.t2c1) and true or false
	SETCARD3 = (args.t3p1 or args.t3c1) and true or false

	ROWCOUNTS.t1p = math.max(DEFAULT_ROW_NUMBER, Table.size(mapArgumentsByPrefix(args, 'p', function(index, prefix) return prefix .. index end))) 
	ROWCOUNTS.t1 = ROWCOUNTS.t1p + Table.size(mapArgumentsByPrefix(args, 'c', function(index, prefix) return prefix .. index end))
	ROWCOUNTS.t2p = math.max(DEFAULT_ROW_NUMBER, Table.size(mapArgumentsByPrefix(args, 't2p', function(index, prefix) return prefix .. index end)))
	ROWCOUNTS.t2 = ROWCOUNTS.t2p + Table.size(mapArgumentsByPrefix(args, 't2c', function(index, prefix) return prefix .. index end))
	ROWCOUNTS.t3p = math.max(DEFAULT_ROW_NUMBER, Table.size(mapArgumentsByPrefix(args, 't3p', function(index, prefix) return prefix .. index end)))
	ROWCOUNTS.t3 = ROWCOUNTS.t3p + Table.size(mapArgumentsByPrefix(args, 't3c', function(index, prefix) return prefix .. index end))
end

function p._TeamName(frame, team, link)
	local name = link or team
	if name then
		name = Team.page(frame, name, GLOBALDATE)
	end
	
	if not name or name:upper() == 'TBD' then
		name = 'TBD'
		nameRR = 'TBD'
	else
		nameRR = mw.ext.TeamLiquidIntegration.resolve_redirect(name)
	end
	
	if not(TEAMS.team1) then
		Variables.varDefine('team', name)
		Variables.varDefine('teamRR', nameRR)
		TEAMS.lpdb = args.resolveTeam and nameRR or name
	end
	
	return name
end

function p._TeamTemplate(frame, team, link)
	local name = link or team

	if name and mw.ext.TeamTemplate.teamexists(name) then
		return mw.ext.TeamTemplate.raw(name, GLOBALDATE).templatename
	end
end

function p._queryLogo(image, imagedark, name)
	local lpdbResult
	if not image then
		lpdbResult = {ttimage = Team.imageFile(nil, name, GLOBALDATE), ttimagedark = Team.imageFileDark(nil, name, GLOBALDATE)}
		
		local useDefaultFor = mw.loadData('Module:TeamCard/default').useDefaultFor
		
		if not lpdbResult.ttimage or lpdbResult.ttimage:gsub('_', ' ') == useDefaultFor then
			lpdbResult = mw.ext.LiquipediaDB.lpdb('datapoint', {
				limit = 1,
				conditions = '[[type::teamcardimage]] AND [[name::' .. mw.ext.TeamLiquidIntegration.resolve_redirect(name) .. ']] AND [[extradata_startdate::<=' .. GLOBALDATE .. ']] AND [[extradata_enddate::>' .. GLOBALDATE .. ']]',
				query = 'image'
			})[1] or {}
			
			if not lpdbResult.image then
				lpdbResult = mw.ext.LiquipediaDB.lpdb('team', {
					limit = 1,
					conditions = '[[pagename::' .. string.gsub(mw.ext.TeamLiquidIntegration.resolve_redirect(name), ' ', '_') .. ']]',
					query = 'logo'
				})[1] or {}
				
				if not lpdbResult.logo or lpdbResult.logo == '' then
					image = mw.loadData('Module:TeamCard/default')[string.lower(mw.ext.VariablesLua.var('tournament_game') or 'default')]
					if type(image) == 'table' then
						image, imagedark = image.light, image.dark
					end
					if not image then
						error('Add an entry for ' .. string.lower(mw.ext.VariablesLua.var('tournament_game') or 'default') .. ' to Module:TeamCard/default')
					end
				end
			end
		end
	end
	
	image = image or lpdbResult.ttimage or lpdbResult.image or lpdbResult.logo
	image = string.gsub(('File:' .. image), 'File:File:', 'File:')
	imagedark = imagedark or lpdbResult and lpdbResult.ttimagedark or image
	imagedark = string.gsub(('File:' .. imagedark), 'File:File:', 'File:')
	
	return image, imagedark
end

function p._createTable()
	local wrapper = mw.html.create('div')
	local teamcard = p._createOuterTable(args, wrapper)
	
	-- Inner cards
	p._setTableHeights()
	local inner = teamcard:tag('div')
		:addClass('teamcard-inner')
	if SETCARD2 then
		p._createInnerTable( args, inner, '2', string.lower(args.t2type or 'sub') )
	end
	if SETCARD3 then
		p._createInnerTable( args, inner, '3', string.lower(args.t3type or 'former') )
	end
	p._createInnerTable( args, inner, '1', string.lower(args.t1type or 'default') )
	
	-- Logo card
	p._createLogoTable(args, inner)
	
	-- inotes dropdown
	if args.inotes then
		wrapper:tag('div')
			:addClass('toccolours mw-collapsible mw-collapsed')
			:attr('id', 'mw-customcollapsible-' .. p._sanitizeClass(args.class or TEAMS.team1) .. 'notes')
			:css('box-shadow', '3px 3px 5px 0px rgba(0, 0, 0, 0.5)')
			:css('max-width', 'calc(100% - 20px)')
			:css('margin-top', '-5px')
			:css('margin-left', '0 !important')
			:css('position', 'absolute')
			:css('z-index', '17')
			:tag('div'):css('margin-left','-15px'):css('margin-bottom', '-10pt'):wikitext(args.inotes)
	end
	
	return wrapper
end

function p._setTableHeights()
	local mostPlayers = math.max(DEFAULT_ROW_NUMBER, DEFAULT_ROW_NUMBER + EXTRA_ROWS - 1)
	local condenseRow = args.favorDefaultRowNumber and (DEFAULT_ROW_NUMBER + 1) or 1000
	for i=(DEFAULT_ROW_NUMBER + 1), MAX_PLAYERS do
		if args['p' .. i] then
			if i > mostPlayers and not (i == condenseRow and not args.c1) then mostPlayers = mostPlayers + 1 end
		elseif args['t2p' .. i] then
			if i > mostPlayers and not (i == condenseRow and not args.t2c1) then mostPlayers = mostPlayers + 1 end
		elseif args['t3p' .. i] then
			if i > mostPlayers and not (i == condenseRow and not args.t3c1) then mostPlayers = mostPlayers + 1 end
		else
			break
		end
	end
	
	if args.c2 and args.c2OnNewLine then
		mostPlayers = mostPlayers + 1
	end
	for i=3, 5 do
		if args['c' .. i] then
			mostPlayers = mostPlayers + 1
		end
	end
	
	--todo: cleanup condense mechanism
	TABLE_HEIGHT = TABLE_HEIGHT + (HEIGHT_ROW * mostPlayers)
	if (SETCARD2 or SETCARD3) and (not(condenseRow == (mostPlayers + 1)) or args.c1) and not OPTIONS.sideTabs then
		TABLE_HEIGHT = TABLE_HEIGHT + 27
	end
	
	if args.placement then
		TABLE_HEIGHT = TABLE_HEIGHT + HEIGHT_ROW_PLACEMENT
	end
	if args.preview then
		TABLE_HEIGHT = TABLE_HEIGHT + HEIGHT_ROW
	end
	
	-- Compare current table height, minimal height and predefined height then set tables to highest value
	local TCheight = tonumber(args.TCheight or Variables.varDefault('TCheight', '0'))
	TABLE_HEIGHT = math.max(TABLE_HEIGHT, tonumber(args.defaultHeight or '121'), TCheight)
	
	-- Indicator for tallest card; use var:TCheight to standardize height
	if tonumber(Variables.varDefault('maxTCheight', '0')) - TABLE_HEIGHT < 0 then
		Variables.varDefine('maxTCheight', TABLE_HEIGHT)
		mw.log('maxTCheight', TABLE_HEIGHT)
	end
	
	args.imagesize = args.imagesize or ('190x' .. tostring(math.floor(TABLE_HEIGHT/IMCOUNT)-10) .. 'px')
end

function p._createOuterTable(args, root)
	local teamcard = root:tag('div')
		:attr('data-toggle-area', '1')
		:addClass('teamcard toggle-area toggle-area-1')
	if args.showroster == 'true' then
		teamcard:addClass('teamcard-opened')
	end
	
	local headerText = (TEAMS.team1 ~= 'TBD' and '[[' .. TEAMS.team1 .. '|' .. args.team .. ']]') or 'TBD'
	if TEAMS.team2 then
		headerText = headerText .. ' / [[' .. TEAMS.team2 .. '|' .. args.team2 .. ']]'
		if TEAMS.team3 then
			headerText = headerText .. ' / [[' .. TEAMS.team3 .. '|' .. args.team3 .. ']]'
		end
	end
	if args.flag then
		headerText = Flag.Icon({flag = args.flag, shouldLink = true}) .. '&nbsp;' .. headerText
	end
	
	local header = teamcard:tag('center')
		:css('font-weight', 'bold')
		:wikitext(headerText)
	if args.inotes then
		header:css('display','inline-block')
			  :css('vertical-align','bottom')
			  :css('width','100%')
		teamcard:tag('div')
			:css('font-size','60%')
			:css('display','inline-block')
			:css('position','absolute')
			:css('margin-left','-26px')
			:css('margin-top','6px')
			:css('white-space','nowrap')
			:tag('span'):addClass('mw-customtoggle-' .. p._sanitizeClass(args.class or TEAMS.team1) .. 'notes')
				:tag('abbr'):attr('title', 'Click to Toggle'):css('cursor', 'pointer'):wikitext('Notes')
	elseif args.notes then
		header:wikitext('<sup title="' .. args.notes.. '">' .. args.notes .. '</sup>[[Category:Pages with TeamCard using notes parameter]]')
	end

	if args.ref then
		header:wikitext('&nbsp;' .. args.ref .. '[[Category:Pages with TeamCard using ref parameter]]')
	end
	
	return teamcard
end

function p._createInnerTable(args, root, num, cardType)
	local prefix = ''
	if num ~= '1' then
		prefix = 't' .. num
	end
	
	local output = root:tag('table')
		:addClass('wikitable wikitable-bordered list')
		:css('height', tostring(TABLE_HEIGHT) .. 'px')
	
	if OPTIONS.sideTabs then
		output:addClass(p._typeToClass(cardType))
	else
		output:attr('data-toggle-area-content', num)
	end

	if num == '1' and args.hideroster == 'true' or TEAMS.team2 or TEAMS.team3 then
		output:css('display', 'none')
	end
	
	local rowNum = 1
	if num == '1' then
		-- Show the default number of player rows
		for i=1, DEFAULT_ROW_NUMBER do
			p._newRow(output, i, prefix .. 'p' .. tostring(i), cardType, '')
		end
		rowNum = DEFAULT_ROW_NUMBER+1
	end
	
	local tab = prefix
	if tab == '' then tab = 't1' end
	-- Show the rest of the player rows (up to max limit) if they're filled in
	for i=rowNum, MAX_PLAYERS do
		if args[prefix .. 'p' .. tostring(i)] then
			p._newRow(output, i, prefix .. 'p' .. tostring(i), cardType, '')
		elseif OPTIONS.sideTabs and i <= ROWCOUNTS[tab .. 'p'] then
			p._newRow(output, i, prefix .. 'p' .. tostring(i), cardType, '', true)
		else
			break
		end
	end
	
	-- Show coaches if filled in
	if (num == '1' and Variables.varDefault('teamcard_coaches')) or args[prefix .. 'c1'] then
		if args[prefix .. 'c2'] and not(args.c2OnNewLine) and (num == '1' or args.tabsMergeCoaches) then
			p._newRowDouble(output, prefix .. 'c', cardType, 'coach')
		else
			for key, coach in Table.iter.pairsByPrefix(args, prefix .. 'c') do
				p._newRow(output, i, key, cardType, 'coach')
			end
		end
	end
	
	if not OPTIONS.sideTabs then
		p._createButtons(output, tonumber(num))
	end
	
	if args.placement then
		local placeRow = output:tag('tr')
		local placeCell = placeRow:tag('td')
			:addClass('teamcard-placement')
			:attr('colspan', '2')
			:css('height', HEIGHT_ROW_PLACEMENT .. 'px')
		placementCell{parent = placeCell, placement = args.placement, text = args.placementicon}
	end
	
	if args.preview then
		output:tag('tr'):tag('td')
			:addClass('teamcard-preview')
			:attr('colspan', '2')
			:css('height', HEIGHT_ROW .. 'px')
			:wikitext('[' .. args.preview .. ' Preview]')
	end
end

function p._newRow(root, rowNum, prefix, teamcardType, playerType, empty)
	local prefixPos = args[prefix .. 'pos']
	local prefixPosLegacy
	
	if teamcardType ~= 'default' then
		prefixPosLegacy = args[string.sub(prefix, 1, 2) .. 'pos' .. tostring(rowNum)]
	elseif playerType == '' then
		prefixPosLegacy = args['pos' .. tostring(rowNum)]
	end
	
	local row = root:tag('tr')
	if args[prefix .. 'dnp'] and OPTIONS.backgroundColor then
		row:css('background-color', 'rgba(229, 229, 229, 0.5)')
	end
	if args[prefix .. 'leave'] and OPTIONS.backgroundColor then
		row:css('background-color', 'rgba(249, 199, 199, 0.5)')
	end
	
	local position = ''
	local game = args.game or mw.ext.VariablesLua.var('tournament_game')
	if Icon and not empty then
		if args.iconGame and game ~= '' then
			if prefixPos then
				position = Icon[game][string.lower(prefixPos)]
			elseif prefixPosLegacy then
				position = Icon[game][string.lower(prefixPosLegacy)]
			elseif playerType == 'coach' then
				position = Icon[game]['coach']
			elseif teamcardType == 'sub' then
				position = Icon[game]['substitute']
			elseif teamcardType == 'staff' then
				position = Icon[game]['staff']
			else
				position = Icon[game][string.lower(tostring(rowNum))] or tostring(rowNum)
			end
		else
			if prefixPos then
				position = Icon[string.lower(prefixPos)]
			elseif prefixPosLegacy then
				position = Icon[string.lower(prefixPosLegacy)]
			elseif playerType == 'coach' then
				position = Icon['coach']
			elseif teamcardType == 'sub' then
				position = Icon['substitute']
			elseif teamcardType == 'staff' then
				position = Icon['staff']
			else
				position = Icon[string.lower(tostring(rowNum))] or tostring(rowNum)
			end
		end
	elseif not empty then
		if prefixPos == 'substitute' then
			position = '<abbr title="Substitute">S</abbr>'
		elseif (prefixPos or prefixPosLegacy) and teamcardType == 'default' then
			position = (prefixPos or prefixPosLegacy)
		elseif prefixPos then
			position = prefixPos
		elseif playerType == 'coach' then
			position = '<abbr title="Coach">C</abbr>'
		elseif teamcardType == 'sub' then
			position = '<abbr title="Substitute">S</abbr>'
		elseif teamcardType == 'staff' then
			position = '<abbr title="Staff">S</abbr>'
		else
			position = tostring(rowNum)
		end
	end
	
	local cell = row:tag('th'):wikitext(position)
	
	if Icon and not(position) then
		cell:wikitext('[[Category:Pages with unknown player icons]]')
		mw.log('Unknown player icon: ' .. (args[prefix] or ''))
	end
	if playerType == 'coach' and rowNum == 1 and args['coachborder'] ~= 'false' and teamcardType == 'default' then
		cell:css('border-top-width', '2px !important')
	end
	
	local cell = row:tag('td')

	if empty then
		cell:wikitext('&nbsp;')
		p._createSideButton(row, p._typeToClass(teamcardType), rowNum)
		return
	end

	if playerType == 'coach' and rowNum == 1 and args['coachborder'] ~= 'false' and teamcardType == 'default' then
		cell:css('border-top-width', '2px !important')
	end
	if args[prefix .. 'dnp'] then
		if args[prefix .. 'team'] then
			cell:wikitext('<div style="float:right; margin:6px 1px 0 0; font-size:60%"><abbr title="Did not play">DNP</abbr></div>')
		else
			cell:wikitext('<div style="float:right; margin:4px 1px 0 0; font-size:60%"><abbr title="Did not play">DNP</abbr></div>')
		end
	elseif args[prefix .. 'sub'] == 'true' then
		if args[prefix .. 'team'] then
			cell:wikitext('<div style="float:right; margin:6px 1px 0 0; font-size:60%"><abbr title="Registered as Substitute Player">Sub</abbr></div>')
		else
			cell:wikitext('<div style="float:right; margin:4px 1px 0 0; font-size:60%"><abbr title="Registered as Substitute Player">Sub</abbr></div>')
		end
	elseif args[prefix .. 'leave'] then
		if args[prefix .. 'team'] then
			cell:wikitext('<div style="float:right; margin:6px 1px 0 0; font-size:60%"><abbr title="Left the team before the tournament ended">LEFT</abbr></div>')
		else
			cell:wikitext('<div style="float:right; margin:4px 1px 0 0; font-size:60%"><abbr title="Left the team before the tournament ended">LEFT</abbr></div>')
		end
	end

	if args[prefix .. 'wins'] or args[prefix .. 'winsc'] then
		if args[prefix .. 'wins'] then
			for i=1, tonumber(args[prefix .. 'wins']) or 1 do 
			    cell:wikitext('<i class="fas fa-fw fa-trophy-alt" style="float: right; margin-top: 4px;" title="Has won the tournament previously"></i>')			
		    end
		end
		if args[prefix .. 'winsc'] then
			for i=1, tonumber(args[prefix .. 'winsc']) or 1 do 
			    cell:wikitext('<i class="far fa-fw fa-trophy" style="float: right; margin-top: 4px;" title="Has coached a winning team"></i>')			
		    end
		end			
	end

	if args[prefix .. 'team'] then
		cell:wikitext(Team.part(frame, args[prefix .. 'team'], GLOBALDATE))
	end
	args[prefix .. 'link'] = args[prefix .. 'link'] or args[prefix]
	args[prefix .. 'flag'] = args[prefix .. 'flag_o'] or args[prefix .. 'link'] and p._queryPlayer(args[prefix .. 'link']) or args[prefix .. 'flag']
	if args[prefix .. 'flag'] then
		cell:wikitext(Flag.Icon({flag = args[prefix .. 'flag'], shouldLink = true}) .. '&nbsp;')
	else
		cell:wikitext('<span class=flag>[[File:Space filler flag.png|link=]]</span>&nbsp;')
	end
	if args[prefix] then
		if args[prefix .. 'leave'] then
			cell:wikitext('<s>[[' .. args[prefix .. 'link'] .. '|' .. args[prefix] .. ']]</s>')
		else
			cell:wikitext('[[' .. args[prefix .. 'link'] .. '|' .. args[prefix] .. ']]')
		end
		p._playervariables(prefix, teamcardType, playerType)
	else
		cell:tag('abbr'):attr('title', 'To Be Determined'):wikitext('TBD')
	end
	if args[prefix .. 'preview'] then
		cell:wikitext('[[File:Writers_Icon.png|15px|link=' .. args[prefix .. 'preview'] .. '|Preview]]')
	end
	
	if OPTIONS.sideTabs then
		 p._createSideButton(row, p._typeToClass(teamcardType), rowNum)
	end
end

function p._newRowDouble(root, prefix, teamcardType, playerType)
	local row = root:tag('tr')
	
	local cell = row:tag('th')
	if playerType == 'coach' and args['coachborder'] ~= 'false' then
		cell:css('border-top-width', '2px !important')
	end
	cell:tag('abbr'):attr('title', 'Coaches'):wikitext('C')
	
	local cell = row:tag('td')
	if playerType == 'coach' and args['coachborder'] ~= 'false' then
		cell:css('border-top-width', '2px !important')
	end

	for i=1, 5 do
		if args[prefix .. i] then
			args[prefix .. i .. 'link'] = args[prefix .. i .. 'link'] or args[prefix .. i]
			args[prefix .. i .. 'flag'] = args[prefix .. i .. 'flag_o'] or args[prefix .. i .. 'link'] and p._queryPlayer(args[prefix .. i .. 'link']) or args[prefix .. i .. 'flag']
			if i < 3 then
				-- Display only for the first two.
				if args[prefix .. i .. 'flag'] then
					cell:wikitext(Flag.Icon({flag = args[prefix .. i .. 'flag'], shouldLink = true}) .. '&nbsp;')
				else
					cell:wikitext('<span class=flag>[[File:Space filler flag.png|link=]]</span>&nbsp;')
				end
				cell:wikitext('[[' .. args[prefix .. i .. 'link'] .. '|' .. args[prefix .. i] .. ']]&nbsp;')
			end
			p._playervariables(prefix .. i, teamcardType, playerType)
		end
	end
end

function p._queryPlayer(player)
	local results = mw.ext.LiquipediaDB.lpdb('player', {
		limit = 1,
		conditions = '[[pagename::' .. string.gsub(mw.ext.TeamLiquidIntegration.resolve_redirect(player), ' ', '_') .. ']]',
		query = 'nationality',
	})[1] or {}
	
	return results.nationality
end

function p._playervariables(prefix, teamcardType, playerType)
	if not(args[prefix .. 'dnp']) and ( teamcardType == 'default' or not(args.noVarDefault) or (args.noVarDefault and args[prefix ..'played']) ) then
		local prefixVar, prefixLPDB
		if playerType == 'coach' or teamcardType == 'staff' then
			prefixVar = TEAMS.lpdb .. '_c' .. CCOUNT
			prefixLPDB = 'c' .. CCOUNT
			CCOUNT = CCOUNT + 1
		else
			prefixVar = TEAMS.lpdb .. '_p' .. PCOUNT
			prefixLPDB = 'p' .. PCOUNT
			PCOUNT = PCOUNT + 1
		end
		Custom._Players(args, PLAYERS, prefix, prefixVar, prefixLPDB, args.alsoknownas, TEAMS)
	end
end

function p._createButtons(root, cardnumber)
	local button1title, button2title, toggleArea1, toggleArea2
	local teamcard1title = args.t1title or 'Main Roster'
	local teamcard2title = args.t2title or 'Substitutes'
	local teamcard3title = args.t3title or 'Former'
	
	if cardnumber == 1 then
		button1title, button2title = teamcard2title, teamcard3title
		toggleArea1, toggleArea2 = '2', '3'
	elseif cardnumber == 2 then
		button1title, button2title = teamcard1title, teamcard3title
		toggleArea1, toggleArea2 = '1', '3'
	elseif cardnumber == 3 then
		button1title, button2title = teamcard1title, teamcard2title
		toggleArea1, toggleArea2 = '1', '2'
	end
	
	if SETCARD2 and SETCARD3 then
		root:tag('tr'):tag('td')
			:attr('colspan', '2')
			:css('height', '27px')
			:css('padding', '0px !important')
			:wikitext('<span class="toggle-area-button btn btn-theme" data-toggle-area-btn="' .. toggleArea1 .. '" style="width:50%; padding:2px; border:0; border-right:1px solid rgb(187, 187, 187); text-overflow:ellipsis; overflow:hidden;">' .. button1title .. '</span><span class="toggle-area-button btn btn-theme" data-toggle-area-btn="' .. toggleArea2 .. '" style="width:50%; padding:2px; border:0; text-overflow:ellipsis; overflow:hidden;">' .. button2title .. '</span>')
	elseif SETCARD2 then
		root:tag('tr'):tag('td')
			:attr('colspan', '2')
			:css('height', '27px')
			:css('padding', '0px !important')
			:wikitext('<span class="toggle-area-button btn btn-theme" data-toggle-area-btn="' .. toggleArea1 .. '" style="width:100%; padding:2px; border:0; text-overflow:ellipsis; overflow:hidden;">' .. button1title .. '</span>')
	end
end

function p._createSideButton(row, class, id)
	if SETCARD2 and SETCARD3 then
		if id == 1 then
			if class == 'active' then
				p._generateButtonCell(row, ROWCOUNTS.t1, 'former', math.ceil(ROWCOUNTS.t1/2))
			elseif class == 'former' then
				p._generateButtonCell(row, ROWCOUNTS.t3, 'active', math.ceil(ROWCOUNTS.t3/2))
			elseif class == 'subs' then
				p._generateButtonCell(row, ROWCOUNTS.t2, 'former', math.ceil(ROWCOUNTS.t2/2))
			end
		elseif class == 'active' and id == math.ceil(ROWCOUNTS.t1/2) + 1 then
			p._generateButtonCell(row, ROWCOUNTS.t1, 'subs', math.floor(ROWCOUNTS.t1/2))
		elseif class == 'former' and id == math.ceil(ROWCOUNTS.t3/2) + 1 then
			p._generateButtonCell(row, ROWCOUNTS.t3, 'subs', math.floor(ROWCOUNTS.t3/2))
		elseif class == 'subs' and id == math.ceil(ROWCOUNTS.t2/2) + 1 then
			p._generateButtonCell(row, ROWCOUNTS.t2, 'active', math.floor(ROWCOUNTS.t2/2))
		end
	elseif SETCARD2 and id == 1 then
		if class == 'active' then
			p._generateButtonCell(row, ROWCOUNTS.t1, p._typeToClass(args['t2type']))
		else
			p._generateButtonCell(row, ROWCOUNTS.t2, p._typeToClass(args['t2type']))
		end
	end
end

function p._generateButtonCell(row, rowcount, cardType, rowspan)
	cell = row:tag('td')
			  :attr('rowspan', rowspan or rowcount)
			  :css('vertical-align', 'top')
			  :css('max-width', '25px')
			  :css('width', '25px')
			  :css('padding', 0)
	cell:tag('div')
		:addClass('teamcard-' .. cardType .. '-toggle-button')
		:attr('data-width', math.ceil((TABLE_HEIGHT - 2) * (rowspan or rowcount) / rowcount))
end

function p._createLogoTable(args, root)
	local logoTable = root:tag('table'):addClass('wikitable wikitable-bordered logo')
	
	for i=1,3 do
		if args['image' .. i] then
			logoTable:tag('tr'):css('height', tostring(math.floor(TABLE_HEIGHT/IMCOUNT-0.5)) .. 'px;'):tag('td')
				:addClass(TEAMS['team' .. i] == 'TBD' and 'teamlogo-tbd' or '')
				:wikitext('<span class="logo-lightmode">[[' .. args['image' .. i] .. '|' .. args.imagesize .. '|center|link=]]</span>')
				:wikitext('<span class="logo-darkmode" style="display:none">[[' .. args['imagedark' .. i] .. '|' .. args.imagesize .. '|center|link=]]</span>')
		else
			break
		end
	end
	if args.qualifier then
		logoTable:tag('tr'):tag('td')
			:addClass('teamcard-qualifier')
			:attr('colspan', '2')
			:wikitext(args.qualifier)
	end
end

function p._prizePlayers()
	local prizeTeam = mw.ext.LiquipediaDB.lpdb('placement', {
			conditions = '[[pagename::' .. string.gsub(mw.title.getCurrentTitle().text, ' ', '_') .. ']] AND [[participant::' .. TEAMS.team1 .. ']]',
			query = 'prizemoney'
	})[1] or {}
	
	prizeTeam = tonumber(prizeTeam.prizemoney or 0)
	local prizePlayer = 0
	if PCOUNT > 1 and prizeTeam > 0 then
		prizePlayer = prizeTeam / (PCOUNT-1)
	end
	Variables.varDefine('prizemoney_individual', prizePlayer)
	
	return prizePlayer
end

function p._columns(columns)
	local output = ''
	local columns = Variables.varDefault('TeamCard columns')
	local columnsNum = tonumber(Variables.varDefault('TeamCard columns') or 0)
	local columnsReset = tonumber(Variables.varDefault('TeamCard columns reset') or 0)
	if columns then
		if columnsNum == 0 then
			output = '</div></div></div><div class="template-box"><div><div class="template-box" style="padding-right:2em">'
			Variables.varDefine('TeamCard columns', columnsReset - 1)
		else
			if columnsNum == columnsReset then
				output = '<div><div class="template-box" style="padding-right:2em">'
			else
				output = '</div><div class="template-box" style="padding-right:2em">'
			end
			Variables.varDefine('TeamCard columns', columnsNum - 1)
		end
	end
	
	return output
end

--creates SMW objects for templates such as Country representation and MVP tables)
function p._smwMVP(PLAYERS)
	for key, player in Table.iter.pairsByPrefix(PLAYERS, 'p') do
		mw.smw.subobject({
			'Has player pages=' .. PLAYERS[key],
			'Has mvptable flag=' .. (PLAYERS[key .. 'flag'] or 'filler flag')
		}, 'mvptable_flag_' .. (PLAYERS[key .. 'flag'] or 'filler flag'))
	end
end

-- mw.html.addClass and mw.html.attr handles some special character in different ways, causes bad output. Replace all these with another character that both handles the same.
function p._sanitizeClass(class)
	--class = mw.uri.encode(class, "PATH"):gsub("%%",".25")
	local specialCharactersToRemove = "[\(\)']"
	class = class:gsub(' ','_'):gsub(specialCharactersToRemove, '_')
	
	return class
end

function p._typeToClass(cardType)
	if cardType == 'sub' then
		return 'subs'
	elseif cardType == 'former' then
		return 'former'
	else
		return 'active'
	end
end

return p