Module:GladeResourcesController: Difference between revisions

From Against the Storm Official Wiki
Now lists are sorted alphabetically by deposit name, and biomes are sorted LtR alphabetically too
Remove unneeded suppressTrees from render view functions
 
(11 intermediate revisions by 2 users not shown)
Line 51: Line 51:
local VIEW_ARG_BIOME_PREFIX = "biome"
local VIEW_ARG_BIOME_PREFIX = "biome"


local VIEW_ARG_FORCE_DEFAULT = "default"
--endregion
--endregion


Line 57: Line 58:
--region Localization string constants
--region Localization string constants


local ERROR_MESSAGE_PRODUCT_AND_BONUS_BUT_NOT_MATCH = "When searching for a good as both a main and bonus resource, the parameters must match."
local ERROR_MESSAGE_PRODUCT_AND_BONUS_BUT_NOT_MATCH = "When searching for a good as both a main and bonus resource, the parameters must match"
local ERROR_MESSAGE_NO_PARAMETERS_GIVEN = "You must specify the name of a product, a bonus resource, or a building—and as it's spelled in the game."
local ERROR_MESSAGE_NO_PARAMETERS_GIVEN = "You must specify the name of a product, a bonus resource, or a building—and as it's spelled in the game"


--endregion
--endregion
Line 95: Line 96:
---@param buildingID string a building's ID
---@param buildingID string a building's ID
---@return table array of deposit records retrieved from GladeResourcesData
---@return table array of deposit records retrieved from GladeResourcesData
local function assembleDepositsFromBuilding(buildingID)
local function assembleDepositsFromBuilding(buildingID, suppressTrees)


     local depositsList = {}
     local depositsList = {}
     --This isn't as expensive as it looks, because typically only one data model will return something. However, we don't know which it will be in advance.
     local isWoodcutter = buildingID == "Woodcutters Camp"
    for _, dataModel in ipairs(DataModelsWithGladeResourcesRecipes) do
   
    if isWoodcutter then
        local newList = GladeResourcesData.getAllDepositsWhereDepositIsTree()
        for _, deposit in ipairs(newList) do
            table.insert(depositsList, deposit)
        end
    else
    --This isn't as expensive as it looks, because typically only one data model will return something. However, we don't know which it will be in advance.
    for _, dataModel in ipairs(DataModelsWithGladeResourcesRecipes) do
        local recipeDataArray = dataModel:getIDsAndRecipesWhereBuildingID(buildingID)
        if #recipeDataArray > 0 then


        local recipeDataArray = dataModel:getIDsAndRecipesWhereBuildingID(buildingID)
            local products = {}
        if #recipeDataArray > 0 then
            for _, recipeData in ipairs(recipeDataArray) do
 
                local id = dataModel:getRecipeProductID(recipeData)
            local products = {}
                products[id] = dataModel:getRecipeGrade(recipeData)
            for _, recipeData in ipairs(recipeDataArray) do
            end
                local id = dataModel:getRecipeProductID(recipeData)
            for id, grade in pairs(products) do
                products[id] = dataModel:getRecipeGrade(recipeData)
                local newList = GladeResourcesData.getAllDepositsWherePrimaryResourceAndGrade(id, grade, suppressTrees)
            end
                for _, deposit in ipairs(newList) do
            for id, grade in pairs(products) do
                    table.insert(depositsList, deposit)
                local newList = GladeResourcesData.getAllDepositsWherePrimaryResourceAndGrade(id, grade)
                end
                for _, deposit in ipairs(newList) do
            end
                    table.insert(depositsList, deposit)
        end
                end
    end
            end
        end
     end
     end


Line 131: Line 141:


     local productID = GladeResourcesData.getDepositPrimaryProduct(deposit)
     local productID = GladeResourcesData.getDepositPrimaryProduct(deposit)
    local isTree = GladeResourcesData.isDepositTree(deposit)


     --This isn't as expensive as it looks, because typically only one data model will return something. However, we don't know which it will be in advance.
     --This isn't as expensive as it looks, because typically only one data model will return something. However, we don't know which it will be in advance.
Line 137: Line 148:
         local recipeDataArray = dataModel:getIDsAndRecipesWhereProductID(productID)
         local recipeDataArray = dataModel:getIDsAndRecipesWhereProductID(productID)
         if #recipeDataArray > 0 then
         if #recipeDataArray > 0 then
             --Just return the first one.
             for i = 1, #recipeDataArray do
            local id = dataModel:getRecipeBuildingID(recipeDataArray[1])
            local id = dataModel:getRecipeBuildingID(recipeDataArray[i])
            local name = dataModel:getName(id)
            if (isTree or dataModel:getName(id) ~= "Woodcutters' Camp") then
            local icon = dataModel:getIcon(id)
            local name = dataModel:getName(id)
            return name, icon
            local icon = dataModel:getIcon(id)
            return name, icon
            end
            end
         end
         end
     end
     end
Line 291: Line 305:
     --Special case where we swap the title out for the name of the one deposit and show the description
     --Special case where we swap the title out for the name of the one deposit and show the description
     local description = ""
     local description = ""
     if numDeposits == 1 then
     if numDeposits == 1 and not displayOverride == VIEW_ARG_FORCE_DEFAULT then
         title = GladeResourcesData.getDepositName(depositsList[1])
         title = GladeResourcesData.getDepositName(depositsList[1])
         description = writeDescriptionForDeposits(depositsList)
         description = writeDescriptionForDeposits(depositsList)
Line 385: Line 399:
         title = VIEW_RESOURCE_LINK,
         title = VIEW_RESOURCE_LINK,
         args = {
         args = {
            ["name"] = productName,
            ["iconfilename"] = iconFilename,
             ["iconsize"] = "medium",
             ["iconsize"] = "medium",
            ["iconfilename"] = iconFilename,
            ["name"] = productName,
             ["display"] = "notext",
             ["display"] = "notext",
         }
         }
Line 415: Line 429:
             ["name"] = buildingName,
             ["name"] = buildingName,
             ["iconfilename"] = buildingIcon,
             ["iconfilename"] = buildingIcon,
            ["iconsize"] = "large",
             ["display"] = "notext"
             ["display"] = "notext"
         }
         }
Line 428: Line 443:
---@param buildingID string
---@param buildingID string
---@return string wiki markup
---@return string wiki markup
local function renderLargeBuildingLink(buildingID, buildingName, buildingIcon)
local function renderBottomBuildingLink(buildingID, buildingName, buildingIcon)


     if buildingID then
     if buildingID then
Line 440: Line 455:
             ["name"] = buildingName,
             ["name"] = buildingName,
             ["iconfilename"] = buildingIcon,
             ["iconfilename"] = buildingIcon,
             ["iconsize"] = "large"
             ["iconsize"] = "huge"
         }
         }
     }
     }
Line 546: Line 561:


     displayOverride = displayOverride --TODO write the other display modes
     displayOverride = displayOverride --TODO write the other display modes
    local numDeposits = #depositsList


     local buildingLink = ""
     local buildingLink = ""
     if buildingID then
 
        buildingLink = renderLargeBuildingLink(buildingID)
     if numDeposits > 0 then
    else
        if buildingID then
        local buildingName, buildingIcon = checkOnlyOneBuildingForAllDeposits(depositsList)
            buildingLink = renderBottomBuildingLink(buildingID)
        if buildingName and buildingIcon then
        else
            buildingLink = renderLargeBuildingLink(nil, buildingName, buildingIcon)
            local buildingName, buildingIcon = checkOnlyOneBuildingForAllDeposits(depositsList)
            if buildingName and buildingIcon then
                buildingLink = renderBottomBuildingLink(nil, buildingName, buildingIcon)
            end
         end
         end
     end
     end
Line 561: Line 581:
         title = VIEW_TEMPLATE_END,
         title = VIEW_TEMPLATE_END,
         args = {
         args = {
            ["numdeposits"] = numDeposits,
             ["building"] = buildingLink,
             ["building"] = buildingLink,
         },
         },
Line 585: Line 606:
     local skipFlagForBiomes = frame.args[VIEW_ARG_SKIP_BIOMES]
     local skipFlagForBiomes = frame.args[VIEW_ARG_SKIP_BIOMES]
     local displayOverride = frame.args.display
     local displayOverride = frame.args.display
    local suppressTrees = true


     biomesIDs = sortArrayWith(BiomesData.getAllBiomeIDs(), BiomesData.getName)
     biomesIDs = sortArrayWith(BiomesData.getAllBiomeIDs(), BiomesData.getName)
Line 598: Line 620:
     end
     end
     local buildingID = BuildingDataProxy.getID(buildingName)
     local buildingID = BuildingDataProxy.getID(buildingName)
--Find out whether Trees should be excluded
if (not buildingName) or buildingName == "" or buildingName == "Woodcutters' Camp" then
suppressTrees = false
end


     local depositsList = {}
     local depositsList = {}
Line 603: Line 630:
     if productID and bonusID then
     if productID and bonusID then
         if productID ~= bonusID then
         if productID ~= bonusID then
             return ERROR_MESSAGE_PRODUCT_AND_BONUS_BUT_NOT_MATCH
             error(ERROR_MESSAGE_PRODUCT_AND_BONUS_BUT_NOT_MATCH)
         else
         else
             depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWherePrimaryOrBonusResource(productID), GladeResourcesData.getDepositName)
             depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWherePrimaryOrBonusResource(productID, suppressTrees), GladeResourcesData.getDepositName)
         end
         end
         title = #depositsList .. " Deposits for " .. productName .. " or as a bonus."
         title = #depositsList .. " Deposits for " .. productName .. " or as a bonus."


     elseif productID then
     elseif productID then
         depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWherePrimaryResource(productID), GladeResourcesData.getDepositName)
         depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWherePrimaryResource(productID, suppressTrees), GladeResourcesData.getDepositName)
         title = #depositsList .. " Deposits for " .. productName .. "."
         title = #depositsList .. " Deposits for " .. productName .. "."


     elseif bonusID then
     elseif bonusID then
         depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWhereBonusResource(bonusID), GladeResourcesData.getDepositName)
         depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWhereBonusResource(bonusID, suppressTrees), GladeResourcesData.getDepositName)
         title = #depositsList .. " Deposits where " .. bonusName .. " is a bonus."
         title = #depositsList .. " Deposits where " .. bonusName .. " is a bonus."


     elseif buildingID then
     elseif buildingID then
         depositsList = sortArrayWith(assembleDepositsFromBuilding(buildingID), GladeResourcesData.getDepositName)
         depositsList = sortArrayWith(assembleDepositsFromBuilding(buildingID, suppressTrees), GladeResourcesData.getDepositName)
         title = #depositsList .. " Deposits exploited by the " .. buildingName .. "."
         title = #depositsList .. " Deposits exploited by the " .. buildingName .. "."


     else
     else
         return ERROR_MESSAGE_NO_PARAMETERS_GIVEN
         error(ERROR_MESSAGE_NO_PARAMETERS_GIVEN)
     end
     end



Latest revision as of 02:12, 9 August 2025

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

---@class GladeResourcesController
local GladeResourcesController = {}



--region Dependencies

local GladeResourcesData = require("Module:GladeResourcesData")
local GoodsData = require("Module:GoodsData")
local BiomesData = require("Module:BiomesData")
local BuildingDataProxy = require("Module:BuildingDataProxy")
local DataModelsWithGladeResourcesRecipes = {
    require("Module:MinesData"),
    require("Module:CampsData"),
    require("Module:FishingData"),
    require("Module:GatheringData"),
}

local VIEW_TEMPLATE_START = "Deposit/view"
local VIEW_TEMPLATE_ROW = "Deposit/view/row"
local VIEW_TEMPLATE_END = "Deposit/view/end"

local VIEW_BUILDING_LINK = "Building_link/view"
local VIEW_RESOURCE_LINK = "Resource_link/view"

--endregion



--region Private constants

local VIEW_ARG_HEAD = "heading"
local VIEW_ARG_DESC = "description"
local VIEW_ARG_CAPTION = "caption"
local VIEW_ARG_CHARGES = "charges"
local VIEW_ARG_NUM_DEPOSITS = "numdeposits"
local VIEW_ARG_MAX_BONUSES = "maxbonuses"

local VIEW_ARG_DEPOSIT = "deposit"
local VIEW_ARG_BUILDING = "building"

local VIEW_ARG_PRODUCT = "primary"
local VIEW_ARG_BONUS_1 = "bonus1"
local VIEW_ARG_BONUS_2 = "bonus2"
local VIEW_ARG_BONUS_3 = "bonus3"
local VIEW_ARG_BONUS_4 = "bonus4"

local VIEW_ARG_SKIP_BIOMES = "skip_biomes"
local SKIP_FLAG = "skip"

local VIEW_ARG_BIOME_PREFIX = "biome"

local VIEW_ARG_FORCE_DEFAULT = "default"
--endregion



--region Localization string constants

local ERROR_MESSAGE_PRODUCT_AND_BONUS_BUT_NOT_MATCH = "When searching for a good as both a main and bonus resource, the parameters must match"
local ERROR_MESSAGE_NO_PARAMETERS_GIVEN = "You must specify the name of a product, a bonus resource, or a building—and as it's spelled in the game"

--endregion



--region Private member variables

local frame

local biomesIDs

--endregion



--region Private methods

---@private
---Sorts given the function provided.
---
---@param arrayToSort table
---@param comparisonFunction function
---@return table the sorted array
local function sortArrayWith(arrayToSort, comparisonFunction)
    table.sort(arrayToSort, function(a, b)
        return comparisonFunction(a) < comparisonFunction(b)
    end)
    return arrayToSort
end


---@private
---Uses the specified building ID to go through applicable data models, assembling a list of possible deposits, filtered by the grade of the building.
---
---@param buildingID string a building's ID
---@return table array of deposit records retrieved from GladeResourcesData
local function assembleDepositsFromBuilding(buildingID, suppressTrees)

    local depositsList = {}
    local isWoodcutter = buildingID == "Woodcutters Camp"
    
    if isWoodcutter then
        local newList = GladeResourcesData.getAllDepositsWhereDepositIsTree()
        for _, deposit in ipairs(newList) do
            table.insert(depositsList, deposit)
        end
    else
    	--This isn't as expensive as it looks, because typically only one data model will return something. However, we don't know which it will be in advance.
    	for _, dataModel in ipairs(DataModelsWithGladeResourcesRecipes) do
		
        	local recipeDataArray = dataModel:getIDsAndRecipesWhereBuildingID(buildingID)
        	if #recipeDataArray > 0 then

            	local products = {}
            	for _, recipeData in ipairs(recipeDataArray) do
                	local id = dataModel:getRecipeProductID(recipeData)
                	products[id] = dataModel:getRecipeGrade(recipeData)
            	end
            	for id, grade in pairs(products) do
                	local newList = GladeResourcesData.getAllDepositsWherePrimaryResourceAndGrade(id, grade, suppressTrees)
                	for _, deposit in ipairs(newList) do
                    	table.insert(depositsList, deposit)
                	end
            	end
        	end
    	end
    end

    return depositsList
end



---@private
---Looks up the building ID from the building data models for the one that can exploit the given deposit.
---
---@param deposit table record from GladeResourcesData
---@return string, string building display name, icon filename
local function getBuildingForDeposit(deposit)

    local productID = GladeResourcesData.getDepositPrimaryProduct(deposit)
    local isTree = GladeResourcesData.isDepositTree(deposit)

    --This isn't as expensive as it looks, because typically only one data model will return something. However, we don't know which it will be in advance.
    for _, dataModel in ipairs(DataModelsWithGladeResourcesRecipes) do

        local recipeDataArray = dataModel:getIDsAndRecipesWhereProductID(productID)
        if #recipeDataArray > 0 then
            for i = 1, #recipeDataArray do
            	local id = dataModel:getRecipeBuildingID(recipeDataArray[i])
            	if (isTree or dataModel:getName(id) ~= "Woodcutters' Camp") then
            		local name = dataModel:getName(id)
            		local icon = dataModel:getIcon(id)
            		return name, icon
            	end
            end
        end
    end
    return ""
end



---@private
---Checks whether the provided list of deposits can be harvested by a single building. If so, returns that building's name and icon (for use in a building link). Otherwise, returns nil.
---
---@param depositsList table array of deposit records retrieved from GladeResourcesData
---@return string, string building name and icon, if there is only one, nil if none or more than one
local function checkOnlyOneBuildingForAllDeposits(depositsList)

    local buildings = {}
    for _, deposit in ipairs(depositsList) do
        local name, icon = getBuildingForDeposit(deposit)
        buildings[name] = icon
    end

    local firstBuildingName, firstBuildingIcon = next(buildings)
    if firstBuildingName and not next(buildings, firstBuildingName) then
        return firstBuildingName, firstBuildingIcon
    else
        return nil
    end
end



---@private
---Goes through the provided list of deposits and finds the maximum number of bonuses, so that the view can be standardized.
---
---@param depositsList table array of deposit records retrieved from GladeResourcesData
---@return number maximum bonuses across all in the list
local function countMaxBonusesInList(depositsList)
    local maxFound = 0
    for _, deposit in ipairs(depositsList) do
        local numBonuses = GladeResourcesData.getDepositNumBonusResources(deposit)
        if numBonuses > maxFound then
            maxFound = numBonuses
        end
    end
    return maxFound
end



---@private
---Tallies up the categories and concatenates them. Also makes them plural if there are more than one of a particular category.
---
---@param depositsList table
---@return string the categories among the deposits
local function writeCaptionForDeposits(depositsList)

    local categories = {}
    for _, deposit in ipairs(depositsList) do
        local category = GladeResourcesData.getDepositCategory(deposit)
        if not categories[category] then
            categories[category] = 1
        else
            categories[category] = categories[category] + 1
        end
    end

    local caption = ""
    for category, categoryCount in pairs(categories) do
        if categoryCount > 1 then
            category = category .. "s"
        end
        if caption == "" then
            caption = category
        else
            caption = caption .. " & " .. category
        end
    end

    return caption
end



---@private
---Tallies up the descriptions. If there's exactly one, it returns that description, otherwise it returns blank since they can't be combined (and to save space).
---
---@param depositsList table
---@return string the one or shared description in the list, or "" if they are varied
local function writeDescriptionForDeposits(depositsList)

    --Heuristic: If there are more then 2, then there's no way all the descriptions match.
    if #depositsList > 2 then
        return ""
    end

    local descriptions = {}
    for _, deposit in ipairs(depositsList) do
        local desc = GladeResourcesData.getDepositDescription(deposit)
        descriptions[desc] = true
    end

    --Use exactly one description unless there are more, in which case return empty.
    local firstDescription = next(descriptions)
    if firstDescription and not next(descriptions, firstDescription) then
        return firstDescription
    else
        return ""
    end
end


local function writeBiomePartialLink(biomeID)

    local icon = BiomesData.getIcon(biomeID) .. ".png"
    local pageName = BiomesData.getName(biomeID)
    return "File:" .. icon .. "|" .. pageName
end



---@private
---Sets up the view, based on the display override or lack thereof. Returns whatever else needs to be appended to do so.
---
---@param depositsList table list of deposits from GladeResourcesData
---@param maxBonuses number slots for bonus resources
---@param title string title for the whole panel
---@param buildingID string an ID for a building specified, if any
---@param skipFlagForBiomes string flag for skipping biomes columns
---@param displayOverride string override code, if any
---@return string wiki markup
local function renderViewStart(depositsList, maxBonuses, title, buildingID, skipFlagForBiomes, displayOverride)

    displayOverride = displayOverride --TODO write the other view modes

    local skipBuildingColumn = ""
    if buildingID then
        skipBuildingColumn = "skip"
    else
        local buildingName = checkOnlyOneBuildingForAllDeposits(depositsList)
        if buildingName then
            skipBuildingColumn = "skip"
        end
    end


    local caption = writeCaptionForDeposits(depositsList)
    local numDeposits = #depositsList

    --Special case where we swap the title out for the name of the one deposit and show the description
    local description = ""
    if numDeposits == 1 and not displayOverride == VIEW_ARG_FORCE_DEFAULT then
        title = GladeResourcesData.getDepositName(depositsList[1])
        description = writeDescriptionForDeposits(depositsList)
    end

    local viewArgs = {
        [VIEW_ARG_HEAD] = title,
        [VIEW_ARG_DESC] = description,
        [VIEW_ARG_CAPTION] = caption,
        [VIEW_ARG_NUM_DEPOSITS] = numDeposits,
        [VIEW_ARG_MAX_BONUSES] = maxBonuses,
        [VIEW_ARG_BUILDING] = skipBuildingColumn,
        [VIEW_ARG_SKIP_BIOMES] = skipFlagForBiomes,
    }

    if skipFlagForBiomes ~= SKIP_FLAG then
        for i, biomeID in ipairs(biomesIDs) do
            local biomeArg = VIEW_ARG_BIOME_PREFIX .. i
            viewArgs[biomeArg] = writeBiomePartialLink(biomeID)
        end
    end

    local wikiMarkup = frame:expandTemplate{
        title = VIEW_TEMPLATE_START,
        args = viewArgs,
    }

    return wikiMarkup
end



---@private
-----Converts the data about the product in the specified deposit for the view.
---
---@param deposit table from GladeResourceData
---@param displayOverride string override code, if any
---@return string wiki markup
local function renderProduct(deposit, displayOverride)

    displayOverride = displayOverride --TODO write the other display modes

    local productID, amount = GladeResourcesData.getDepositPrimaryProduct(deposit)
    local iconFilename = GoodsData.getIcon(productID)
    local productName = GoodsData.getName(productID)

    local wikiMarkup = ""
    if amount > 1 then
        wikiMarkup = wikiMarkup .. "'''" .. amount .. "'''&nbsp;"
    end

    wikiMarkup = wikiMarkup .. frame:expandTemplate{
        title = VIEW_RESOURCE_LINK,
        args = {
            ["iconsize"] = "medium",
            ["iconfilename"] = iconFilename,
            ["name"] = productName,
            ["display"] = "notext",
        }
    }

    return wikiMarkup
end



---@private
-----Converts the data about the specified bonus in the specified deposit for the view.
---
---@param deposit table from GladeResourceData
---@param index number which bonus from deposit to get
---@param displayOverride string override code, if any
---@return string wiki markup
local function renderBonus(deposit, index, displayOverride)

    displayOverride = displayOverride --TODO write the other display modes

    local productID, amount, chance = GladeResourcesData.getDepositBonusResourceAt(deposit, index)

    if not productID then
        return ""
    end

    local iconFilename = GoodsData.getIcon(productID)
    local productName = GoodsData.getName(productID)

    local wikiMarkup = ""
    if amount and amount > 1 then
        wikiMarkup = wikiMarkup .. "'''" .. amount .. "'''&nbsp;"
    end

    wikiMarkup = wikiMarkup .. frame:expandTemplate{
        title = VIEW_RESOURCE_LINK,
        args = {
            ["name"] = productName,
            ["iconfilename"] = iconFilename,
            ["iconsize"] = "medium",
            ["display"] = "notext",
        }
    }

    wikiMarkup = wikiMarkup .. "<br>'''" .. chance*100 .. "%'''"

    return wikiMarkup
end


---@private
---Looks up the building that can get the specified deposit for the view.
---
---@param deposit table from GladeResourceData
---@param displayOverride string override code, if any
---@return string wiki markup
local function renderBuilding(deposit, displayOverride)

    displayOverride = displayOverride --TODO write the other display modes

    local buildingName, buildingIcon = getBuildingForDeposit(deposit)

    local wikiMarkup = frame:expandTemplate{
        title = VIEW_BUILDING_LINK,
        args = {
            ["name"] = buildingName,
            ["iconfilename"] = buildingIcon,
            ["iconsize"] = "large",
            ["display"] = "notext"
        }
    }

    return wikiMarkup
end


---@private
---Used when a building is specified or there's just one row.
---
---@param buildingID string
---@return string wiki markup
local function renderBottomBuildingLink(buildingID, buildingName, buildingIcon)

    if buildingID then
        buildingName = BuildingDataProxy.getName(buildingID)
        buildingIcon = BuildingDataProxy.getIcon(buildingID)
    end

    local wikiMarkup = frame:expandTemplate{
        title = VIEW_BUILDING_LINK,
        args = {
            ["name"] = buildingName,
            ["iconfilename"] = buildingIcon,
            ["iconsize"] = "huge"
        }
    }

    return wikiMarkup
end



---@private
---Makes a row for one deposit. If the buildingID is given, skips showing it, because it means it's factored out of the rows.
---
---@param deposit table from GladeResourceData
---@param numDeposits number total number of deposits
---@param maxBonuses number slots for bonus resources
---@param buildingID string a specified buildingID, if any
---@param isSkippingBiomes string flag for skipping biomes columns
---@param displayOverride string override code, if any
---@return string wiki markup
local function renderViewOneRow(deposit, numDeposits, maxBonuses, buildingID, isSkippingBiomes, displayOverride)

    displayOverride = displayOverride --TODO write the other display modes

    --Special case where we take out the name and building icon when there's just one deposit or it's all harvestable by one building.
    local depositName = ""
    if numDeposits > 1 then
        depositName = GladeResourcesData.getDepositName(deposit)
    end
    local buildingLink = ""
    if not buildingID then
        buildingLink = renderBuilding(deposit, displayOverride)
    end

    local viewArgs = {
        [VIEW_ARG_DEPOSIT] = depositName,
        [VIEW_ARG_BUILDING] = buildingLink,
        [VIEW_ARG_CHARGES] = GladeResourcesData.getDepositCharges(deposit),
        [VIEW_ARG_PRODUCT] = renderProduct(deposit, displayOverride),
        [VIEW_ARG_MAX_BONUSES] = maxBonuses,
        [VIEW_ARG_BONUS_1] = renderBonus(deposit, 1, displayOverride),
        [VIEW_ARG_BONUS_2] = renderBonus(deposit, 2, displayOverride),
        [VIEW_ARG_BONUS_3] = renderBonus(deposit, 3, displayOverride),
        [VIEW_ARG_BONUS_4] = renderBonus(deposit, 4, displayOverride),
        [VIEW_ARG_SKIP_BIOMES] = isSkippingBiomes,
    }

    local depositID = GladeResourcesData.getDepositID(deposit)
    if isSkippingBiomes ~= SKIP_FLAG then
        for i, biomeID in ipairs(biomesIDs) do
            local biomeArg = VIEW_ARG_BIOME_PREFIX .. i
            local isInBiome = BiomesData.isNodeOfAnyTypeInBiome(depositID, biomeID)
            local biomePartialLink = writeBiomePartialLink(biomeID)
            viewArgs[biomeArg] = isInBiome and biomePartialLink or ""
        end
    end

    return frame:expandTemplate{
        title = VIEW_TEMPLATE_ROW,
        args = viewArgs,
    }
end



---@private
---Goes through the list of deposits and makes rows for each.
---
---@param depositsList table list of deposits from GladeResourcesData
---@param maxBonuses number slots for bonus resources
---@param buildingID string an ID for a building specified, if any
---@param skipFlagForBiomes string flag for skipping biomes columns
---@param displayOverride string override code, if any
---@return string wiki markup
local function renderViewAllRows(depositsList, maxBonuses, buildingID, skipFlagForBiomes, displayOverride)

    local numDeposits = #depositsList

    -- Factor out a single building from the rows.
    if not buildingID then
        local buildingName = checkOnlyOneBuildingForAllDeposits(depositsList)
        if buildingName then
            buildingID = BuildingDataProxy.getID(buildingName)
        end
    end

    local wikiMarkup = ""
    for _, deposit in ipairs(depositsList) do
        wikiMarkup = wikiMarkup .. "\n"
        wikiMarkup = wikiMarkup .. renderViewOneRow(deposit, numDeposits, maxBonuses, buildingID, skipFlagForBiomes, displayOverride)
    end

    return wikiMarkup
end



---@private
---Closes out the view, based on the display override or lack thereof. Returns whatever else needs to be appended to do so.
---
---@param depositsList table list of deposits from GladeResourcesData
---@param buildingID string an ID for a building specified, if any
---@param displayOverride string override code, if any
---@return string wiki markup
local function renderViewEnd(depositsList, buildingID, displayOverride)

    displayOverride = displayOverride --TODO write the other display modes

    local numDeposits = #depositsList

    local buildingLink = ""

    if numDeposits > 0 then
        if buildingID then
            buildingLink = renderBottomBuildingLink(buildingID)
        else
            local buildingName, buildingIcon = checkOnlyOneBuildingForAllDeposits(depositsList)
            if buildingName and buildingIcon then
                buildingLink = renderBottomBuildingLink(nil, buildingName, buildingIcon)
            end
        end
    end

    local wikiMarkup = ""
    wikiMarkup = wikiMarkup .. frame:expandTemplate{
        title = VIEW_TEMPLATE_END,
        args = {
            ["numdeposits"] = numDeposits,
            ["building"] = buildingLink,
        },
    }
    return wikiMarkup
end

--endregion



--region Public methods

---Called from Template:Deposit. Returns markup text for display by using external view templates.
---
---@param mwFrame table the Mediawiki calling context for the template
---@return string wiki markup
function GladeResourcesController.main(mwFrame)

    frame = mwFrame
    local productName = frame.args.product --TODO these are currently IDs, they need to be names
    local bonusName = frame.args.bonus
    local buildingName = frame.args.building
    local skipFlagForBiomes = frame.args[VIEW_ARG_SKIP_BIOMES]
    local displayOverride = frame.args.display
    local suppressTrees = true

    biomesIDs = sortArrayWith(BiomesData.getAllBiomeIDs(), BiomesData.getName)

    --Unset blanks back to nil or get their names from their data model
    local productID
    if productName and productName ~= "" then
        productID = GoodsData.getGoodID(productName)
    end
    local bonusID
    if bonusName and bonusName ~= "" then
        bonusID = GoodsData.getGoodID(bonusName)
    end
    local buildingID = BuildingDataProxy.getID(buildingName)

	--Find out whether Trees should be excluded
	if (not buildingName) or buildingName == "" or buildingName == "Woodcutters' Camp" then
		suppressTrees = false
	end

    local depositsList = {}
    local title = ""
    if productID and bonusID then
        if productID ~= bonusID then
            error(ERROR_MESSAGE_PRODUCT_AND_BONUS_BUT_NOT_MATCH)
        else
            depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWherePrimaryOrBonusResource(productID, suppressTrees), GladeResourcesData.getDepositName)
        end
        title = #depositsList .. " Deposits for " .. productName .. " or as a bonus."

    elseif productID then
        depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWherePrimaryResource(productID, suppressTrees), GladeResourcesData.getDepositName)
        title = #depositsList .. " Deposits for " .. productName .. "."

    elseif bonusID then
        depositsList = sortArrayWith(GladeResourcesData.getAllDepositsWhereBonusResource(bonusID, suppressTrees), GladeResourcesData.getDepositName)
        title = #depositsList .. " Deposits where " .. bonusName .. " is a bonus."

    elseif buildingID then
        depositsList = sortArrayWith(assembleDepositsFromBuilding(buildingID, suppressTrees), GladeResourcesData.getDepositName)
        title = #depositsList .. " Deposits exploited by the " .. buildingName .. "."

    else
        error(ERROR_MESSAGE_NO_PARAMETERS_GIVEN)
    end

    local maxBonuses = countMaxBonusesInList(depositsList)

    local wikiMarkup = ""
    wikiMarkup = wikiMarkup .. renderViewStart(depositsList, maxBonuses, title, buildingID, skipFlagForBiomes, displayOverride)
    wikiMarkup = wikiMarkup .. renderViewAllRows(depositsList, maxBonuses, buildingID, skipFlagForBiomes, displayOverride)
    wikiMarkup = wikiMarkup .. renderViewEnd(depositsList, buildingID, displayOverride)

    return wikiMarkup
end

--endregion



return GladeResourcesController