mic_none

Module:Medals table country/sandbox Source: en.wikipedia.org/wiki/Module:Medals_table_country/sandbox

local p = {}

-- Load the flagicon data module used to map years to host country flags
local flagicon_data = require("Module:Medals table country/data/Olympic Games host flagicons")

function p.render(frame)
	-- Get arguments passed from the parent template
    local args = frame:getParent().args
    local country = args["country"] or "Country"
    local maxRows = 50

    -- Control flags
    local show_dual_ranks = args["show_dual_ranks"] == "yes"
    local show_games_flag = args["show_games_flag"] == "yes"

    -- Determine if the table is for Summer or Winter Olympics
    local season = (args["season"] or "summer"):lower()
    local is_winter = (season == "winter")
    local season_name = is_winter and "Winter" or "Summer"

    -- Dynamically require ranking data based on season
    local ranking_data
    if is_winter then
        ranking_data = require("Module:Medals table country/data/Winter Olympics ranking")
    else
        ranking_data = require("Module:Medals table country/data/Summer Olympics ranking")
    end
	
	-------------------------------------------------------------------
    -- Helper functions
    -------------------------------------------------------------------
    -- Helper for border-style adds a purple border around the row (hosted games).
		local function get_border_style(position) 
			local base = 'border-top:3px solid purple; border-bottom:3px solid purple;'
			if position == "left" then
				return base .. ' border-left:3px solid purple;'
			elseif position == "right" then
				return base .. ' border-right:3px solid purple;'
			else
				return base
			end
		end
    -- Helper to find rank by country
    local function find_rank(list, name)
        for _, entry in ipairs(list) do
            if entry.country == name then
                return entry.rank
            end
        end
        return nil
    end
	
	-- Helper: extract leading numeric value for calculations while preserving full notext for display of added notes 
	   local function extract_number_and_text(val)
		   val = tostring(val or "")
		   local num = val:match("^%s*(%d+)")
		   return tonumber(num) or 0, val
	   end
		-- Helper: Split number and text to extract the number (to compute things like max athletes), but separate the number from the trailing wikitext (like references)
		local function split_number_and_text(val)
			val = tostring(val or ""):match("^%s*(.-)%s*$") -- trim outer spaces
			local num_str = val:match("^(%d+)")
			local num = tonumber(num_str) or 0
			local note = val:match("^%d+%s*(.*)") or ""
			return num, note
		end
	-------------------------------------------------------------------
    -------------------------------------------------------------------

    -- Fetch or derive total rank values
    local alltime_gold_rank_raw = args["alltime_gold_rank"]
    if not alltime_gold_rank_raw or alltime_gold_rank_raw == "" then
        local gold_rank = find_rank(ranking_data.gold_ranking, country)
        alltime_gold_rank_raw = gold_rank and tostring(gold_rank) or ""
    end

    local alltime_medal_rank_raw = args["alltime_medal_rank"]
    if not alltime_medal_rank_raw or alltime_medal_rank_raw == "" then
        local alltime_medal_rank = find_rank(ranking_data.total_ranking, country)
        alltime_medal_rank_raw = alltime_medal_rank and tostring(alltime_medal_rank) or ""
    end

    -------------------------------------------------------------------
    -- Step 1: Collect raw input rows from arguments into a raw_rows list
    -------------------------------------------------------------------
    local raw_rows = {}

    for i = 1, maxRows do
        local games = args["row"..i.."_games"]
        if games and games ~= "" then
	        if games == "note" then
	            local participation_text = args["row"..i.."_participation"] or ""
	            table.insert(raw_rows, {
	                is_note = true,
	                note_text = participation_text,
	            })
	        else
            local participation = args["row"..i.."_participation"]
            local is_host = args["row"..i.."_host"] == "yes"
            
			
            -- Parse athletes and medals
			local athletes_val, athletes_text = split_number_and_text(args["row"..i.."_athletes"])
            local gold_num, gold_text = extract_number_and_text(args["row"..i.."_gold"])
            local silver_num, silver_text = extract_number_and_text(args["row"..i.."_silver"])
            local bronze_num, bronze_text = extract_number_and_text(args["row"..i.."_bronze"])
            local rank_raw = args["row"..i.."_rank"] or ""
            local medal_rank_raw = args["row"..i.."_medal_rank"] or ""

            -- Add row to list
            table.insert(raw_rows, {
                games = games,
                athletes_val = athletes_val,
				athletes_text = athletes_text,
                participation = participation,
                gold = gold_num,
                silver = silver_num,
                bronze = bronze_num,
                gold_display = gold_text,
                silver_display = silver_text,
                bronze_display = bronze_text,
                rank_raw = rank_raw,
                medal_rank_raw = medal_rank_raw,
                is_host = is_host,
            })
            end
		end
    end

    -------------------------------------------------------------------
    -- Step 2: Process rows and merge participation rows with rowspan
    -------------------------------------------------------------------
    local rows = {}
    local i = 1
    while i <= #raw_rows do
        local row = raw_rows[i]
        
        if row.is_note then
        table.insert(rows, row)
        i = i + 1
        
        -- Merge rows that have identical participation notes
        elseif row.participation and row.participation ~= "" then
            local rowspan = 1
            local is_host_in_merged = row.is_host
            for j = i + 1, #raw_rows do
                if raw_rows[j].participation == row.participation then
                    rowspan = rowspan + 1
                    if raw_rows[j].is_host then
                        is_host_in_merged = true
                    end
                else
                    break
                end
            end
            local merged_games = {}
            for k = i, i + rowspan - 1 do
                table.insert(merged_games, raw_rows[k].games)
            end
            table.insert(rows, {
                participation = row.participation,
                rowspan = rowspan,
                games_list = merged_games,
                merged = true,
                is_host = is_host_in_merged,
            })
            i = i + rowspan
        else
            -- Regular row: build structured row with medals and rankings
            local year = row.games and row.games:match("(%d%d%d%d)") or ""
            local athletes_num = tonumber(row.athletes_val) or 0
			local athletes_note = tostring(row.athletes_text) or ""
			local athletes_cell = string.format("[[%s at the %s %s Olympics|%d]] %s", country, year, season_name, athletes_num, athletes_note)
																							 
            -- Medal totals
            local total = row.gold + row.silver + row.bronze

            -- Helper: build medal table rank links
            local function make_rank_link(rank_raw)
                local rank_num = tonumber(rank_raw)
                local medal_table_title = string.format("%s %s Olympics medal table", year, season_name)
                if rank_num then
                    return string.format("[[%s|%d]]", medal_table_title, rank_num), rank_num
                elseif rank_raw == "" then
                    return string.format("[[%s|–]]", medal_table_title), nil
                elseif rank_raw ~= "" then
                    return rank_raw, nil
                else
                    return "", nil
                end
            end

            -- Rank links
            local gold_rank_link, gold_rank_num = make_rank_link(row.rank_raw)
            local medal_rank_link, medal_rank_num = make_rank_link(row.medal_rank_raw)

            -- Background color for top 3 ranks
            local function get_rank_color(rank)
				return (rank == 1 and "#F7F6A8") or (rank == 2 and "#dce5e5") or (rank == 3 and "#ffdab9") or ""
			end
            local bgcolor_gold_ranking = get_rank_color(gold_rank_num)
            local bgcolor_medal_ranking = get_rank_color(medal_rank_num)

            -- Add full row to output list
            table.insert(rows, {
                games = row.games,
                athletes = athletes_cell,
                athletes_num = athletes_num,
                gold = row.gold,
                silver = row.silver,
                bronze = row.bronze,
                gold_display = row.gold_display,
                silver_display = row.silver_display,
                bronze_display = row.bronze_display,
                total = total,
                gold_rank = gold_rank_link,
                total_rank = medal_rank_link,
                bgcolor_gold_ranking = bgcolor_gold_ranking,
                bgcolor_medal_ranking = bgcolor_medal_ranking,
                is_host = row.is_host,
                merged = false,
            })
            i = i + 1
        end
    end

    -------------------------------------------------------------------
    -- Step 3: Compute max athletes, max medals and totals
    -------------------------------------------------------------------
    local max_athletes, max_gold, max_silver, max_bronze, max_total = 0, 0, 0, 0, 0
    local total_gold, total_silver, total_bronze = 0, 0, 0
    for _, row in ipairs(rows) do
        if not row.merged and row.gold and row.silver and row.bronze then
            total_gold = total_gold + row.gold
            total_silver = total_silver + row.silver
            total_bronze = total_bronze + row.bronze
            if row.athletes_num and row.athletes_num > max_athletes then max_athletes = row.athletes_num end
            if row.gold > max_gold then max_gold = row.gold end
            if row.silver > max_silver then max_silver = row.silver end
            if row.bronze > max_bronze then max_bronze = row.bronze end
            if row.total > max_total then max_total = row.total end
        end
    end

	-- Final totals and all-time rank links (already set above from args or module)
	local total_medals = total_gold + total_silver + total_bronze
	
	-- Generating alltime_gold_rank and alltime_gold_rank_link with the same expression
	local function make_alltime_rank_link(rank)
		return rank ~= "" and string.format("[[All-time Olympic Games medal table#Complete ranked medals (excluding precursors)|%s]]", rank) or ""
	end

	local alltime_gold_rank_link = make_alltime_rank_link(alltime_gold_rank_raw)
	local alltime_medal_rank_link = make_alltime_rank_link(alltime_medal_rank_raw)

    -------------------------------------------------------------------
    -- Step 4: Build the wikitable header
    -------------------------------------------------------------------
    local sticky_header = frame:expandTemplate{ title = "sticky-header" }
    local wikitext = '{| class="wikitable sticky-header" style="text-align:center; font-size:90%;"\n'
    wikitext = wikitext .. "|-\n! Games !! Athletes"
    wikitext = wikitext .. ' !! style="background:gold; width:3.7em; font-weight:bold;"| Gold'
    wikitext = wikitext .. ' !! style="background:silver; width:3em; font-weight:bold;"| Silver'
    wikitext = wikitext .. ' !! style="background:#c96; width:3.7em; font-weight:bold;"| Bronze'
    wikitext = wikitext .. ' !! style="width:3.7em; font-weight:bold;"| Total'

    -- Column headers for rank (1 or 2 columns depending on config)
    if show_dual_ranks then
        wikitext = wikitext .. ' !! style="width:4em; font-weight:bold;"| [[Olympic medal table|<small>{{abbr|Gold Medals|Ranking Gold Medals Table}}</small>]]'
        wikitext = wikitext .. ' !! style="width:4em; font-weight:bold;"| [[Olympic medal table|<small>{{abbr|Total Medals|Ranking Total Medals Table}}</small>]]\n'
    else
        wikitext = wikitext .. ' !! style="width:3em; font-weight:bold;"| Rank\n'
    end

    -- Helper: bold value if it’s the max
    local function bold_if_max(val, max)
        return val > 0 and val == max and ("'''" .. val .. "'''") or tostring(val)
    end

    -- Helper: process flag icon with optional flagicon
    local function format_game_with_flag(game)
        local expanded_game = frame:preprocess(game)
        local year = expanded_game:match("(%d%d%d%d)")
        if show_games_flag and year then
            local flag_name = flagicon_data[season] and flagicon_data[season][year]
            if flag_name then
                return string.format("{{flagicon|%s}} %s", flag_name, expanded_game)
            end
        end
        return expanded_game
    end

    -------------------------------------------------------------------
    -- Step 5: Render each table row
    -------------------------------------------------------------------
    for _, row in ipairs(rows) do
	    if row.is_note then
	        local colspan_val = show_dual_ranks and 8 or 7
	        wikitext = wikitext .. string.format(
	            "|-\n| colspan=%d style=\"text-align:center;\" | %s\n",
	            colspan_val,
	            row.note_text
	        )
	    elseif row.merged then
            -- Participation row with rowspan
            local colspan_val = show_dual_ranks and 7 or 6
            wikitext = wikitext .. string.format(
                "|-\n| align=left %s | %s || colspan=%d rowspan=%d %s | %s\n",
                row.is_host and ('style="' .. get_border_style("left") .. '"') or "",
                format_game_with_flag(row.games_list[1]),
                colspan_val,
                row.rowspan,
                row.is_host and ('style="' .. get_border_style("right") .. '"') or "",
                row.participation
            )
            for i = 2, row.rowspan do
                wikitext = wikitext .. string.format(
                    "|-\n| align=left %s | %s\n",
                    row.is_host and ('style="' .. get_border_style() .. '"') or "",
                    format_game_with_flag(row.games_list[i])
                )
            end
        else
            -- Regular medal row

            local line = "|-\n"
            local game_display = format_game_with_flag(row.games)
			local athletes_display = (row.athletes_num == max_athletes) and ("'''" .. row.athletes .. "'''") or row.athletes
            if row.is_host then
				
                line = line .. string.format('| align=left style="%s" | %s', get_border_style("left"), game_display)
				line = line .. string.format(' || style="%s" | %s', get_border_style(), athletes_display)
                line = line .. string.format(' || style="%s" | %s', get_border_style(), bold_if_max(row.gold, max_gold))
                line = line .. string.format(' || style="%s" | %s', get_border_style(), bold_if_max(row.silver, max_silver))
                line = line .. string.format(' || style="%s" | %s', get_border_style(), bold_if_max(row.bronze, max_bronze))
                line = line .. string.format(' || style="%s" | %s', get_border_style(), bold_if_max(row.total, max_total))
                
				if show_dual_ranks then
                    line = line .. string.format(' || style="%s%s" | %s', row.bgcolor_gold_ranking ~= "" and 'background-color:' .. row.bgcolor_gold_ranking .. '; ' or "", get_border_style(), row.gold_rank)
                    line = line .. string.format(' || style="%s%s" | %s', row.bgcolor_medal_ranking ~= "" and 'background-color:' .. row.bgcolor_medal_ranking .. '; ' or "", get_border_style("right"), row.total_rank)
                else
                    line = line .. string.format(' || style="%s%s" | %s', row.bgcolor_gold_ranking ~= "" and 'background-color:' .. row.bgcolor_gold_ranking .. '; ' or "", get_border_style("right"), row.gold_rank)
                end
            else
                -- Regular non-hosted row
                line = line .. "| align=left | " .. game_display
                line = line .. " || " .. (row.athletes_num == max_athletes and ("'''" .. row.athletes .. "'''") or row.athletes)
                line = line .. " || " .. (row.gold == max_gold and ("'''" .. row.gold_display .. "'''") or row.gold_display)
                line = line .. " || " .. (row.silver == max_silver and ("'''" .. row.silver_display .. "'''") or row.silver_display)
                line = line .. " || " .. (row.bronze == max_bronze and ("'''" .. row.bronze_display .. "'''") or row.bronze_display)
                line = line .. " || " .. bold_if_max(row.total, max_total)
                if show_dual_ranks then
                    line = line .. (row.bgcolor_gold_ranking ~= "" and (' || style="background-color:' .. row.bgcolor_gold_ranking .. '" | ' .. row.gold_rank) or (" || " .. row.gold_rank))
                    line = line .. (row.bgcolor_medal_ranking ~= "" and (' || style="background-color:' .. row.bgcolor_medal_ranking .. '" | ' .. row.total_rank) or (" || " .. row.total_rank))
                else
                    line = line .. (row.bgcolor_gold_ranking ~= "" and (' || style="background-color:' .. row.bgcolor_gold_ranking .. '" | ' .. row.gold_rank) or (" || " .. row.gold_rank))
                end
            end
            wikitext = wikitext .. line .. "\n"
        end
    end

    -------------------------------------------------------------------
    -- Step 6: Add total row at bottom of table
    -------------------------------------------------------------------
	wikitext = wikitext .. "|-\n! colspan=2 | Total"
	wikitext = wikitext .. string.format(" !! %d !! %d !! %d !! %d", total_gold, total_silver, total_bronze, total_medals)
	if show_dual_ranks then
	    wikitext = wikitext .. " !! " .. alltime_gold_rank_link .. " !! " .. alltime_medal_rank_link .. "\n"
	else
	    wikitext = wikitext .. " !! " .. alltime_gold_rank_link .. "\n"
	end
    wikitext = wikitext .. "|}"

    -------------------------------------------------------------------
    -- Final output
    -------------------------------------------------------------------
    local full_wikitext = sticky_header .. "\n" .. wikitext
    return frame:preprocess(full_wikitext)
end

return p