Actions

Module

DataTableParser: Difference between revisions

From Dune Awakening DB

mNo edit summary
mNo edit summary
Line 10: Line 10:
         return ""
         return ""
     end
     end
   
     -- Convert spaces to underscores for file name
     -- Convert spaces to underscores for file name
     local fileName = resourceName:gsub("%s+", "_") .. "_-_Icon.png"
     local fileName = resourceName:gsub("%s+", "_") .. "_-_Icon.png"
Line 41: Line 42:
     local inTable = false
     local inTable = false
     local headers = {}  -- store the table headers
     local headers = {}  -- store the table headers
     local colMap = {}   -- map header name to column index
     local colMap = {} -- map header name to column index
     local data = {}    -- array of row arrays
     local data = {}    -- array of row arrays
     local currentRow = {}
     local currentRow = {}
     local readingHeader = true  -- before the first row separator ("|-"), treat cells as headers
     local readingHeader = true  -- before the first row separator ("|-"), treat cells as headers


     for _, line in ipairs(filtered) do
     for _, line in ipairs(filtered) do
         if line:match("^{|") then
         if line:match("^{|") then
            -- Start of table (line starts with "{|")
             inTable = true
             inTable = true
         elseif line:match("^|%-") then
         elseif line:match("^|%-") then
            -- Row separator ("|-")
             if not readingHeader and #currentRow > 0 then
             if not readingHeader and #currentRow > 0 then
                 table.insert(data, currentRow)
                 table.insert(data, currentRow)
             end
             end
             currentRow = {}
             currentRow = {}
             readingHeader = false
             readingHeader = false -- after the first row separator, subsequent cells are row cells
         elseif line:match("^!") then
         elseif line:match("^!") then
             local header = line:gsub("^!+%s*", "")
            -- Header cell (starts with "!")
             local header = line:gsub("^!+%s*", "") -- Remove leading ! and whitespace
             header = mw.text.trim(header)
             header = mw.text.trim(header)
             table.insert(headers, header)
             table.insert(headers, header)
         elseif line:match("^|%+") then
         elseif line:match("^|%+") then
             -- Skip table caption lines
             -- Table caption line ("|+"), skip it
         elseif line:match("^|}") then
         elseif line:match("^|}") then
            -- End of table ("|}")
             if not readingHeader and #currentRow > 0 then
             if not readingHeader and #currentRow > 0 then
                 table.insert(data, currentRow)
                 table.insert(data, currentRow)
Line 67: Line 73:
             inTable = false
             inTable = false
         elseif inTable and line:match("^|[^%-+}]") then
         elseif inTable and line:match("^|[^%-+}]") then
             local cell = line:gsub("^|+%s*", "")
            -- Process table cell lines (lines that start with "|" but not "|+", "|-", or "|}")
             local cell = line:gsub("^|+%s*", "") -- Remove leading | and whitespace
             cell = mw.text.trim(cell)
             cell = mw.text.trim(cell)
             if not readingHeader then
             if not readingHeader then
Line 94: Line 101:


     local lines = mw.text.split(content, "\n")
     local lines = mw.text.split(content, "\n")
    -- Filter out magic words, noindex tags, or blank lines
     local filtered = {}
     local filtered = {}
     for _, line in ipairs(lines) do
     for _, line in ipairs(lines) do
Line 106: Line 114:


     local inTable = false
     local inTable = false
     local headers = {}
     local headers = {} -- store the table headers
     local colMap = {}
     local colMap = {} -- map header name to column index
     local data = {}
     local data = {}     -- array of row arrays
     local currentRow = {}
     local currentRow = {}
     local readingHeader = true
 
     local readingHeader = true -- before the first row separator ("|-"), treat cells as headers


     for _, line in ipairs(filtered) do
     for _, line in ipairs(filtered) do
         if line:match("^{|") then
         if line:match("^{|") then
            -- Start of table (line starts with "{|")
             inTable = true
             inTable = true
         elseif line:match("^|%-") then
         elseif line:match("^|%-") then
            -- Row separator ("|-")
             if not readingHeader and #currentRow > 0 then
             if not readingHeader and #currentRow > 0 then
                 table.insert(data, currentRow)
                 table.insert(data, currentRow)
             end
             end
             currentRow = {}
             currentRow = {}
             readingHeader = false
             readingHeader = false -- after the first row separator, subsequent cells are row cells
         elseif line:match("^!") then
         elseif line:match("^!") then
             local header = line:gsub("^!+%s*", "")
            -- Header cell (starts with "!")
             local header = line:gsub("^!+%s*", "") -- Remove leading ! and whitespace
             header = mw.text.trim(header)
             header = mw.text.trim(header)
             table.insert(headers, header)
             table.insert(headers, header)
         elseif line:match("^|%+") then
         elseif line:match("^|%+") then
             -- Skip caption lines
             -- Table caption line ("|+"), skip it
         elseif line:match("^|}") then
         elseif line:match("^|}") then
            -- End of table ("|}")
             if not readingHeader and #currentRow > 0 then
             if not readingHeader and #currentRow > 0 then
                 table.insert(data, currentRow)
                 table.insert(data, currentRow)
Line 133: Line 146:
             inTable = false
             inTable = false
         elseif inTable and line:match("^|[^%-+}]") then
         elseif inTable and line:match("^|[^%-+}]") then
             local cell = line:gsub("^|+%s*", "")
            -- Process table cell lines (lines that start with "|" but not "|+", "|-", or "|}")
             local cell = line:gsub("^|+%s*", "") -- Remove leading | and whitespace
             cell = mw.text.trim(cell)
             cell = mw.text.trim(cell)
             if not readingHeader then
             if not readingHeader then
Line 141: Line 155:
     end
     end


    -- Build colMap from headers (map header name to its column index)
     for i, h in ipairs(headers) do
     for i, h in ipairs(headers) do
         colMap[mw.text.trim(h)] = i
         colMap[mw.text.trim(h)] = i
Line 151: Line 166:
function p.iconize(frame)
function p.iconize(frame)
     local text = frame.args[1] or ""
     local text = frame.args[1] or ""
   
    -- Find all resource links [[Resource Name]] and add appropriate icons
     text = text:gsub("%[%[([^%]]+)%]%]", function(resourceName)
     text = text:gsub("%[%[([^%]]+)%]%]", function(resourceName)
         local icon = getResourceIcon(resourceName)
         local icon = getResourceIcon(resourceName)
         return icon .. " [[" .. resourceName .. "]]"
         return icon .. " [[" .. resourceName .. "]]"
     end)
     end)
   
     return text
     return text
end
end
Line 164: Line 182:
     end
     end
      
      
     -- Split recipe into components using semicolon as delimiter
     -- Split recipe into components (in case there are multiple)
     local components = mw.text.split(text, ";")
     local components = mw.text.split(text, ";")
     local formatted = {}
     local formatted = {}
Line 170: Line 188:
     for i, component in ipairs(components) do
     for i, component in ipairs(components) do
         component = mw.text.trim(component)
         component = mw.text.trim(component)
         -- Extract item name and quantity, allowing optional spaces around "x"
         -- Extract item name and quantity (e.g., "[[Salvaged Metal]] x 25")
         local itemName, quantity = component:match("%[%[([^%]]+)%]%]%s*x%s*(%d+)")
         local itemName, quantity = component:match("%[%[([^%]]+)%]%] x (%d+)")
       
         if itemName and quantity then
         if itemName and quantity then
             local icon = getResourceIcon(itemName)
             local icon = getResourceIcon(itemName)
             table.insert(formatted, icon .. " [[" .. itemName .. "]] x " .. quantity)
             table.insert(formatted, icon .. " [[" .. itemName .. "]] x " .. quantity)
         else
         else
            -- If pattern doesn't match, use as is
             table.insert(formatted, component)
             table.insert(formatted, component)
         end
         end
Line 192: Line 212:
     local colMap = buildingData.colMap
     local colMap = buildingData.colMap
     local rows = buildingData.rows
     local rows = buildingData.rows
   
    local nameCol = colMap["Name"]
    if not nameCol then
        return nil, "Error: 'Name' column not found in Building table"
    end
      
      
     for _, fields in ipairs(rows) do
     for _, fields in ipairs(rows) do
         local name = fields[colMap["Name"]]
         local name = fields[nameCol]
         if name == buildingName then
         if name == buildingName then
             return fields, colMap
             return fields, colMap
Line 236: Line 261:
     local quantity = recipe[colMap["Recipe to Smelt"]] or "1"
     local quantity = recipe[colMap["Recipe to Smelt"]] or "1"
      
      
    -- Add icons to resource names
     local formattedOutput = p.iconize({args = {[1] = output}})
     local formattedOutput = p.iconize({args = {[1] = output}})
     local formattedIngredient = p.iconize({args = {[1] = ingredient}})
     local formattedIngredient = p.iconize({args = {[1] = ingredient}})
      
      
    -- Return formatted row HTML directly
     return "<tr>" ..
     return "<tr>" ..
           "<td>" .. formattedOutput .. "</td>" ..
           "<td>" .. formattedOutput .. "</td>" ..
Line 250: Line 277:
function p.formatBuilding(frame)
function p.formatBuilding(frame)
     local buildingName = frame.args[1] or frame.args.name
     local buildingName = frame.args[1] or frame.args.name
   
     if not buildingName then
     if not buildingName then
         return "Error: No building name provided"
         return "Error: No building name provided"
     end
     end
      
      
    -- Get building data
     local building, colMap = p.getBuildingData(buildingName)
     local building, colMap = p.getBuildingData(buildingName)
   
     if not building then
     if not building then
         return "Error: Building '" .. buildingName .. "' not found"
         return "Error: Building '" .. buildingName .. "' not found"
     end
     end
      
      
    -- Map building data to template parameters
     local params = {
     local params = {
         Name = building[colMap["Name"]] or "",
         Name = building[colMap["Name"]] or "",
         Description = p.iconize({args = {[1] = building[colMap["Description"]] or ""}}),
         Description = p.iconize({args = {[1] = building[colMap["Description"]] or ""}}),
         JourneyRequirement = p.iconize({args = {[1] = building[colMap["Journey Requirement"]] or ""}}),
         JourneyRequirement = p.iconize({args = {[1] = building[colMap["Journey Requirement"]] or ""}}),
        ImageFile = building[colMap["Image File"]] or "",
         Health = building[colMap["Health"]] or "",
         Health = building[colMap["Health"]] or "",
         EnergyConsumption = building[colMap["Power Cost"]] or "",
         EnergyConsumption = building[colMap["Power Cost"]] or "",
Line 271: Line 303:
         PlacedWith = p.iconize({args = {[1] = building[colMap["Placed With"]] or ""}}),
         PlacedWith = p.iconize({args = {[1] = building[colMap["Placed With"]] or ""}}),
         AdditionalNotes = p.iconize({args = {[1] = building[colMap["Additional Notes"]] or ""}}),
         AdditionalNotes = p.iconize({args = {[1] = building[colMap["Additional Notes"]] or ""}}),
        ImageFile = building[colMap["Image File"]] or "",
         PrimarySource = "Crafting" -- Default value, adjust as needed
         PrimarySource = "Crafting"
     }
     }
      
      
    -- Generate formatted output for template
     local output = ""
     local output = ""
     for param, value in pairs(params) do
     for param, value in pairs(params) do
Line 288: Line 320:
function p.formatRefiningRecipes(frame)
function p.formatRefiningRecipes(frame)
     local buildingName = frame.args[1] or frame.args.name
     local buildingName = frame.args[1] or frame.args.name
   
     if not buildingName then
     if not buildingName then
         return "Error: No building name provided"
         return "Error: No building name provided"
     end
     end
      
      
    -- Get refining recipes
     local recipes, colMap = p.getRefiningRecipes(buildingName)
     local recipes, colMap = p.getRefiningRecipes(buildingName)
   
     if not recipes or #recipes == 0 then
     if not recipes or #recipes == 0 then
         return "<tr><td colspan=\"4\" style=\"text-align:center;\">No recipes found.</td></tr>"
         return "<tr><td colspan=\"4\" style=\"text-align:center;\">No recipes found.</td></tr>"
Line 308: Line 343:
function p.displayBuilding(frame)
function p.displayBuilding(frame)
     local buildingName = frame.args[1] or frame.args.name
     local buildingName = frame.args[1] or frame.args.name
   
     if not buildingName then
     if not buildingName then
         return "Error: No building name provided"
         return "Error: No building name provided"
     end
     end
      
      
    -- Get building data directly
     local buildingData, err = loadBuildingWikiTable()
     local buildingData, err = loadBuildingWikiTable()
     if not buildingData then
     if not buildingData then
Line 317: Line 354:
     end
     end
      
      
    local colMap = buildingData.colMap
     local rows = buildingData.rows
     local rows = buildingData.rows
   
    -- Find the building by name (column 2)
     local building = nil
     local building = nil
   
     for _, row in ipairs(rows) do
     for _, row in ipairs(rows) do
         if row[colMap["Name"]] == buildingName then
         if row[2] == buildingName then
             building = row
             building = row
             break
             break
Line 332: Line 369:
     end
     end
      
      
    -- Map table columns directly to template parameters using the exact column structure
    -- from the wiki table
     local args = {
     local args = {
         Name = building[colMap["Name"]] or "",
         ["Name"] = building[2] or "",               -- Column 2: Name
         Description = p.iconize({args = {[1] = building[colMap["Description"]] or ""}}),
         ["Description"] = building[3] or "",         -- Column 3: Description
         JourneyRequirement = p.iconize({args = {[1] = building[colMap["Journey Requirement"]] or ""}}),
         ["JourneyRequirement"] = building[4] or "", -- Column 4: Journey Requirement
         Health = building[colMap["Health"]] or "",
         ["Health"] = building[5] or "",             -- Column 5: Health
         EnergyConsumption = building[colMap["Power Cost"]] or "",
         ["EnergyConsumption"] = building[6] or "",   -- Column 6: Power Cost
         GeneratesPower = building[colMap["Generates Power"]] or "",
         ["GeneratesPower"] = building[7] or "",     -- Column 7: Generates Power
         StorageSlots = building[colMap["Storage Slots"]] or "",
         ["StorageSlots"] = building[8] or "",       -- Column 8: Storage Slots
         StorageVolume = building[colMap["Storage Capacity"]] or "",
         ["StorageVolume"] = building[9] or "",       -- Column 9: Storage Capacity
         Components = p.formatComponent(building[colMap["Recipe to Build"]] or ""),
         ["Components"] = building[10] or "",         -- Column 10: Recipe to Build
         PlacedWith = p.iconize({args = {[1] = building[colMap["Placed With"]] or ""}}),
         ["PlacedWith"] = building[11] or "",         -- Column 11: Placed With
         AdditionalNotes = p.iconize({args = {[1] = building[colMap["Additional Notes"]] or ""}}),
         ["AdditionalNotes"] = building[12] or "",   -- Column 12: Additional Notes
         ImageFile = building[colMap["Image File"]] or "",
         ["ImageFile"] = building[14] or "",         -- Column 14: Image File
         PrimarySource = "Crafting"
         ["PrimarySource"] = "Crafting"               -- Default value
     }
     }
      
      
    -- Process recipe components
    if args["Components"] and args["Components"] ~= "" then
        args["Components"] = p.formatComponent(args["Components"])
    end
   
    -- Process descriptions and journey requirements with iconize
    if args["Description"] and args["Description"] ~= "" then
        args["Description"] = p.iconize({args = {[1] = args["Description"]}})
    end
   
    if args["JourneyRequirement"] and args["JourneyRequirement"] ~= "" then
        args["JourneyRequirement"] = p.iconize({args = {[1] = args["JourneyRequirement"]}})
    end
   
    if args["PlacedWith"] and args["PlacedWith"] ~= "" then
        args["PlacedWith"] = p.iconize({args = {[1] = args["PlacedWith"]}})
    end
   
    -- For debugging - uncomment to see raw data
    -- local debug = "Building raw data:\n"
    -- for i, v in ipairs(building) do
    --    debug = debug .. "Column " .. i .. ": " .. tostring(v) .. "\n"
    -- end
    -- return debug
   
    -- Expand the template with our parameters
     return frame:expandTemplate{ title = 'BuildingRefinerDisplay', args = args }
     return frame:expandTemplate{ title = 'BuildingRefinerDisplay', args = args }
end
end
Line 354: Line 419:
function p.debugBuilding(frame)
function p.debugBuilding(frame)
     local buildingName = frame.args[1] or frame.args.name
     local buildingName = frame.args[1] or frame.args.name
   
     if not buildingName then
     if not buildingName then
         return "Error: No building name provided"
         return "Error: No building name provided"
     end
     end
      
      
    -- Get building data directly
     local buildingData, err = loadBuildingWikiTable()
     local buildingData, err = loadBuildingWikiTable()
     if not buildingData then
     if not buildingData then
Line 365: Line 432:
     local colMap = buildingData.colMap
     local colMap = buildingData.colMap
     local rows = buildingData.rows
     local rows = buildingData.rows
   
    -- Find the building
     local building = nil
     local building = nil
     for _, row in ipairs(rows) do
     for _, row in ipairs(rows) do
Line 377: Line 446:
     end
     end
      
      
    -- Output all data for debugging
     local debug = "Building: " .. buildingName .. "\n\n"
     local debug = "Building: " .. buildingName .. "\n\n"
     for k, v in pairs(colMap) do
     for k, v in pairs(colMap) do
Line 393: Line 463:
      
      
     local colMap = buildingData.colMap
     local colMap = buildingData.colMap
   
    -- Show all column mappings
     local output = "Column mappings:\n"
     local output = "Column mappings:\n"
     for name, index in pairs(colMap) do
     for name, index in pairs(colMap) do
Line 410: Line 482:
function p.debugTemplateParams(frame)
function p.debugTemplateParams(frame)
     local buildingName = frame.args[1] or frame.args.name
     local buildingName = frame.args[1] or frame.args.name
   
     if not buildingName then
     if not buildingName then
         return "Error: No building name provided"
         return "Error: No building name provided"
     end
     end
      
      
    -- Get building data directly
     local buildingData, err = loadBuildingWikiTable()
     local buildingData, err = loadBuildingWikiTable()
     if not buildingData then
     if not buildingData then
Line 421: Line 495:
     local colMap = buildingData.colMap
     local colMap = buildingData.colMap
     local rows = buildingData.rows
     local rows = buildingData.rows
   
    -- Find the building
     local building = nil
     local building = nil
     for _, row in ipairs(rows) do
     for _, row in ipairs(rows) do
Line 433: Line 509:
     end
     end
      
      
    -- Map building data to template parameters
     local args = {
     local args = {
         Name = building[colMap["Name"]] or "",
         ["Name"] = building[colMap["Name"]] or "",
         Description = building[colMap["Description"]] or "",
         ["Description"] = building[colMap["Description"]] or "",
         JourneyRequirement = building[colMap["Journey Requirement"]] or "",
         ["JourneyRequirement"] = building[colMap["Journey Requirement"]] or "",
         ImageFile = building[colMap["Image File"]] or "",
         ["ImageFile"] = building[colMap["Image File"]] or "",
         Health = building[colMap["Health"]] or "",
         ["Health"] = building[colMap["Health"]] or "",
         EnergyConsumption = building[colMap["Power Cost"]] or "",
         ["EnergyConsumption"] = building[colMap["Power Cost"]] or "",
         GeneratesPower = building[colMap["Generates Power"]] or "",
         ["GeneratesPower"] = building[colMap["Generates Power"]] or "",
         StorageSlots = building[colMap["Storage Slots"]] or "",
         ["StorageSlots"] = building[colMap["Storage Slots"]] or "",
         StorageVolume = building[colMap["Storage Capacity"]] or "",
         ["StorageVolume"] = building[colMap["Storage Capacity"]] or "",
         Components = building[colMap["Recipe to Build"]] or "",
         ["Components"] = building[colMap["Recipe to Build"]] or "",
         PlacedWith = building[colMap["Placed With"]] or "",
         ["PlacedWith"] = building[colMap["Placed With"]] or "",
         AdditionalNotes = building[colMap["Additional Notes"]] or "",
         ["AdditionalNotes"] = building[colMap["Additional Notes"]] or "",
         PrimarySource = "Crafting"
         ["PrimarySource"] = "Crafting" -- Default value, adjust as needed
     }
     }
      
      
    -- Output the template parameters
     local result = "Template Parameters:\n"
     local result = "Template Parameters:\n"
     for param, value in pairs(args) do
     for param, value in pairs(args) do

Revision as of 13:01, 16 March 2025

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

-- Module:DataTableParser
-- Handles display and formatting of building data for Templates
-- Updated to parse wiki tables directly instead of using Cargo

local p = {}

-- Function to generate icon file reference for a resource
local function getResourceIcon(resourceName)
    if not resourceName or resourceName == "" then
        return ""
    end
    
    -- Convert spaces to underscores for file name
    local fileName = resourceName:gsub("%s+", "_") .. "_-_Icon.png"
    -- Return file reference with appropriate size
    return "[[File:" .. fileName .. "|20px]]"
end

----------------------------------------------------------------------
-- Helper function to load/parse the wiki table page for buildings
----------------------------------------------------------------------
local function loadBuildingWikiTable()
    local title = "Data:Building"  -- The page containing your building table
    local content = mw.title.new(title):getContent()
    if not content then
        return nil, "Error: Building wiki table page not found!"
    end

    local lines = mw.text.split(content, "\n")
    -- Filter out magic words, noindex tags, or blank lines
    local filtered = {}
    for _, line in ipairs(lines) do
        line = mw.text.trim(line)
        if line ~= "" and not line:match("^__") and not line:match("<noindex>") and not line:match("</noindex>") then
            table.insert(filtered, line)
        end
    end
    if #filtered < 2 then
        return nil, "Error: No valid wiki table content found!"
    end

    local inTable = false
    local headers = {}  -- store the table headers
    local colMap = {}  -- map header name to column index
    local data = {}     -- array of row arrays
    local currentRow = {}

    local readingHeader = true  -- before the first row separator ("|-"), treat cells as headers

    for _, line in ipairs(filtered) do
        if line:match("^{|") then
            -- Start of table (line starts with "{|")
            inTable = true
        elseif line:match("^|%-") then
            -- Row separator ("|-")
            if not readingHeader and #currentRow > 0 then
                table.insert(data, currentRow)
            end
            currentRow = {}
            readingHeader = false  -- after the first row separator, subsequent cells are row cells
        elseif line:match("^!") then
            -- Header cell (starts with "!")
            local header = line:gsub("^!+%s*", "")  -- Remove leading ! and whitespace
            header = mw.text.trim(header)
            table.insert(headers, header)
        elseif line:match("^|%+") then
            -- Table caption line ("|+"), skip it
        elseif line:match("^|}") then
            -- End of table ("|}")
            if not readingHeader and #currentRow > 0 then
                table.insert(data, currentRow)
            end
            inTable = false
        elseif inTable and line:match("^|[^%-+}]") then
            -- Process table cell lines (lines that start with "|" but not "|+", "|-", or "|}")
            local cell = line:gsub("^|+%s*", "")  -- Remove leading | and whitespace
            cell = mw.text.trim(cell)
            if not readingHeader then
                table.insert(currentRow, cell)
            end
        end
    end

    -- Build colMap from headers (map header name to its column index)
    for i, h in ipairs(headers) do
        colMap[mw.text.trim(h)] = i
    end

    return { rows = data, colMap = colMap }, nil
end

----------------------------------------------------------------------
-- Helper function to load/parse the wiki table page for refining data
----------------------------------------------------------------------
local function loadRefiningWikiTable()
    local title = "Data:Refining"  -- The page containing your refining table
    local content = mw.title.new(title):getContent()
    if not content then
        return nil, "Error: Refining wiki table page not found!"
    end

    local lines = mw.text.split(content, "\n")
    -- Filter out magic words, noindex tags, or blank lines
    local filtered = {}
    for _, line in ipairs(lines) do
        line = mw.text.trim(line)
        if line ~= "" and not line:match("^__") and not line:match("<noindex>") and not line:match("</noindex>") then
            table.insert(filtered, line)
        end
    end
    if #filtered < 2 then
        return nil, "Error: No valid wiki table content found!"
    end

    local inTable = false
    local headers = {}  -- store the table headers
    local colMap = {}  -- map header name to column index
    local data = {}     -- array of row arrays
    local currentRow = {}

    local readingHeader = true  -- before the first row separator ("|-"), treat cells as headers

    for _, line in ipairs(filtered) do
        if line:match("^{|") then
            -- Start of table (line starts with "{|")
            inTable = true
        elseif line:match("^|%-") then
            -- Row separator ("|-")
            if not readingHeader and #currentRow > 0 then
                table.insert(data, currentRow)
            end
            currentRow = {}
            readingHeader = false  -- after the first row separator, subsequent cells are row cells
        elseif line:match("^!") then
            -- Header cell (starts with "!")
            local header = line:gsub("^!+%s*", "")  -- Remove leading ! and whitespace
            header = mw.text.trim(header)
            table.insert(headers, header)
        elseif line:match("^|%+") then
            -- Table caption line ("|+"), skip it
        elseif line:match("^|}") then
            -- End of table ("|}")
            if not readingHeader and #currentRow > 0 then
                table.insert(data, currentRow)
            end
            inTable = false
        elseif inTable and line:match("^|[^%-+}]") then
            -- Process table cell lines (lines that start with "|" but not "|+", "|-", or "|}")
            local cell = line:gsub("^|+%s*", "")  -- Remove leading | and whitespace
            cell = mw.text.trim(cell)
            if not readingHeader then
                table.insert(currentRow, cell)
            end
        end
    end

    -- Build colMap from headers (map header name to its column index)
    for i, h in ipairs(headers) do
        colMap[mw.text.trim(h)] = i
    end

    return { rows = data, colMap = colMap }, nil
end

-- Function to add icons to resource links in text
function p.iconize(frame)
    local text = frame.args[1] or ""
    
    -- Find all resource links [[Resource Name]] and add appropriate icons
    text = text:gsub("%[%[([^%]]+)%]%]", function(resourceName)
        local icon = getResourceIcon(resourceName)
        return icon .. " [[" .. resourceName .. "]]"
    end)
    
    return text
end

-- Function to format a recipe component
function p.formatComponent(text)
    if not text or text == "" then
        return ""
    end
    
    -- Split recipe into components (in case there are multiple)
    local components = mw.text.split(text, ";")
    local formatted = {}
    
    for i, component in ipairs(components) do
        component = mw.text.trim(component)
        -- Extract item name and quantity (e.g., "[[Salvaged Metal]] x 25")
        local itemName, quantity = component:match("%[%[([^%]]+)%]%] x (%d+)")
        
        if itemName and quantity then
            local icon = getResourceIcon(itemName)
            table.insert(formatted, icon .. " [[" .. itemName .. "]] x " .. quantity)
        else
            -- If pattern doesn't match, use as is
            table.insert(formatted, component)
        end
    end
    
    return table.concat(formatted, "<br>")
end

-- Function to get building data by name
function p.getBuildingData(buildingName)
    local buildingData, err = loadBuildingWikiTable()
    if not buildingData then
        return nil, err
    end
    
    local colMap = buildingData.colMap
    local rows = buildingData.rows
    
    local nameCol = colMap["Name"]
    if not nameCol then
        return nil, "Error: 'Name' column not found in Building table"
    end
    
    for _, fields in ipairs(rows) do
        local name = fields[nameCol]
        if name == buildingName then
            return fields, colMap
        end
    end
    
    return nil, "Building '" .. buildingName .. "' not found"
end

-- Function to get refining recipes by refiner name
function p.getRefiningRecipes(refinerName)
    local refiningData, err = loadRefiningWikiTable()
    if not refiningData then
        return nil, err
    end
    
    local colMap = refiningData.colMap
    local rows = refiningData.rows
    
    local refinerCol = colMap["Refiner Needed"]
    if not refinerCol then
        return nil, "Error: 'Refiner Needed' column not found in Refining table"
    end
    
    local recipes = {}
    for _, fields in ipairs(rows) do
        local refiner = fields[refinerCol]
        if refiner == refinerName then
            table.insert(recipes, fields)
        end
    end
    
    return recipes, colMap
end

-- Function to format a refining recipe row
function p.formatRecipeRow(recipe, colMap)
    local output = recipe[colMap["Name"]] or ""
    local ingredient = recipe[colMap["Ingredients to Smelt"]] or ""
    local time = recipe[colMap["Time to Smelt"]] or ""
    local quantity = recipe[colMap["Recipe to Smelt"]] or "1"
    
    -- Add icons to resource names
    local formattedOutput = p.iconize({args = {[1] = output}})
    local formattedIngredient = p.iconize({args = {[1] = ingredient}})
    
    -- Return formatted row HTML directly
    return "<tr>" ..
           "<td>" .. formattedOutput .. "</td>" ..
           "<td>" .. formattedIngredient .. "</td>" ..
           "<td style=\"text-align:center;\">" .. time .. "</td>" ..
           "<td style=\"text-align:center;\">" .. quantity .. "</td>" ..
           "</tr>"
end

-- Main function to format building data for the template
function p.formatBuilding(frame)
    local buildingName = frame.args[1] or frame.args.name
    
    if not buildingName then
        return "Error: No building name provided"
    end
    
    -- Get building data
    local building, colMap = p.getBuildingData(buildingName)
    
    if not building then
        return "Error: Building '" .. buildingName .. "' not found"
    end
    
    -- Map building data to template parameters
    local params = {
        Name = building[colMap["Name"]] or "",
        Description = p.iconize({args = {[1] = building[colMap["Description"]] or ""}}),
        JourneyRequirement = p.iconize({args = {[1] = building[colMap["Journey Requirement"]] or ""}}),
        ImageFile = building[colMap["Image File"]] or "",
        Health = building[colMap["Health"]] or "",
        EnergyConsumption = building[colMap["Power Cost"]] or "",
        GeneratesPower = building[colMap["Generates Power"]] or "",
        StorageSlots = building[colMap["Storage Slots"]] or "",
        StorageVolume = building[colMap["Storage Capacity"]] or "",
        Components = p.formatComponent(building[colMap["Recipe to Build"]] or ""),
        PlacedWith = p.iconize({args = {[1] = building[colMap["Placed With"]] or ""}}),
        AdditionalNotes = p.iconize({args = {[1] = building[colMap["Additional Notes"]] or ""}}),
        PrimarySource = "Crafting" -- Default value, adjust as needed
    }
    
    -- Generate formatted output for template
    local output = ""
    for param, value in pairs(params) do
        if value and value ~= "" then
            output = output .. "|" .. param .. "=" .. value .. "\n"
        end
    end
    
    return output
end

-- Function to format all refining recipes for a building
function p.formatRefiningRecipes(frame)
    local buildingName = frame.args[1] or frame.args.name
    
    if not buildingName then
        return "Error: No building name provided"
    end
    
    -- Get refining recipes
    local recipes, colMap = p.getRefiningRecipes(buildingName)
    
    if not recipes or #recipes == 0 then
        return "<tr><td colspan=\"4\" style=\"text-align:center;\">No recipes found.</td></tr>"
    end
    
    local output = ""
    for _, recipe in ipairs(recipes) do
        output = output .. p.formatRecipeRow(recipe, colMap)
    end
    
    return output
end

-- Helper function to display building with the template
function p.displayBuilding(frame)
    local buildingName = frame.args[1] or frame.args.name
    
    if not buildingName then
        return "Error: No building name provided"
    end
    
    -- Get building data directly
    local buildingData, err = loadBuildingWikiTable()
    if not buildingData then
        return err
    end
    
    local rows = buildingData.rows
    
    -- Find the building by name (column 2)
    local building = nil
    for _, row in ipairs(rows) do
        if row[2] == buildingName then
            building = row
            break
        end
    end
    
    if not building then
        return "Error: Building '" .. buildingName .. "' not found"
    end
    
    -- Map table columns directly to template parameters using the exact column structure
    -- from the wiki table
    local args = {
        ["Name"] = building[2] or "",                -- Column 2: Name
        ["Description"] = building[3] or "",         -- Column 3: Description
        ["JourneyRequirement"] = building[4] or "",  -- Column 4: Journey Requirement
        ["Health"] = building[5] or "",              -- Column 5: Health
        ["EnergyConsumption"] = building[6] or "",   -- Column 6: Power Cost
        ["GeneratesPower"] = building[7] or "",      -- Column 7: Generates Power
        ["StorageSlots"] = building[8] or "",        -- Column 8: Storage Slots
        ["StorageVolume"] = building[9] or "",       -- Column 9: Storage Capacity
        ["Components"] = building[10] or "",         -- Column 10: Recipe to Build
        ["PlacedWith"] = building[11] or "",         -- Column 11: Placed With
        ["AdditionalNotes"] = building[12] or "",    -- Column 12: Additional Notes
        ["ImageFile"] = building[14] or "",          -- Column 14: Image File
        ["PrimarySource"] = "Crafting"               -- Default value
    }
    
    -- Process recipe components
    if args["Components"] and args["Components"] ~= "" then
        args["Components"] = p.formatComponent(args["Components"])
    end
    
    -- Process descriptions and journey requirements with iconize
    if args["Description"] and args["Description"] ~= "" then
        args["Description"] = p.iconize({args = {[1] = args["Description"]}})
    end
    
    if args["JourneyRequirement"] and args["JourneyRequirement"] ~= "" then
        args["JourneyRequirement"] = p.iconize({args = {[1] = args["JourneyRequirement"]}})
    end
    
    if args["PlacedWith"] and args["PlacedWith"] ~= "" then
        args["PlacedWith"] = p.iconize({args = {[1] = args["PlacedWith"]}})
    end
    
    -- For debugging - uncomment to see raw data
    -- local debug = "Building raw data:\n"
    -- for i, v in ipairs(building) do
    --     debug = debug .. "Column " .. i .. ": " .. tostring(v) .. "\n"
    -- end
    -- return debug
    
    -- Expand the template with our parameters
    return frame:expandTemplate{ title = 'BuildingRefinerDisplay', args = args }
end

-- Debug function to help troubleshoot
function p.debugBuilding(frame)
    local buildingName = frame.args[1] or frame.args.name
    
    if not buildingName then
        return "Error: No building name provided"
    end
    
    -- Get building data directly
    local buildingData, err = loadBuildingWikiTable()
    if not buildingData then
        return err
    end
    
    local colMap = buildingData.colMap
    local rows = buildingData.rows
    
    -- Find the building
    local building = nil
    for _, row in ipairs(rows) do
        if row[colMap["Name"]] == buildingName then
            building = row
            break
        end
    end
    
    if not building then
        return "Error: Building '" .. buildingName .. "' not found"
    end
    
    -- Output all data for debugging
    local debug = "Building: " .. buildingName .. "\n\n"
    for k, v in pairs(colMap) do
        debug = debug .. k .. ": " .. tostring(building[v] or "nil") .. "\n"
    end
    
    return debug
end

-- Debug function to show raw table data
function p.debugRawData(frame)
    local buildingData, err = loadBuildingWikiTable()
    if not buildingData then
        return err
    end
    
    local colMap = buildingData.colMap
    
    -- Show all column mappings
    local output = "Column mappings:\n"
    for name, index in pairs(colMap) do
        output = output .. name .. " -> " .. index .. "\n"
    end
    
    output = output .. "\n\nFirst row raw data:\n"
    local row = buildingData.rows[1]
    for i = 1, #row do
        output = output .. "Column " .. i .. ": " .. tostring(row[i]) .. "\n"
    end
    
    return output
end

-- Debug function to show template parameters
function p.debugTemplateParams(frame)
    local buildingName = frame.args[1] or frame.args.name
    
    if not buildingName then
        return "Error: No building name provided"
    end
    
    -- Get building data directly
    local buildingData, err = loadBuildingWikiTable()
    if not buildingData then
        return err
    end
    
    local colMap = buildingData.colMap
    local rows = buildingData.rows
    
    -- Find the building
    local building = nil
    for _, row in ipairs(rows) do
        if row[colMap["Name"]] == buildingName then
            building = row
            break
        end
    end
    
    if not building then
        return "Error: Building '" .. buildingName .. "' not found"
    end
    
    -- Map building data to template parameters
    local args = {
        ["Name"] = building[colMap["Name"]] or "",
        ["Description"] = building[colMap["Description"]] or "",
        ["JourneyRequirement"] = building[colMap["Journey Requirement"]] or "",
        ["ImageFile"] = building[colMap["Image File"]] or "",
        ["Health"] = building[colMap["Health"]] or "",
        ["EnergyConsumption"] = building[colMap["Power Cost"]] or "",
        ["GeneratesPower"] = building[colMap["Generates Power"]] or "",
        ["StorageSlots"] = building[colMap["Storage Slots"]] or "",
        ["StorageVolume"] = building[colMap["Storage Capacity"]] or "",
        ["Components"] = building[colMap["Recipe to Build"]] or "",
        ["PlacedWith"] = building[colMap["Placed With"]] or "",
        ["AdditionalNotes"] = building[colMap["Additional Notes"]] or "",
        ["PrimarySource"] = "Crafting" -- Default value, adjust as needed
    }
    
    -- Output the template parameters
    local result = "Template Parameters:\n"
    for param, value in pairs(args) do
        result = result .. param .. " = " .. tostring(value) .. "\n"
    end
    
    return result
end

return p