Module:RecipeController: Difference between revisions

From Against the Storm Official Wiki
(Corrected heading calculation)
m (Fixing count of recipes for building-based table)
 
(4 intermediate revisions by the same user not shown)
Line 18: Line 18:
local FarmsData = require("Module:FarmsData")
local FarmsData = require("Module:FarmsData")
local CampsData = require("Module:CampsData")
local CampsData = require("Module:CampsData")
local CollectorsData = require("Module:CollectorsData")


local GoodsData = require("Module:GoodsData")
local GoodsData = require("Module:GoodsData")
Line 48: Line 49:
--region Private methods
--region Private methods


---extractIngredientsNamesAndIcons
---
--- Get the ingredients from the specified recipe and return a list of
--- the names and icons of the ingredients for that recipe.
---
---@param recipeID string the ID of the recipe from which to extract ingredients
---@param recipeID string the ID of the recipe from which to extract ingredients
---@return table of ingredients and options (stack size, name, and icon)
---@return table of ingredients and options (stack size, name, and icon)
Line 75: Line 79:




---extractServiceGoodsNamesAndIcons
---
--- Get the ingredients from the specified recipe and return a list of
--- the names and icons of the ingredients for that recipe.
---
---@param recipeID string the ID of the recipe from which to extract ingredients
---@param recipeID string the ID of the recipe from which to extract ingredients
---@return table of ingredients and options (stack size, name, and icon)
---@return table of ingredients and options (stack size, name, and icon)
Line 102: Line 109:




---
--- Main engine that builds the view based on the list of buildings from
--- workshops, which are slightly different than other building lists.
---
---@param recipeListFromWorkshops table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param requiredBuilding string the name of the required building
local function buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, requiredBuilding)
local function buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, requiredBuilding)


Line 130: Line 145:
buildingName, buildingIcon,
buildingName, buildingIcon,
gradeStars, productionTime, ingredientsList,
gradeStars, productionTime, ingredientsList,
productStackSize, productName, productIcon)
productStackSize, productName, productIcon, requiredBuilding)


end
end
Line 140: Line 155:




---
--- Main engine that builds the view based on the list of buildings from
--- services, which are slightly different than other building lists.
---
---@param recipeListFromServices table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param requiredBuilding string the name of the required building
local function buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, requiredBuilding)
local function buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, requiredBuilding)


Line 168: Line 191:
buildingName, buildingIcon,
buildingName, buildingIcon,
gradeStars, productionTime, ingredientsList,
gradeStars, productionTime, ingredientsList,
resultStackSize, serviceName, serviceIcon)
resultStackSize, serviceName, serviceIcon, requiredBuilding)
end
end
end
end
Line 177: Line 200:




---
--- Main engine that builds the view based on the list of buildings from
--- farms, which are slightly different than other building lists.
---
---@param buildingListFromFarms table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param productID string of the product to render
---@param requiredBuilding string the name of the required building
local function buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID, requiredBuilding)
local function buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID, requiredBuilding)


Line 203: Line 235:
gradeStars, (plantingTime + harvestingTime),
gradeStars, (plantingTime + harvestingTime),
ingredientsList, stackSize,
ingredientsList, stackSize,
productName, productIcon)
productName, productIcon, requiredBuilding)
end
end


Line 214: Line 246:




---
--- Main engine that builds the view based on the list of buildings from
--- camps, which are slightly different than other building lists.
---
---@param buildingListFromCamps table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param productID string of the product to render
---@param requiredBuilding string the name of the required building
local function buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, requiredBuilding)
local function buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, requiredBuilding)


Line 240: Line 281:
gradeStars, gatheringTime,
gradeStars, gatheringTime,
ingredientsList, stackSize,
ingredientsList, stackSize,
productName, productIcon)
productName, productIcon, requiredBuilding)
end
 
end
end
 
end
end
 
 
 
---
--- Main engine that builds the view based on the list of recipes from
--- collectors, , which are slightly different than other building lists.
---
---@param buildingListFromCollectors table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param productID string of the product to render
---@param requiredBuilding string the name of the required building
local function buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, productID, requiredBuilding)
 
for _, buildingName in ipairs(buildingListFromCollectors or {}) do
 
-- If we're screening for a building (not nil), then we have to match
-- the current building
if not requiredBuilding or buildingName == requiredBuilding then
 
for i = 1, CollectorsData.getCollectorNumberOfRecipes(buildingName) do
 
local goodID, stackSize = CollectorsData.getCollectorRecipeProduct(buildingName, i)
 
-- If we're screening for a productID (not nil), then we have to
-- match the current goodID
if not productID or goodID == productID then
 
local buildingIcon = CollectorsData.getCollectorIcon(buildingName)
local gradeStars, productionTime = CollectorsData.getCollectorRecipeStats(buildingName, i)
local ingredientsList = {}
local productName = GoodsData.getGoodNameByID(goodID)
local productIcon = GoodsData.getGoodIconByID(goodID)
 
RecipeView.addRowForRecipe(displayOverride, numIngredients,
buildingName, buildingIcon,
gradeStars, productionTime,
ingredientsList, stackSize,
productName, productIcon, requiredBuilding)
end
end


Line 274: Line 361:
table.insert(intersection, valueToFind)
table.insert(intersection, valueToFind)
end
end
end
if #intersection == 0 then
return nil
end
end


Line 285: Line 376:
--region Public methods
--region Public methods


function RecipeController.renderWithIngredient(ingredientName, displayOverride)
---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param ingredientName string the ingredient to base the table on
---@param buildingName string the building to base the table on
---@param displayOverride string to control the display if not default
local function renderWithIngredientAndBuilding(ingredientName, buildingName, displayOverride)
 
-- Service and workshops recipes have ingredients. Camps and farms do not.
local ingredientID = GoodsData.getGoodID(ingredientName)
if not ingredientID then
return "No ingredient found with that name: " .. ingredientName .. "."
end
local recipeListFromWorkshopsRecipes = WorkshopsRecipesData.getAllRecipeIDsWithIngredientID(ingredientID)
local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
recipeListFromWorkshops = findIntersection(recipeListFromWorkshopsRecipes, recipeListFromWorkshops)
 
local recipeListFromServicesRecipes = ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(ingredientID)
local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)
recipeListFromServices = findIntersection(recipeListFromServicesRecipes, recipeListFromServices)
 
-- Find out what the largest number of ingredients in this table is. Need
-- this before we start building any of the beginning and the middle so
-- that they are all the same.
local numIngredients  = 1
if recipeListFromWorkshops then
for _, recipeID in ipairs(recipeListFromWorkshops) do
local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID)
if numIngredients < thisNum then
numIngredients = thisNum
end
end
end
if recipeListFromServices then
for _, recipeID in ipairs(recipeListFromServices) do
local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID)
if numIngredients < thisNum then
numIngredients = thisNum
end
end
end
 
-- Count the number of recipes in both the Workshops and Services.
local numRecipes = 0
local header -- = nil
if recipeListFromWorkshops ~= nil then
numRecipes = numRecipes + #recipeListFromWorkshops
header = TABLE_HEADER_PRODUCT
end
if recipeListFromServices ~= nil then
if numRecipes > 0 then
header = TABLE_HEADER_BOTH
else
header = TABLE_HEADER_SERVICE
end
numRecipes = numRecipes + #recipeListFromServices
end
 
if numRecipes == 0 then
return "No recipes found for ingredient " .. ingredientName .. " in the building " .. buildingName .. "."
end
 
RecipeView.startViewForIngredientAndBuilding(ingredientName, buildingName, displayOverride, numRecipes, numIngredients, header)
 
buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, buildingName)
 
buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, buildingName)
 
return RecipeView.endView(displayOverride)
end
 
 
 
---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param ingredientName string the ingredient to base the table on
---@param displayOverride string to control the display if not default
local function renderWithIngredient(ingredientName, displayOverride)


-- Service and workshops recipes have ingredients. Camps and farms do not.
-- Service and workshops recipes have ingredients. Camps and farms do not.
Line 342: Line 515:
buildRowsForServices(recipeListFromServices, displayOverride, numIngredients)
buildRowsForServices(recipeListFromServices, displayOverride, numIngredients)


return RecipeView.endView(ingredientName, displayOverride)
return RecipeView.endView(displayOverride)
end
end






function RecipeController.renderWithProductAndBuilding(productName, buildingName, displayOverride)
---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param productName string the product to base the table on
---@param buildingName string the building to base the table on
---@param displayOverride string to control the display if not default
local function renderWithProductAndBuilding(productName, buildingName, displayOverride)


-- Get lists from all buildings
-- Get lists from all buildings
local productID = GoodsData.getGoodID(productName)
local productID = GoodsData.getGoodID(productName)
if not productID then
 
return "No product found with that name: " .. productName .. "."
local recipeListFromWorkshopsRecipes
if productID then
recipeListFromWorkshopsRecipes = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID)
end
end
local recipeListFromWorkshopsRecipes = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID)
local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
recipeListFromWorkshops = findIntersection(recipeListFromWorkshopsRecipes, recipeListFromWorkshops)
recipeListFromWorkshops = findIntersection(recipeListFromWorkshopsRecipes, recipeListFromWorkshops)


-- It could be the name of a service instead of the name of a product.
-- It could be the name of a service instead of the name of a product.
local recipeListFromServicesRecipes = ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(productName)
local recipeListFromServicesRecipes = ServicesRecipesData.getAllRecipeIDsForServiceName(productName)
local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)
local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)
recipeListFromServices = findIntersection(recipeListFromServicesRecipes, recipeListFromServices)
recipeListFromServices = findIntersection(recipeListFromServicesRecipes, recipeListFromServices)


-- These result in a list of buildings, not recipes.
-- These result in a list of buildings, not recipes.
local buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID)
local buildingListFromCamps
local buildingListFromFarms
local buildingListFromCollectors
if productID then
buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID)
buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID)
buildingListFromCollectors = CollectorsData.getCollectorNamesWithRecipeProductID(productID)
end
buildingListFromCamps = findIntersection(buildingListFromCamps, {buildingName} )
buildingListFromCamps = findIntersection(buildingListFromCamps, {buildingName} )
local buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID)
buildingListFromFarms = findIntersection(buildingListFromFarms, {buildingName})
buildingListFromFarms = findIntersection(buildingListFromFarms, {buildingName})
buildingListFromCollectors = findIntersection(buildingListFromCollectors, {buildingName})


-- Find out what the largest number of ingredients in this table is. Need
-- Find out what the largest number of ingredients in this table is. Need
Line 405: Line 592:
if buildingListFromFarms ~= nil then
if buildingListFromFarms ~= nil then
numRecipes = numRecipes + #buildingListFromFarms
numRecipes = numRecipes + #buildingListFromFarms
header = TABLE_HEADER_PRODUCT
end
if buildingListFromCollectors ~= nil then
numRecipes = numRecipes + #buildingListFromCollectors
header = TABLE_HEADER_PRODUCT
header = TABLE_HEADER_PRODUCT
end
end
Line 417: Line 608:


if numRecipes == 0 then
if numRecipes == 0 then
return "No recipes found for product: " .. productName .. "."
return "No recipes found for product " .. productName .. " at the building " .. buildingName .. "."
end
end


Line 430: Line 621:
buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, buildingName)
buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, buildingName)


return RecipeView.endView(ingredientName, displayOverride)
buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, productID, buildingName)
 
return RecipeView.endView(displayOverride)
end
end






function RecipeController.renderWithProduct(productName, displayOverride)
---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param productName string the product to base the table on
---@param displayOverride string to control the display if not default
local function renderWithProduct(productName, displayOverride)


-- Get lists from all buildings
-- Get lists from all buildings
local productID = GoodsData.getGoodID(productName)
local productID = GoodsData.getGoodID(productName)
if not productID then
 
return "No product found with that name: " .. productName .. "."
local recipeListFromWorkshops
if productID then
recipeListFromWorkshops = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID)
end
end
local recipeListFromWorkshops = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID)
-- It could be the name of a service instead of the name of a product.
-- It could be the name of a service instead of the name of a product.
local recipeListFromServices = ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(productName)
local recipeListFromServices = ServicesRecipesData.getAllRecipeIDsForServiceName(productName)


-- These result in a list of buildings, not recipes.
-- These result in a list of buildings, not recipes.
local buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID)
local buildingListFromCamps
local buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID)
local buildingListFromFarms
local buildingListFromCollectors
if productID then
buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID)
buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID)
buildingListFromCollectors = CollectorsData.getCollectorNamesWithRecipeProductID(productID)
end
 


-- Find out what the largest number of ingredients in this table is. Need
-- Find out what the largest number of ingredients in this table is. Need
Line 484: Line 692:
if buildingListFromFarms ~= nil then
if buildingListFromFarms ~= nil then
numRecipes = numRecipes + #buildingListFromFarms
numRecipes = numRecipes + #buildingListFromFarms
header = TABLE_HEADER_PRODUCT
end
if buildingListFromCollectors ~= nil then
numRecipes = numRecipes + #buildingListFromCollectors
header = TABLE_HEADER_PRODUCT
header = TABLE_HEADER_PRODUCT
end
end
Line 509: Line 721:
buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID)
buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID)


return RecipeView.endView(ingredientName, displayOverride)
buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, productID)
 
return RecipeView.endView(displayOverride)
end
end






function RecipeController.renderWithBuilding(buildingName, displayOverride)
---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param buildingName string the building to base the table on
---@param displayOverride string to control the display if not default
local function renderWithBuilding(buildingName, displayOverride)


local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)
local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)


-- For camps, farms, and collectors where recipes are stored in the
-- building data file, just check to make sure the building has recipes
-- from that data. If so, that's all we need to make the building list.
local buildingListFromCamps = {}
local buildingListFromCamps = {}
if CampsData.getCampNumberOfRecipes(buildingName) > 0 then
if CampsData.getCampNumberOfRecipes(buildingName) > 0 then
buildingListFromCamps = { buildingName }
buildingListFromCamps = { buildingName }
end
end
local buildingListFromFarms = {}
local buildingListFromFarms = {}
if FarmsData.getFarmNumberOfRecipes(buildingName) > 0 then
if FarmsData.getFarmNumberOfRecipes(buildingName) > 0 then
buildingListFromFarms = { buildingName }
buildingListFromFarms = { buildingName }
end
local buildingListFromCollectors = {}
if CollectorsData.getCollectorNumberOfRecipes(buildingName) > 0 then
buildingListFromCollectors = { buildingName }
end
end


Line 558: Line 785:
end
end
if buildingListFromCamps ~= nil then
if buildingListFromCamps ~= nil then
numRecipes = numRecipes + #buildingListFromCamps
numRecipes = numRecipes + CampsData.getCampNumberOfRecipes(buildingName)
header = TABLE_HEADER_PRODUCT
header = TABLE_HEADER_PRODUCT
end
end
if buildingListFromFarms ~= nil then
if buildingListFromFarms ~= nil then
numRecipes = numRecipes + #buildingListFromFarms
numRecipes = numRecipes + FarmsData.getFarmNumberOfRecipes(buildingName)
header = TABLE_HEADER_PRODUCT
end
if buildingListFromCollectors ~= nil then
numRecipes = numRecipes + CollectorsData.getCollectorNumberOfRecipes(buildingName)
header = TABLE_HEADER_PRODUCT
header = TABLE_HEADER_PRODUCT
end
end
Line 588: Line 819:
buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, nil, buildingName)
buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, nil, buildingName)


return RecipeView.endView(ingredientName, displayOverride)
buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, nil, buildingName)
 
return RecipeView.endView(displayOverride)
end
end


Line 597: Line 830:
--region Public methods
--region Public methods


---
--- Called by the template. Distributes
---@param frame table
function RecipeController.renderRecipe(frame)
function RecipeController.renderRecipe(frame)


Line 605: Line 841:
local displayOverride = frame.args.display
local displayOverride = frame.args.display


if ingredientName and ingredientName ~= "" then
if ingredientName and ingredientName ~= "" and buildingName and buildingName ~= "" then
return RecipeController.renderWithIngredient(ingredientName, displayOverride)
return renderWithIngredientAndBuilding(ingredientName, buildingName, displayOverride)
else
else
if productName and productName ~= "" and buildingName and buildingName ~= "" then
if ingredientName and ingredientName ~= "" then
return RecipeController.renderWithProductAndBuilding(productName, buildingName, displayOverride)
return renderWithIngredient(ingredientName, displayOverride)
else
else
if productName and productName ~= "" then
if productName and productName ~= "" and buildingName and buildingName ~= "" then
return RecipeController.renderWithProduct(productName, displayOverride)
return renderWithProductAndBuilding(productName, buildingName, displayOverride)
else
else
if buildingName and buildingName ~= "" then
if productName and productName ~= "" then
return RecipeController.renderWithBuilding(buildingName, displayOverride)
return renderWithProduct(productName, displayOverride)
else
else
return "Unknown parameter in Recipe template."
if buildingName and buildingName ~= "" then
return renderWithBuilding(buildingName, displayOverride)
else
return "Unknown parameter in Recipe template."
end
end
end
end
end

Latest revision as of 22:48, 5 December 2023

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

---
--- Module for merging recipe information from data managers.
---
--- @module RecipeData
local RecipeController = {}



--region Dependencies

local RecipeView = require("Module:RecipeView")

local WorkshopsRecipesData = require("Module:WorkshopsRecipesData")
local ServicesRecipesData = require("Module:ServicesRecipesData")

local InstitutionsData = require("Module:InstitutionsData")
local WorkshopsData = require("Module:WorkshopsData")
local FarmsData = require("Module:FarmsData")
local CampsData = require("Module:CampsData")
local CollectorsData = require("Module:CollectorsData")

local GoodsData = require("Module:GoodsData")

--endregion



--region Private member variables

--endregion



--region Private constants

local MAX_INGREDIENTS = 3
local INDEX_OPTION_STACK_SIZE = "stackSize"
local INDEX_OPTION_GOOD_NAME = "name"
local INDEX_OPTION_GOOD_ICON = "icon"

local TABLE_HEADER_BOTH = "Product or Service"
local TABLE_HEADER_PRODUCT = "Product"
local TABLE_HEADER_SERVICE = "Service"

--endregion



--region Private methods

---
--- Get the ingredients from the specified recipe and return a list of
--- the names and icons of the ingredients for that recipe.
---
---@param recipeID string the ID of the recipe from which to extract ingredients
---@return table of ingredients and options (stack size, name, and icon)
local function extractIngredientsNamesAndIcons(recipeID)

	local ingredientsList = {}
	-- Need to convert IDs to names and icons and keep stack size.
	for i = 1, MAX_INGREDIENTS do
		for j = 1, WorkshopsRecipesData.getRecipeNumberOfOptionsByID(recipeID, i) do

			if not ingredientsList[i] then
				ingredientsList[i] = {}
			end

			local goodID, stackSize = WorkshopsRecipesData.getRecipeOptionByID(recipeID, i, j)
			ingredientsList[i][j] = {}
			ingredientsList[i][j][INDEX_OPTION_STACK_SIZE] = stackSize
			ingredientsList[i][j][INDEX_OPTION_GOOD_NAME] = GoodsData.getGoodNameByID(goodID)
			ingredientsList[i][j][INDEX_OPTION_GOOD_ICON] = GoodsData.getGoodIconByID(goodID)
		end
	end

	return ingredientsList
end



---
--- Get the ingredients from the specified recipe and return a list of
--- the names and icons of the ingredients for that recipe.
---
---@param recipeID string the ID of the recipe from which to extract ingredients
---@return table of ingredients and options (stack size, name, and icon)
local function extractServiceGoodsNamesAndIcons(recipeID)

	local ingredientsList = {}
	-- Need to convert IDs to names and icons and keep stack size.
	for i = 1, ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID) do

		ingredientsList[i] = {}

		-- Need to structure this the same way as when there are several
		-- ingredient option groups. But for services there's just one.
		local j = 1
		ingredientsList[i][j] = {}
		local goodID, stackSize = ServicesRecipesData.getRecipeOptionByID(recipeID, i)
		ingredientsList[i][j][INDEX_OPTION_STACK_SIZE] = stackSize
		ingredientsList[i][j][INDEX_OPTION_GOOD_NAME] = GoodsData.getGoodNameByID(goodID)
		ingredientsList[i][j][INDEX_OPTION_GOOD_ICON] = GoodsData.getGoodIconByID(goodID)
	end

	return ingredientsList
end




---
--- Main engine that builds the view based on the list of buildings from
--- workshops, which are slightly different than other building lists.
---
---@param recipeListFromWorkshops table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param requiredBuilding string the name of the required building
local function buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, requiredBuilding)

	-- Build the rows of the middle of the view. If it's nil and there are no
	-- recipes, skip it by getting ipairs on an empty table.
	for _, recipeID in ipairs(recipeListFromWorkshops or {}) do

		local gradeStars = WorkshopsRecipesData.getRecipeGradeByID(recipeID)
		local productionTime = WorkshopsRecipesData.getRecipeProductionTimeByID(recipeID)
		local productID, productStackSize = WorkshopsRecipesData.getRecipeProductByID(recipeID)

		local ingredientsList = extractIngredientsNamesAndIcons(recipeID)

		local productName = GoodsData.getGoodNameByID(productID)
		local productIcon = GoodsData.getGoodIconByID(productID)

		local buildingListFromWorkshops = WorkshopsData.getWorkshopNamesWithRecipeID(recipeID)

		for _, buildingName in ipairs(buildingListFromWorkshops) do

			-- If we're screening for a building (not nil), then we have
			-- to match the current building
			if not requiredBuilding or buildingName == requiredBuilding then

				local buildingIcon = WorkshopsData.getWorkshopIcon(buildingName)

				RecipeView.addRowForRecipe(displayOverride, numIngredients,
						buildingName, buildingIcon,
						gradeStars, productionTime, ingredientsList,
						productStackSize, productName, productIcon, requiredBuilding)

			end
		end

	end
end



---
--- Main engine that builds the view based on the list of buildings from
--- services, which are slightly different than other building lists.
---
---@param recipeListFromServices table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param requiredBuilding string the name of the required building
local function buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, requiredBuilding)

	-- Same. If it's nil and there are no recipes, skip it by getting ipairs
	-- on an empty table.
	for _, recipeID in ipairs(recipeListFromServices or {}) do

		local gradeStars = ServicesRecipesData.getRecipeGradeByID(recipeID)
		local serviceName = ServicesRecipesData.getRecipeServiceNameByID(recipeID)
		local serviceIcon = ServicesRecipesData.getRecipeServiceIconByID(recipeID)

		local ingredientsList = extractServiceGoodsNamesAndIcons(recipeID)

		local buildingListFromInstitutions = InstitutionsData.getInstitutionNamesWithRecipeID(recipeID)

		local productionTime -- = nil
		local resultStackSize -- = nil

		for _, buildingName in ipairs(buildingListFromInstitutions) do

			-- If we're screening for a building (not nil), then we have
			-- to match the current building
			if not requiredBuilding or buildingName == requiredBuilding then

				local buildingIcon = InstitutionsData.getInstitutionIcon(buildingName)

				RecipeView.addRowForRecipe(displayOverride, numIngredients,
						buildingName, buildingIcon,
						gradeStars, productionTime, ingredientsList,
						resultStackSize, serviceName, serviceIcon, requiredBuilding)
			end
		end

	end
end



---
--- Main engine that builds the view based on the list of buildings from
--- farms, which are slightly different than other building lists.
---
---@param buildingListFromFarms table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param productID string of the product to render
---@param requiredBuilding string the name of the required building
local function buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID, requiredBuilding)

	for _, buildingName in ipairs(buildingListFromFarms or {}) do

		-- If we're screening for a building (not nil), then we have to match
		-- the current building
		if not requiredBuilding or buildingName == requiredBuilding then

			for i = 1, FarmsData.getFarmNumberOfRecipes(buildingName) do

				local goodID, stackSize = FarmsData.getFarmRecipeProduct(buildingName, i)

				-- If we're screening for a productID (not nil), then we have
				-- to match the current goodID
				if not productID or goodID == productID then

					local buildingIcon = FarmsData.getFarmIcon(buildingName)
					local gradeStars, plantingTime, harvestingTime = FarmsData.getFarmRecipeStats(buildingName, i)
					local ingredientsList = {}
					local productName = GoodsData.getGoodNameByID(goodID)
					local productIcon = GoodsData.getGoodIconByID(goodID)

					RecipeView.addRowForRecipe(displayOverride, numIngredients,
							buildingName, buildingIcon,
							gradeStars, (plantingTime + harvestingTime),
							ingredientsList, stackSize,
							productName, productIcon, requiredBuilding)
				end

			end
		end

	end
end



---
--- Main engine that builds the view based on the list of buildings from
--- camps, which are slightly different than other building lists.
---
---@param buildingListFromCamps table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param productID string of the product to render
---@param requiredBuilding string the name of the required building
local function buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, requiredBuilding)

	for _, buildingName in ipairs(buildingListFromCamps or {}) do

		-- If we're screening for a building (not nil), then we have to match
		-- the current building
		if not requiredBuilding or buildingName == requiredBuilding then

			for i = 1, CampsData.getCampNumberOfRecipes(buildingName) do

				local goodID, stackSize = CampsData.getCampRecipeProduct(buildingName, i)

				-- If we're screening for a productID (not nil), then we have to
				-- match the current goodID
				if not productID or goodID == productID then

					local buildingIcon = CampsData.getCampIcon(buildingName)
					local gradeStars, gatheringTime = CampsData.getCampRecipeStats(buildingName, i)
					local ingredientsList = {}
					local productName = GoodsData.getGoodNameByID(goodID)
					local productIcon = GoodsData.getGoodIconByID(goodID)

					RecipeView.addRowForRecipe(displayOverride, numIngredients,
							buildingName, buildingIcon,
							gradeStars, gatheringTime,
							ingredientsList, stackSize,
							productName, productIcon, requiredBuilding)
				end

			end
		end

	end
end



---
--- Main engine that builds the view based on the list of recipes from
--- collectors, , which are slightly different than other building lists.
---
---@param buildingListFromCollectors table the list of recipes to render
---@param displayOverride string to control if the display isn't default
---@param numIngredients number of ingredient columns to display
---@param productID string of the product to render
---@param requiredBuilding string the name of the required building
local function buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, productID, requiredBuilding)

	for _, buildingName in ipairs(buildingListFromCollectors or {}) do

		-- If we're screening for a building (not nil), then we have to match
		-- the current building
		if not requiredBuilding or buildingName == requiredBuilding then

			for i = 1, CollectorsData.getCollectorNumberOfRecipes(buildingName) do

				local goodID, stackSize = CollectorsData.getCollectorRecipeProduct(buildingName, i)

				-- If we're screening for a productID (not nil), then we have to
				-- match the current goodID
				if not productID or goodID == productID then

					local buildingIcon = CollectorsData.getCollectorIcon(buildingName)
					local gradeStars, productionTime = CollectorsData.getCollectorRecipeStats(buildingName, i)
					local ingredientsList = {}
					local productName = GoodsData.getGoodNameByID(goodID)
					local productIcon = GoodsData.getGoodIconByID(goodID)

					RecipeView.addRowForRecipe(displayOverride, numIngredients,
							buildingName, buildingIcon,
							gradeStars, productionTime,
							ingredientsList, stackSize,
							productName, productIcon, requiredBuilding)
				end

			end
		end

	end
end



---
--- Takes two lists and returns the intersection of the two. Slightly more
--- efficient if the second list is smaller than the first.
---
---@param list1 table a flat table (list of values)
---@param list2 table another flat table
---@return table a similarly flat table consisting only of the values that are in both provided lists
local function findIntersection(list1, list2)

	local intersection = {}

	-- Create a new mapping to efficiently check for existence in list2 by
	-- checking the same ID in the next loop.
	local setList2 = {}
	for _, value in ipairs(list2 or {}) do
		setList2[value] = true
	end

	-- Check for intersection and populate the result table.
	for _, valueToFind in ipairs(list1 or {}) do
		if setList2[valueToFind] then
			table.insert(intersection, valueToFind)
		end
	end

	if #intersection == 0 then
		return nil
	end

	return intersection
end

--endregion



--region Public methods

---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param ingredientName string the ingredient to base the table on
---@param buildingName string the building to base the table on
---@param displayOverride string to control the display if not default
local function renderWithIngredientAndBuilding(ingredientName, buildingName, displayOverride)

	-- Service and workshops recipes have ingredients. Camps and farms do not.
	local ingredientID = GoodsData.getGoodID(ingredientName)
	if not ingredientID then
		return "No ingredient found with that name: " .. ingredientName .. "."
	end
	local recipeListFromWorkshopsRecipes = WorkshopsRecipesData.getAllRecipeIDsWithIngredientID(ingredientID)
	local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
	recipeListFromWorkshops = findIntersection(recipeListFromWorkshopsRecipes, recipeListFromWorkshops)

	local recipeListFromServicesRecipes = ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(ingredientID)
	local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)
	recipeListFromServices = findIntersection(recipeListFromServicesRecipes, recipeListFromServices)

	-- Find out what the largest number of ingredients in this table is. Need
	-- this before we start building any of the beginning and the middle so
	-- that they are all the same.
	local numIngredients  = 1
	if recipeListFromWorkshops then
		for _, recipeID in ipairs(recipeListFromWorkshops) do
			local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end
	if recipeListFromServices then
		for _, recipeID in ipairs(recipeListFromServices) do
			local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end

	-- Count the number of recipes in both the Workshops and Services.
	local numRecipes = 0
	local header -- = nil
	if recipeListFromWorkshops ~= nil then
		numRecipes = numRecipes + #recipeListFromWorkshops
		header = TABLE_HEADER_PRODUCT
	end
	if recipeListFromServices ~= nil then
		if numRecipes > 0 then
			header = TABLE_HEADER_BOTH
		else
			header = TABLE_HEADER_SERVICE
		end
		numRecipes = numRecipes + #recipeListFromServices
	end

	if numRecipes == 0 then
		return "No recipes found for ingredient " .. ingredientName .. " in the building " .. buildingName .. "."
	end

	RecipeView.startViewForIngredientAndBuilding(ingredientName, buildingName, displayOverride, numRecipes, numIngredients, header)

	buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, buildingName)

	buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, buildingName)

	return RecipeView.endView(displayOverride)
end



---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param ingredientName string the ingredient to base the table on
---@param displayOverride string to control the display if not default
local function renderWithIngredient(ingredientName, displayOverride)

	-- Service and workshops recipes have ingredients. Camps and farms do not.
	local ingredientID = GoodsData.getGoodID(ingredientName)
	if not ingredientID then
		return "No ingredient found with that name: " .. ingredientName .. "."
	end
	local recipeListFromWorkshops = WorkshopsRecipesData.getAllRecipeIDsWithIngredientID(ingredientID)
	local recipeListFromServices = ServicesRecipesData.getAllRecipeIDsWithServiceGoodID(ingredientID)

	-- Find out what the largest number of ingredients in this table is. Need
	-- this before we start building any of the beginning and the middle so
	-- that they are all the same.
	local numIngredients  = 1
	if recipeListFromWorkshops then
		for _, recipeID in ipairs(recipeListFromWorkshops) do
			local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end
	if recipeListFromServices then
		for _, recipeID in ipairs(recipeListFromServices) do
			local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end

	-- Count the number of recipes in both the Workshops and Services.
	local numRecipes = 0
	local header -- = nil
	if recipeListFromWorkshops ~= nil then
		numRecipes = numRecipes + #recipeListFromWorkshops
		header = TABLE_HEADER_PRODUCT
	end
	if recipeListFromServices ~= nil then
		if numRecipes > 0 then
			header = TABLE_HEADER_BOTH
		else
			header = TABLE_HEADER_SERVICE
		end
		numRecipes = numRecipes + #recipeListFromServices
	end

	if numRecipes == 0 then
		return "No recipes found for ingredient: " .. ingredientName .. "."
	end

	RecipeView.startViewForIngredient(ingredientName, displayOverride, numRecipes, numIngredients, header)

	buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients)

	buildRowsForServices(recipeListFromServices, displayOverride, numIngredients)

	return RecipeView.endView(displayOverride)
end



---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param productName string the product to base the table on
---@param buildingName string the building to base the table on
---@param displayOverride string to control the display if not default
local function renderWithProductAndBuilding(productName, buildingName, displayOverride)

	-- Get lists from all buildings
	local productID = GoodsData.getGoodID(productName)

	local recipeListFromWorkshopsRecipes
	if productID then
		recipeListFromWorkshopsRecipes = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID)
	end
	local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
	recipeListFromWorkshops = findIntersection(recipeListFromWorkshopsRecipes, recipeListFromWorkshops)

	-- It could be the name of a service instead of the name of a product.
	local recipeListFromServicesRecipes = ServicesRecipesData.getAllRecipeIDsForServiceName(productName)
	local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)
	recipeListFromServices = findIntersection(recipeListFromServicesRecipes, recipeListFromServices)

	-- These result in a list of buildings, not recipes.
	local buildingListFromCamps
	local buildingListFromFarms
	local buildingListFromCollectors
	if productID then
		buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID)
		buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID)
		buildingListFromCollectors = CollectorsData.getCollectorNamesWithRecipeProductID(productID)
	end
	buildingListFromCamps = findIntersection(buildingListFromCamps, {buildingName} )
	buildingListFromFarms = findIntersection(buildingListFromFarms, {buildingName})
	buildingListFromCollectors = findIntersection(buildingListFromCollectors, {buildingName})

	-- Find out what the largest number of ingredients in this table is. Need
	-- this before we start building any of the beginning and the middle so
	-- that they are all the same.
	local numIngredients  = 1
	if recipeListFromWorkshops then
		for _, recipeID in ipairs(recipeListFromWorkshops) do
			local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end
	if recipeListFromServices then
		for _, recipeID in ipairs(recipeListFromServices) do
			local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end

	-- Count the number of recipes in all the lists
	local numRecipes = 0
	local header -- = nil
	if recipeListFromWorkshops ~= nil then
		numRecipes = numRecipes + #recipeListFromWorkshops
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromCamps ~= nil then
		numRecipes = numRecipes + #buildingListFromCamps
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromFarms ~= nil then
		numRecipes = numRecipes + #buildingListFromFarms
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromCollectors ~= nil then
		numRecipes = numRecipes + #buildingListFromCollectors
		header = TABLE_HEADER_PRODUCT
	end
	if recipeListFromServices ~= nil then
		if numRecipes > 0 then
			header = TABLE_HEADER_BOTH
		else
			header = TABLE_HEADER_SERVICE
		end
		numRecipes = numRecipes + #recipeListFromServices
	end

	if numRecipes == 0 then
		return "No recipes found for product " .. productName .. " at the building " .. buildingName .. "."
	end

	RecipeView.startViewForProductAndBuilding(productName, buildingName, displayOverride, numRecipes, numIngredients, header)

	buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, buildingName)

	buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, buildingName)

	buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID, buildingName)

	buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID, buildingName)

	buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, productID, buildingName)

	return RecipeView.endView(displayOverride)
end



---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param productName string the product to base the table on
---@param displayOverride string to control the display if not default
local function renderWithProduct(productName, displayOverride)

	-- Get lists from all buildings
	local productID = GoodsData.getGoodID(productName)

	local recipeListFromWorkshops
	if productID then
		recipeListFromWorkshops = WorkshopsRecipesData.getAllRecipeIDsForProductID(productID)
	end
	-- It could be the name of a service instead of the name of a product.
	local recipeListFromServices = ServicesRecipesData.getAllRecipeIDsForServiceName(productName)

	-- These result in a list of buildings, not recipes.
	local buildingListFromCamps
	local buildingListFromFarms
	local buildingListFromCollectors
	if productID then
		buildingListFromCamps = CampsData.getCampNamesWithRecipeProductID(productID)
		buildingListFromFarms = FarmsData.getFarmNamesWithRecipeProductID(productID)
		buildingListFromCollectors = CollectorsData.getCollectorNamesWithRecipeProductID(productID)
	end


	-- Find out what the largest number of ingredients in this table is. Need
	-- this before we start building any of the beginning and the middle so
	-- that they are all the same.
	local numIngredients  = 1
	if recipeListFromWorkshops then
		for _, recipeID in ipairs(recipeListFromWorkshops) do
			local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end
	if recipeListFromServices then
		for _, recipeID in ipairs(recipeListFromServices) do
			local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end

	-- Count the number of recipes in all the lists
	local numRecipes = 0
	local header -- = nil
	if recipeListFromWorkshops ~= nil then
		numRecipes = numRecipes + #recipeListFromWorkshops
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromCamps ~= nil then
		numRecipes = numRecipes + #buildingListFromCamps
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromFarms ~= nil then
		numRecipes = numRecipes + #buildingListFromFarms
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromCollectors ~= nil then
		numRecipes = numRecipes + #buildingListFromCollectors
		header = TABLE_HEADER_PRODUCT
	end
	if recipeListFromServices ~= nil then
		if numRecipes > 0 then
			header = TABLE_HEADER_BOTH
		else
			header = TABLE_HEADER_SERVICE
		end
		numRecipes = numRecipes + #recipeListFromServices
	end

	if numRecipes == 0 then
		return "No recipes found for product: " .. productName .. "."
	end

	RecipeView.startViewForProduct(productName, displayOverride, numRecipes, numIngredients, header)

	buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients)

	buildRowsForServices(recipeListFromServices, displayOverride, numIngredients)

	buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, productID)

	buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, productID)

	buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, productID)

	return RecipeView.endView(displayOverride)
end



---
--- Compiles the data, organizes it appropriately and in a way that protects
--- the view from needing the details, and sends it to the view for rendering
--- before returning it to the template.
---
---@param buildingName string the building to base the table on
---@param displayOverride string to control the display if not default
local function renderWithBuilding(buildingName, displayOverride)

	local recipeListFromWorkshops = WorkshopsData.getAllWorkshopRecipes(buildingName)
	local recipeListFromServices = InstitutionsData.getAllInstitutionRecipes(buildingName)

	-- For camps, farms, and collectors where recipes are stored in the
	-- building data file, just check to make sure the building has recipes
	-- from that data. If so, that's all we need to make the building list.
	local buildingListFromCamps = {}
	if CampsData.getCampNumberOfRecipes(buildingName) > 0 then
		buildingListFromCamps = { buildingName }
	end
	local buildingListFromFarms = {}
	if FarmsData.getFarmNumberOfRecipes(buildingName) > 0 then
		buildingListFromFarms = { buildingName }
	end
	local buildingListFromCollectors = {}
	if CollectorsData.getCollectorNumberOfRecipes(buildingName) > 0 then
		buildingListFromCollectors = { buildingName }
	end

	-- Find out what the largest number of ingredients in this table is. Need
	-- this before we start building any of the beginning and the middle so
	-- that they are all the same.
	local numIngredients  = 1
	if recipeListFromWorkshops then
		for _, recipeID in ipairs(recipeListFromWorkshops) do
			local thisNum = WorkshopsRecipesData.getRecipeNumberOfIngredientsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end
	if recipeListFromServices then
		for _, recipeID in ipairs(recipeListFromServices) do
			local thisNum = ServicesRecipesData.getRecipeNumberOfServiceGoodsByID(recipeID)
			if numIngredients < thisNum	then
				numIngredients = thisNum
			end
		end
	end

	-- Count the number of recipes in all the lists
	local numRecipes = 0
	local header -- = nil
	if recipeListFromWorkshops ~= nil then
		numRecipes = numRecipes + #recipeListFromWorkshops
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromCamps ~= nil then
		numRecipes = numRecipes + CampsData.getCampNumberOfRecipes(buildingName)
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromFarms ~= nil then
		numRecipes = numRecipes + FarmsData.getFarmNumberOfRecipes(buildingName)
		header = TABLE_HEADER_PRODUCT
	end
	if buildingListFromCollectors ~= nil then
		numRecipes = numRecipes + CollectorsData.getCollectorNumberOfRecipes(buildingName)
		header = TABLE_HEADER_PRODUCT
	end
	if recipeListFromServices ~= nil then
		if numRecipes > 0 then
			header = TABLE_HEADER_BOTH
		else
			header = TABLE_HEADER_SERVICE
		end
		numRecipes = numRecipes + #recipeListFromServices
	end

	if numRecipes == 0 then
		return "No recipes found at building: " .. buildingName .. "."
	end

	RecipeView.startViewForBuilding(buildingName, displayOverride, numRecipes, numIngredients, header)

	buildRowsForWorkshops(recipeListFromWorkshops, displayOverride, numIngredients, buildingName)

	buildRowsForServices(recipeListFromServices, displayOverride, numIngredients, buildingName)

	buildRowsForFarms(buildingListFromFarms, displayOverride, numIngredients, nil, buildingName)

	buildRowsForCamps(buildingListFromCamps, displayOverride, numIngredients, nil, buildingName)

	buildRowsForCollectors(buildingListFromCollectors, displayOverride, numIngredients, nil, buildingName)

	return RecipeView.endView(displayOverride)
end

--endregion



--region Public methods

---
--- Called by the template. Distributes
---@param frame table
function RecipeController.renderRecipe(frame)

	-- Extract the template parameters.
	local productName = frame.args.product or frame.args[1]
	local buildingName = frame.args.building or frame.args[2]
	local ingredientName = frame.args.ingredient
	local displayOverride = frame.args.display

	if ingredientName and ingredientName ~= "" and buildingName and buildingName ~= "" then
		return renderWithIngredientAndBuilding(ingredientName, buildingName, displayOverride)
	else
		if ingredientName and ingredientName ~= "" then
			return renderWithIngredient(ingredientName, displayOverride)
		else
			if productName and productName ~= "" and buildingName and buildingName ~= "" then
				return renderWithProductAndBuilding(productName, buildingName, displayOverride)
			else
				if productName and productName ~= "" then
					return renderWithProduct(productName, displayOverride)
				else
					if buildingName and buildingName ~= "" then
						return renderWithBuilding(buildingName, displayOverride)
					else
						return "Unknown parameter in Recipe template."
					end
				end
			end
		end
	end
end

--endregion

return RecipeController