モジュール:Sports table

-- Module to build tables for standings in Sports
-- See documentation for details
require('strict')
local p = {}
-- Helper functions
local function isnotblank(s)
	return s and s:match( '^%s*(.-)%s*$' ) ~= 
end
local function firstnonblank(s1,s2)
	if ( s1 and s1:match( '^%s*(.-)%s*$' ) ~=  ) then
		return s1
	elseif ( s2 and s2:match( '^%s*(.-)%s*$' ) ~=  ) then
		return s2
	end
	return nil
end

-- Main function
function p.main(frame)
	-- Declare locals
	local Args = frame.args
	local Pargs = frame:getParent().args
	local ii_start, ii_end, N_rows_res = 0
	local text_field_result
	local notes_exist = false
	local t = {}
	local t_footer = {}
	local t_return = {}
	local team_list = {}
	local jj, jjj
	
	-- Exit early if we are using section transclusion for a different section
	if( isnotblank(Pargs'transcludesection') and isnotblank(Args'section') ) then
		if( Pargs'transcludesection' ~= Args'section' ) then
			return 
		end
	end
	-- Get the custom start point for the table (most will start by default at 1
	local top_pos = tonumber(Args'highest_pos') or 1
	local N_teams = top_pos - 1 -- Default to 0 at start, but higher number needed to skip certain entries
	
	-- Load modules
	local yesno = require('Module:Yesno')
	-- Load style and (sub) modules
	local style_def = Args'style' or 'WDL'
	-- Historically 'football' exists as style, this is now forwarded to WDL
	if style_def == 'football' then style_def = 'WDL' end
	local p_style = require('モジュール:Sports table/'..style_def)
	local p_sub = require('モジュール:Sports table/sub')
	local p_matches = require('モジュール:Sports results')
	
	-- Random value used for uniqueness
	math.randomseed( os.clock() * 10^8 )
	local rand_val = math.random()
	
	-- Declare colour scheme
	local result_col = {}
	result_col = {green1='#BBF3BB', green2='#CCF9CC', green3='#DDFCDD', green4='#EEFFEE',
		blue1='#BBF3FF', blue2='#CCF9FF', blue3='#DDFCFF', blue4='#EEFFFF',
		yellow1='#FFFFBB', yellow2='#FFFFCC', yellow3='#FFFFDD', yellow4='#FFFFEE',
		red1='#FFBBBB', red2='#FFCCCC', red3='#FFDDDD', red4='#FFEEEE',
		black1='#BBBBBB', black2='#CCCCCC', black3='#DDDDDD', black4='#EEEEEE'}
	
	-- Declare results column header
	local results_header = {}
	results_header = {Q='出場権', QR='出場権または降格',
		P='昇格', PQR='昇格、出場権または降格',
		PR='昇格または降格', R='降格'}
	local results_defined = false -- Check whether this would be needed
	-- Now define line for column header (either option or custom)
	local local_res_header = results_headerArgs'res_col_header' or Args'res_col_header'  or 
	-- Check whether it includes a note
	local res_head_note = Args'note_header_res'
	local res_head_note_text = 
	if res_head_note then
		notes_exist = true
		res_head_note_text = frame:expandTemplate{ title = 'efn', args = { group='注',  res_head_note} }
	end
	local results_header_txt = '! scope="col" |'..local_res_header..res_head_note_text..'\n'
	
	-- Get status option
	local t_status = p_style.status(Args)
	
	-- Alternative syntax for team list
	if Args'team_order' and Args'team_order' ~=  then
		local team_order_offset = (tonumber(Args'team_order_start') or 1) - 1
		local tlist = mw.text.split(Args'team_order', '%s*;,%s*')
		for k, tname in ipairs(tlist) do
			if tname ~=  then
				Args'team' .. (k + team_order_offset) = tname
			end
		end
	end
	
	-- Read in number of consecutive teams (ignore entries after skipping a spot)
	while Args'team'..N_teams+1 ~= nil do
		N_teams = N_teams+1
		-- Sneakily add it twice to the team_list parameter, once for the actual
		-- ranking, the second for position lookup in sub-tables
		-- This is possible because Lua allows both numbers and strings as indices.
		team_listN_teams = Args'team'..N_teams -- i^th entry is team X
		team_listArgs'team'..N_teams = N_teams -- team X entry is position i
	end
	
	-- Show all stats in table or just matches played and points
	local pld_pts_val = firstnonblank(Pargs'only_pld_pts', Args'only_pld_pts') or 'no'
	local full_table
	local hide_class_rules = false
	-- True if par doesn't exist, false otherwise
	-- First convert to lower case if it is a string
	pld_pts_val = string.lower(pld_pts_val)
	if yesno(pld_pts_val) then
		full_table = false
	elseif pld_pts_val=='no_hide_class_rules' then
		full_table = true
		hide_class_rules = true
	else
		full_table = true
	end
	
	-- Show groups or note
	local show_groups_val = firstnonblank(Pargs'show_groups', Args'show_groups') or 'no'
	local group_col = false
	if yesno(show_groups_val) then group_col = true end
	
	-- Show match_table or not
	local show_matches_val = firstnonblank(Pargs'show_matches', Args'show_matches') or 'no'
	local match_table = false
	if yesno(show_matches_val) then match_table = true end
	
	-- Custom position column label or note
	local pos_label = Args'postitle' or 
	if pos_label ==  then
		-- If empty (or undeclared) then default
		pos_label = '順'
	end
	-- Get VTE button text (but only for non-empty text)
	local template_name = Args'template_name' or 
	local VTE_text = 
	if template_name~= then
		VTE_text = frame:expandTemplate{ title = 'navbar', args = { mini=1, style='float:right',  template_name} }
	end
	-- Write column headers
	t_return = p_style.header(t,Args,p_sub,pos_label,group_col,VTE_text,full_table,results_header_txt)
	if match_table then
		-- Add empty column header
		t_return.count = t_return.count+1
		table.insert(t_return.tab_text,'! scope="row" class="unsortable" style="background-color:white;border-top:white;border-bottom:white;line-width:3pt;"| \n')
		
		-- Add rest of header
		t_return = p_matches.header(t_return,Args,p_sub,N_teams,team_list)
	end
	t = t_return.tab_text
	local N_cols = t_return.count
	
	-- Determine what entries go into table
	-- Find out which team to show (if any)
	local ii_show = team_listfirstnonblank(Pargs'showteam', Args'showteam') -- nil if non-existant
	-- Start and end positions to show
	local n_to_show = tonumber(Args'show_limit') or N_teams
	-- Check for "legal value", if not legal (or non declared), then show all
	local check_n = ((n_to_show>=(N_teams-top_pos+1)) or (n_to_show=(N_teams+1-n_show_side) then
			-- Bottom team
			ii_start = N_teams+1-n_to_show
			ii_end = N_teams
		else
			-- Normal case
			ii_start = ii_show-n_show_side
			ii_end = ii_show+n_show_side
		end
	end
	
	-- For results column
	local new_res_ii = ii_start
	-- Pre-check for existence of column
	for ii = ii_start, ii_end do
		if Args'result'..ii then results_defined = true end
	end
	-- Remove results header if it is unused
	if full_table and not results_defined then
		-- First get it as one string, then use string replace to replace that header by empty string
		local t_str = tostring(table.concat(t))
		t_str = mw.ustring.gsub( t_str, results_header_txt,  )
		N_cols = N_cols-1 -- There is actually one column less
		t = {}
		table.insert(t, t_str)
	end
	
	-- Write rows
	local team_name, team_code_ii, team_code_jj, pos_num, group_txt, note_local
	local note_string, note_local, note_local_num, note_id
	local note_id_list = {}
	local hth_id_list = {}
	for ii = ii_start, ii_end do
		-- First get code
		team_code_ii = team_listii
		-- Now read values
		pos_num = Args'pos_'..team_code_ii			or ii
		group_txt = Args'group_'..team_code_ii		or ' '
		team_name = Args'name_'..team_code_ii		 	or team_code_ii
		note_local = Args'note_'..team_code_ii 		or nil
		
		-- Does it need a promotion/qualification/relegation tag
		local result_local = Args'result'..ii or nil
		local bg_col = nil
		-- Get local background colour
		if result_local then
			bg_col = result_colArgs'col_'..result_local or Args'col_'..result_local or 'white'
			bg_col = 'background-color:'..bg_col..';' 	-- Full style tag
		end
		if not bg_col then bg_col = 'background-color:transparent;' end -- Becomes default if undefined
		
		-- Bold this line or not
		local ii_fw = ii == ii_show and 'font-weight: bold;' or 'font-weight: normal;'
		
		-- Check whether there is a note or not, if so get text ready for it
		if note_local and full_table then
			-- Set global check for notes to true
			notes_exist = true
			-- There are now 3 options for notes
			-- 1) It is a full note
			-- 2) It is a referal to another note (i.e. it's just a team code; e.g. note_AAA=Text, note_BBB=AAA) in which the note for BBB should link to the same footnote as AAA, with
			-- 2a) The other linked note exist in the part of the table shown
			-- 2b) The part of the note does not exist in the part of the table shown
			if not Args'note_'..note_local then
				-- Option 1
				-- Now define the identifier for this
				note_id = '"table_note_'..team_code_ii..rand_val..'"' -- Add random end for unique ID if more tables are present on article (which might otherwise share an ID)
				note_id_listteam_code_ii = note_id
				
				-- Call refn template
				note_string = frame:expandTemplate{ title = 'efn', args = { group='注',  name=note_id, note_local} }
			else 
				-- Option 2
				-- It is option 2a in either one if either the main note is inside the sub-table
				--                                  or another ref to that note is inside the sub-table
				-- Basically when it either has been defined, or the main link will be in the table
				note_local_num = team_listnote_local
				if note_id_listnote_local or ((note_local_num >= ii_start) and (note_local_num '..string.lower(status_string)..' – ' 
				else
					status_string = ' ('..status_string..')' 
				end
			end
		end
		
		-- Now build the rows
		table.insert(t,'
		table.insert(t,'! scope="row" style="text-align: center;'..ii_fw..bg_col..'"| '..pos_num..'\n')	-- Position number
		if full_table and group_col then
			table.insert(t,'| style="'..ii_fw..bg_col..'" |'..group_txt..'\n') 							-- Group number/name
		end
		-- Build the team string order based on status position
		local team_string
		if t_status.position == 'before' then
			team_string = status_string..team_name..note_string
		else
			team_string = team_name..note_string..status_string
		end
		table.insert(t,'| style="text-align: left; white-space:nowrap;'..ii_fw..bg_col..'"| '..team_string..'\n')-- Team (with possible note)
		-- Call to subfunction
		t_return = p_style.row(frame,t,Args,p_sub,notes_exist,hth_id_list,full_table,rand_val,team_list,team_code_ii,ii_start,ii_end,ii_fw,bg_col,N_teams,ii,ii_show)
		t = t_return.t
		notes_exist = t_return.notes_exist
		hth_id_list = t_return.hth_id_list
		
		-- Now check what needs to be added inside the results column
		if full_table then
			local res_jjj
			if ii == new_res_ii then
				-- First check how many rows you need for this
				N_rows_res = 1
				jjj = ii+1
				result_local = Args'result'..ii or 
				local cont_loop = true
				while (jjj=ii) and (ii_show= ii_start) and (note_local_num '
	
	if footer_break and (not (string.lower(update)=='complete')) then table.insert(t_footer,stack_string) end
	
	table.insert(t_footer,'出典: '..source)
	if class_rules and full_table and (not hide_class_rules) then
		table.insert(t_footer,'順位の決定基準: '..class_rules)
	end
	
	-- Now for the named status
	local status_exist = false
	local status_string = 
	local curr_letter
	for jjj = 1,mw.ustring.len(t_status.letters) do
		curr_letter = mw.ustring.upper(mw.ustring.sub(t_status.letters,jjj,jjj))
		if t_status.calledcurr_letter then
			if (footer_break and status_exist) then
				status_string = status_string..stack_string
			end
			if t_status.position == 'before' then
				status_string = status_string....string.lower(curr_letter)..' – '..t_status.codecurr_letter..'; '
			else
				status_string = status_string..'('..curr_letter..') '..t_status.codecurr_letter..'; '
			end
			status_exist = true
		end
	end
	-- Now end it with a point instead (if it contains entries the '; ' needs to be removed)
	if status_exist then
		status_string = ..mw.ustring.sub(status_string,1,mw.ustring.len(status_string)-2)..'.'
		table.insert(t_footer,status_string)
	end
	-- Add notes (if applicable)
	if notes_exist then
		table.insert(t_footer,'注釈:')
		-- As reflist size text
		t_footer = ..table.concat(t_footer)..
		t_footer = t_footer..frame:expandTemplate{ title = 'notelist', args = { group='注'} }
	else
		-- As reflist size text
		t_footer = ..table.concat(t_footer)..
	end
	-- Add footer to main text table
	table.insert(t,t_footer)
	
	return table.concat(t)
end

return p