Module:Recipe: Difference between revisions

From Against the Storm Official Wiki
(Better table formatting for the building part)
m (Shrinking icon sizes)
 
(13 intermediate revisions by the same user not shown)
Line 31: Line 31:
-- @module Recipe
-- @module Recipe
local Recipe = {}
local Recipe = {}




---
---
-- Constants
-- Constants
---
--
-- Currently this is the max number of ingredients a recipe can have
local RECIPE_INGRED_MAX = 3
-- Indexes to use for recipe records as they come from the CSV data
local INDEX_RECIPE_GRADE = 2
local INDEX_RECIPE_GRADE = 2
local INDEX_RECIPE_PRODTIME = 3
local INDEX_RECIPE_PRODTIME = 3
Line 41: Line 45:
local INDEX_RECIPE_PRODUCT_ID = 5
local INDEX_RECIPE_PRODUCT_ID = 5
local INDEX_RECIPE_INGREDIENTS = 6
local INDEX_RECIPE_INGREDIENTS = 6
 
-- Indexes to use for workshop records as they come from the CSV data
local INDEX_WORKSHOP_ID = 1
local INDEX_WORKSHOP_ID = 1
local INDEX_WORKSHOP_NAME = 2
local INDEX_WORKSHOP_NAME = 2
local INDEX_WORKSHOP_INITIALLY_ESSENTIAL = 9
local RECIPE_INGRED_MAX = 3


local CSS_CLASS_WIKITABLE_SORTABLE = "wikitable sortable"
-- CSS styling for the recipe table... for now
local WIKI_RESOURCE_SMALL_ICON_MARKUP = ".png|32px|alt="
local CSS_CLASS_WIKITABLE = "wikitable sortable mw-collapsible"
local WIKI_RESOURCE_LARGE_ICON_MARKUP = ".png|64px|alt="
-- Shortcut markup to go after the filename to add the extension and display parameters
local WIKI_BUILDING_ICON_MARKUP = "|left|64px|alt="
local WIKI_MED_ICON_MARKUP = ".png|24px|alt="
local WIKI_LARGE_ICON_MARKUP = ".png|42px|alt="
-- Shortcut markup that's easier for Lua string concatenations
local BR = "<br />"
local BR = "<br />"
local BOLD = "\'\'\'"
local NBSP = "&nbsp;"
local BOLD = "'''"
-- Shortcuts for displaying the efficency grades as stars or the "low meter"
local GRADE_TO_STARS = {
local GRADE_TO_STARS = {
["Grade0"] = "[[File:Grade Stars 0.png|12px|link=|alt=0 Stars]]",
["Grade0"] = "[[File:Grade Stars 0.png|12px|link=|alt=0 Stars]]",
Line 60: Line 65:
["Grade3"] = "★★★"
["Grade3"] = "★★★"
}
}


---
---
Line 70: Line 77:
---
---
-- Called from Template:Recipe to render a table displaying the information.
-- Called from Template:Recipe to render a table displaying the information.
-- Retrieves data from Module:RecipeData. The data retrieved looks like this:
--  
--  
-- Retrieves data from Module:RecipeData
-- (sometimes there's an extra first variable like workshop or ingredient)
-- targetRecipeMatches = {
-- [recipeID] match between recipe and workshops = {
-- [1] = { recipe data },
-- [2] = list of workshops that make it = {
-- { workshop data },
-- { workshop data },
-- { workshop data }
-- }
-- },
-- [recipeID] match between recipe and workshops = {
-- similar
-- }
-- }
--
--
-- @param frame the template includes the calling context, mw.frame
-- @param frame the template passes parameters and page data via frame
-- @return the formatted wikitext/HTML
-- @return the formatted wikitext/HTML
function Recipe.render(frame)
function Recipe.render(frame)
Line 80: Line 101:
     local productName = frame.args.product or frame.args[1]
     local productName = frame.args.product or frame.args[1]
     local buildingName = frame.args.building or frame.args[2]
     local buildingName = frame.args.building or frame.args[2]
     local ingredientName = frame.args.ingredient or frame.args[3]
     local ingredientName = frame.args.ingredient
local display = frame.args.display
local displayListOverride = false
if display == "list" then
displayListOverride = true
end
-- At least one parameter is required.
-- At least one parameter is required.
if productName == "" and buildingName == "" and ingredientName == "" then
if productName == "" and buildingName == "" and ingredientName == "" then
         return "Error: The Recipe template requires that you specify at least one of the following: product, building, or ingredient."
         return "The Recipe template requires that you specify at least one of the following: product, building, or ingredient."
     end
     end
-- Split according to parameters
-- Decide according to parameters
if ingredientName and ingredientName ~= "" then
if ingredientName and ingredientName ~= "" then
return buildIngredientWikitext(ingredientName)
return buildIngredientWikitext(ingredientName, displayListOverride)
else
else
if productName and productName ~= "" and buildingName and buildingName ~= "" then
if productName and productName ~= "" and buildingName and buildingName ~= "" then
return buildProductBuildingWikitext(productName, buildingName)
return buildProductBuildingWikitext(productName, buildingName, displayListOverride)
else
else
if productName and productName ~= "" then
if productName and productName ~= "" then
return buildProductWikitext(productName)
return buildProductWikitext(productName, displayListOverride)
else
else
if buildingName and buildingName ~= "" then
if buildingName and buildingName ~= "" then
return buildBuildingWikitext(buildingName)
return buildBuildingWikitext(buildingName, displayListOverride)
else
else
return "Error: Unknown parameter error."
return "Error in Recipe template: Unknown parameter error."
end
end
end
end
Line 110: Line 136:


---
---
-- Renders the table for the specified product. The data retrieved from
-- Renders the table for the specified product.
-- RecipeData looks like this:
--
-- table of matches {
-- match[recipeID] between recipe and workshops = {
-- recipe data { ... }
-- workshops that make it {
-- workshop data { ... }
-- workshop data { ... }
-- workshop data { ... }
-- }
-- }
-- match[recipeID] between recipe and workshops = {
-- ...
-- }
-- }
--
--
-- @param productName the name of the product
-- @param productName the name of the product
-- @return the wikitext that renders the table
-- @return the wikitext that renders the table
function buildProductWikitext(productName)
function buildProductWikitext(productName, displayListOverride)
local targetRecipeMatches = RecipeData.getAllRecipesForProduct(productName)
local targetRecipeMatches = RecipeData.getAllRecipesForProduct(productName)
if not targetRecipeMatches then
if not targetRecipeMatches then
error("Error retrieving recipes for product: " .. productName)
return "There are no recipes for product " .. productName .. "."
end
end
local wikiTable = startNewWikiTable("Recipes for " .. productName .. ".")
-- Start a wiki table the same way each time, and then populate each row.
local wikiTable
if displayListOverride then
wikiTable = startList()
else
wikiTable = startNewWikiTable("Recipes for " .. productName .. ".")
end
-- Add the data retrieved to the wiki table
for _, match in pairs(targetRecipeMatches) do
for _, match in pairs(targetRecipeMatches) do
local recipe = match[1]
local recipe = match[1]
for _, building in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, building)
for _, workshop in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
end
end
end
end
Line 154: Line 175:


---
---
-- Renders the table for the specified building. The data retrieved from
-- Renders the table for the specified building.
-- RecipeData looks like this:
--
-- workshop,
-- table of matches {
-- match[recipeID] between recipe and workshops = {
-- recipe data { ... },
-- workshops that make it {
-- workshop data { ... }
-- }
-- },
-- match[recipeID] between recipe and workshops = {
-- recipe data { ... },
-- workshops that make it {
-- workshop data { ... }
-- }
-- }
-- }
--  
--  
-- @param buildingName the name of the building
-- @param buildingName the name of the building
-- @return the wikitext that renders the table
-- @return the wikitext that renders the table
function buildBuildingWikitext(buildingName)
function buildBuildingWikitext(buildingName, displayListOverride)
-- Ignore the workshop also returned.
local _, targetRecipeMatches = RecipeData.getAllRecipesAtBuilding(buildingName)
local _, targetRecipeMatches = RecipeData.getAllRecipesAtBuilding(buildingName)
if not targetRecipeMatches then
if not targetRecipeMatches then
error("Error retrieving recipes at building: " .. buildingName)
return "There are no recipes in the building " .. buildingName .. "."
end
end
local wikiTable = startNewWikiTable("Recipes in the " .. buildingName .. ".")
-- Start a wiki table the same way each time, and then populate each row.
local wikiTable
if displayListOverride then
wikiTable = startList()
else
wikiTable = startNewWikiTable("Recipes in the " .. buildingName .. ".")
end
-- Add the data retrieved to the wiki table
for _, match in pairs(targetRecipeMatches) do
for _, match in pairs(targetRecipeMatches) do
local recipe = match[1]
local recipe = match[1]
for _, building in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, building)
for _, workshop in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
end
end
end
end
Line 200: Line 215:


---
---
-- Renders the table for the specified product and building. The data returned
-- Renders the table for the specified product and building.
-- by RecipeData looks like this:
--
-- table of matches {
-- match[recipeID] between recipe and workshops = {
-- [1] = recipe data { ... }
-- [2] = workshops that make it {
-- workshop data { ... }
-- }
-- }
-- }
--  
--  
-- @param productName the name of the product
-- @param productName the name of the product
-- @param buildingName the name of the building
-- @param buildingName the name of the building
-- @return the wikitext that renders the table
-- @return the wikitext that renders the table
function buildProductBuildingWikitext(productName, buildingName)
function buildProductBuildingWikitext(productName, buildingName, displayListOverride)
local targetRecipeMatches = RecipeData.getOneRecipeAtBuilding(productName, buildingName)
local targetRecipeMatches = RecipeData.getOneRecipeAtBuilding(productName, buildingName)
if not targetRecipeMatches then
if not targetRecipeMatches then
error("Error retrieving the recipe for: " .. productName .. " (in the) " .. buildingName)
return "There is no recipe for product " .. productName .. " in the building " .. buildingName .. "."
end
end
local wikiTable = startNewWikiTable("Recipe for " .. productName .. " in the " .. buildingName .. ".")
-- Start a wiki table the same way each time, and then populate each row.
local wikiTable
if displayListOverride then
wikiTable = startList()
else
wikiTable = startNewWikiTable("Recipe for " .. productName .. " in the " .. buildingName .. ".")
end
-- Add the data retrieved to the wiki table
for _, match in pairs(targetRecipeMatches) do
for _, match in pairs(targetRecipeMatches) do
local recipe = match[1]
local recipe = match[1]
for _, building in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, building)
for _, workshop in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
end
end
end
end
Line 240: Line 255:


---
---
-- Renders the table for the specified ingredient. The data retrieved from
-- Renders the table for the specified ingredient.
-- RecipeData looks like this:
--
-- ingredient good data { ... },
-- table of matches {
-- match[recipeID] between recipe and workshops = {
-- recipe data { ... }
-- workshops that make it {
-- workshop data { ... }
-- workshop data { ... }
-- workshop data { ... }
-- }
-- }
-- match[recipeID] between recipe and workshops = {
-- ...
-- }
-- }
--
--
-- @param ingredientName the name of the ingredient
-- @param ingredientName the name of the ingredient
-- @return the wikitext that renders the table
-- @return the wikitext that renders the table
function buildIngredientWikitext(ingredientName)
function buildIngredientWikitext(ingredientName, displayListOverride)
-- Ignore the ingredient also returned
local _, targetRecipeMatches = RecipeData.getAllRecipesWithIngredient(ingredientName)
local _, targetRecipeMatches = RecipeData.getAllRecipesWithIngredient(ingredientName)
if not targetRecipeMatches then
if not targetRecipeMatches then
error("Error retrieving recipes that require ingredient: " .. ingredientName)
return "There are no recipes for ingredient " .. ingredientName .. "."
end
-- Start a wiki table the same way each time, and then populate each row.
local wikiTable
if displayListOverride then
wikiTable = startList()
else
wikiTable = startNewWikiTable("Recipes that require " .. ingredientName .. ".")
end
end
local wikiTable = startNewWikiTable("Recipes that require " .. ingredientName .. ".")
-- Add the data retrieved to the wiki table
 
for _, match in pairs(targetRecipeMatches) do
for _, match in pairs(targetRecipeMatches) do
local recipe = match[1]
local recipe = match[1]
for _, building in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, building)
for _, workshop in ipairs(match[2]) do
wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
end
end
end
end
Line 349: Line 359:
-- @param workshop the workshop to pull data from
-- @param workshop the workshop to pull data from
-- @return wikiTable, augmented with the new row
-- @return wikiTable, augmented with the new row
function addRowToTable(wikiTable, recipe, workshop)
function addRowToTable(wikiTable, recipe, workshop, displayListOverride)
if not wikiTable then
if not wikiTable then
Line 361: Line 371:
-- Get everything extracted and ready to go for easier code below
-- Get everything extracted and ready to go for easier code below
local buildingName = workshop[INDEX_WORKSHOP_NAME]
local buildingName = workshop[INDEX_WORKSHOP_NAME]
local buildingIsEssential = workshop[INDEX_WORKSHOP_INITIALLY_ESSENTIAL]
-- Do not add the extension here
local buildingIconFile = workshop[INDEX_WORKSHOP_ID] .. "_icon.png"
local buildingIconFile = workshop[INDEX_WORKSHOP_ID] .. "_icon"
local recipeGrade = recipe[INDEX_RECIPE_GRADE]
local recipeGrade = recipe[INDEX_RECIPE_GRADE]
Line 368: Line 378:
local recipeProductStackSize = recipe[INDEX_RECIPE_PRODUCT_STACK_SIZE]
local recipeProductStackSize = recipe[INDEX_RECIPE_PRODUCT_STACK_SIZE]
local recipeProductID = recipe[INDEX_RECIPE_PRODUCT_ID]
local recipeProductID = recipe[INDEX_RECIPE_PRODUCT_ID]
-- Now build the table tags. We're going to add everything to this row, so
-- Now build the table tags. We're going to add everything to this row, so
-- save the row itself to keep the hierarchy straight.
-- save the row itself to keep the hierarchy straight.
local wikiTableRow = wikiTable:tag('tr')
local wikiTableRow = wikiTable:tag('tr')
wikiTableRow:wikitext("\n")
wikiTableRow:newline()
-- Simple name and stars if display overriding
if displayListOverride then
wikiTableRow:tag("td")
:wikitext("[[" .. buildingName .. "]]" .. NBSP)
:wikitext("(" .. GRADE_TO_STARS[recipeGrade] .. ")" .. BR)
:allDone():newline()
return wikiTable
end
wikiTableRow:tag('td')
wikiTableRow:tag('td')
:wikitext(writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime))
:wikitext(writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime))
:done()
:done():newline()
wikiTableRow:wikitext("\n")
-- Skip empty cells for this recipe
-- Skip empty cells of ingredients in this recipe
local blanks = 0
local blanks = 0
for i, tableToCheck in ipairs(recipe[INDEX_RECIPE_INGREDIENTS]) do
for i, tableToCheck in ipairs(recipe[INDEX_RECIPE_INGREDIENTS]) do
Line 388: Line 405:
end
end
for i = 1,blanks do
for i = 1,blanks do
wikiTableRow:tag('td'):done()
wikiTableRow:tag('td'):done():newline()
wikiTableRow:wikitext("\n")
end
end
Line 398: Line 414:
-- Skip blank groups; we already inserted blanks earlier.
-- Skip blank groups; we already inserted blanks earlier.
if #optionsGroup > 0 then
if #optionsGroup > 0 then
local innerTable = wikiTableRow:tag('td'):tag('table')
local innerTable = wikiTableRow:tag('td'):tag('table')
innerTable:newline()
-- Loop through each option, but only up until they are blank
-- Loop through each option, but only up until they are blank
Line 415: Line 433:
end
end
wikiTableRow:tag('td')
local productCell, productName = writeProductCellContent(recipeProductID)
:wikitext(BOLD .. recipeProductStackSize .. BOLD .. writeProductCellContent(recipeProductID))
-- Sort on product name, not the stack size
wikiTableRow:tag('td'):attr("data-sort-value", productName)
:wikitext(BOLD .. recipeProductStackSize .. BOLD .. NBSP .. productCell)
:done()
:done()
wikiTableRow:wikitext("\n")
wikiTableRow:wikitext("\n")
Line 435: Line 456:
function writeIngredientCellContent(ingredientGoodID)
function writeIngredientCellContent(ingredientGoodID)
local goodName, goodIconFile = getGoodNameAndIcon(ingredientGoodID)
local goodName, goodIconFile = RecipeData.getGoodNameAndIcon(ingredientGoodID)
local iconMarkup = "[[File:" .. goodIconFile .. WIKI_RESOURCE_SMALL_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "]]"
local iconMarkup = "[[File:" .. goodIconFile .. WIKI_MED_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "#Product]]"
local linkMarkup = "[[" .. goodName .. "]]"
local linkMarkup = "[[" .. goodName .. "#Product|" .. goodName .. "]]"
return iconMarkup .. "&nbsp;" .. linkMarkup
return iconMarkup .. NBSP .. linkMarkup
end
end


Line 450: Line 471:
-- @param productGoodID the good ID to use to create the link
-- @param productGoodID the good ID to use to create the link
-- @return a string of wiki markup
-- @return a string of wiki markup
-- @return the string name of the product
function writeProductCellContent(productGoodID)
function writeProductCellContent(productGoodID)
local goodName, goodIconFile = getGoodNameAndIcon(productGoodID)
local goodName, goodIconFile = RecipeData.getGoodNameAndIcon(productGoodID)
local iconMarkup = "[[File:" .. goodIconFile .. WIKI_RESOURCE_LARGE_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "]]"
local iconMarkup = "[[File:" .. goodIconFile .. WIKI_LARGE_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "#Ingredient]]"
local linkMarkup = "[[" .. goodName .. "]]"
local linkMarkup = "[[" .. goodName .. "#Ingredient|" .. goodName .. "]]"
return iconMarkup .. linkMarkup
return iconMarkup .. NBSP .. linkMarkup, goodName
end
end


Line 473: Line 495:
function writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime)
function writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime)


local iconMarkup = "[[File:" .. buildingIconFile .. WIKI_BUILDING_ICON_MARKUP .. buildingName .. "|link=" .. buildingName .. "]]"
local iconMarkup = "[[File:" .. buildingIconFile .. WIKI_LARGE_ICON_MARKUP .. buildingName .. "|link=" .. buildingName .. "]]"
local linkMarkup = "[[" .. buildingName .. "]]"
local linkMarkup = "[[" .. buildingName .. "]]"
Line 504: Line 526:
---
---
-- Start all the tables the same way so they look the same
-- Start all the tables the same way so they look the same
-- @param the caption, if wanted
-- @param the caption, if wanted
-- @return the wikitext
-- @return the wikitext
function startNewWikiTable(caption)
function startNewWikiTable(caption)
local wikiTable = mw.html.create('table'):addClass(CSS_CLASS_WIKITABLE_SORTABLE)
local wikiTable = mw.html.create('table'):addClass(CSS_CLASS_WIKITABLE)
wikiTable:wikitext("\n")
wikiTable:newline()
if caption then
if caption then
wikiTable:tag('caption'):wikitext(caption):done()
wikiTable:tag('caption'):wikitext(caption):done():newline()
wikiTable:wikitext("\n")
end
end
wikiTable:tag('tr')
wikiTable:tag('tr')
:tag('th'):wikitext("Building"):done()
:tag('th'):wikitext("Building"):done()
:tag('th'):wikitext("Ingredient #1"):done()
:tag('th'):wikitext("Ingredient #1 options"):done()
:tag('th'):wikitext("Ingredient #2"):done()
:tag('th'):wikitext("Ingredient #2 options"):done()
:tag('th'):wikitext("Ingredient #3"):done()
:tag('th'):wikitext("Ingredient #3 options"):done()
:tag('th'):wikitext("Product"):done()
:tag('th'):wikitext("Product"):done()
:done():newline()
wikiTable:wikitext("\n")
return wikiTable
end
 
 
 
---
-- Start the simple list display
--
-- @return the wikitext
function startList()
local wikiTable = mw.html.create('table')
wikiTable:newline()
return wikiTable
return wikiTable

Latest revision as of 02:58, 12 November 2023

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

-------------------------------------------------------------------------------
-- This module renders the {{Recipe}} template with a nice wikitable.
-- https://hoodedhorse.com/wiki/Against_the_Storm/Template:Recipe.
--
-- The template #invokes Recipe.render(frame), with provided parameters.
-- The template requires at least one of its three parameters, or it will
-- return an error.
--
-- The first parameter `product` specifies that you want a table rendered to 
-- show all recipes that result in the specified product. Make sure it is 
-- spelled correctly, as it appears in the game.
--
-- The second parameter `building` species that you want a table rendered to 
-- show all recipes that are produced in the specified building. Again, make
-- sure it is spelled and puncutated (apostrophes) correctly.
--
-- The third, parameter `ingredient` species that you want a table rendered to
-- show all the recipes that take that ingredient to produce, whether 
-- specifically (like Wood for Planks) or as an option among many. This 
-- parameter is intended to be used by itself, without a product or building
-- named.
-- 
-- The data for the recipes is retrieved from Module:RecipeData, which hides the 
-- variations in the original data from this module that extracts only what's
-- needed to display one or more wikitables on a wiki page. Using the data, this
-- module creates an HTML table.
-- 
-- The table will have exactly one row if both the product and building are 
-- specified. If either is missing, then expect a table with multiple rows.
-- If an ingredient is specified, then the table will likely be very long.
-- @module Recipe
local Recipe = {}



---
-- Constants
--
-- Currently this is the max number of ingredients a recipe can have
local RECIPE_INGRED_MAX = 3
-- Indexes to use for recipe records as they come from the CSV data
local INDEX_RECIPE_GRADE = 2
local INDEX_RECIPE_PRODTIME = 3
local INDEX_RECIPE_PRODUCT_STACK_SIZE = 4
local INDEX_RECIPE_PRODUCT_ID = 5
local INDEX_RECIPE_INGREDIENTS = 6
-- Indexes to use for workshop records as they come from the CSV data
local INDEX_WORKSHOP_ID = 1
local INDEX_WORKSHOP_NAME = 2

-- CSS styling for the recipe table... for now
local CSS_CLASS_WIKITABLE = "wikitable sortable mw-collapsible"
-- Shortcut markup to go after the filename to add the extension and display parameters
local WIKI_MED_ICON_MARKUP = ".png|24px|alt="
local WIKI_LARGE_ICON_MARKUP = ".png|42px|alt="
-- Shortcut markup that's easier for Lua string concatenations
local BR = "<br />"
local NBSP = "&nbsp;"
local BOLD = "'''"
-- Shortcuts for displaying the efficency grades as stars or the "low meter"
local GRADE_TO_STARS = {
	["Grade0"] = "[[File:Grade Stars 0.png|12px|link=|alt=0 Stars]]",
	["Grade1"] = "★",
	["Grade2"] = "★★",
	["Grade3"] = "★★★"
}



---
-- Member variables
---
local RecipeData = require('Module:RecipeData')



---
-- Called from Template:Recipe to render a table displaying the information.
-- Retrieves data from Module:RecipeData. The data retrieved looks like this:
-- 
-- (sometimes there's an extra first variable like workshop or ingredient)
-- targetRecipeMatches = {
-- 		[recipeID] match between recipe and workshops = {
--			[1] = { recipe data },
--			[2] = list of workshops that make it = {
--				{ workshop data },
--				{ workshop data },
--				{ workshop data }
--			}
--		},
--		[recipeID] match between recipe and workshops = {
--			similar
-- 		}
-- }
--
-- @param frame the template passes parameters and page data via frame
-- @return the formatted wikitext/HTML
function Recipe.render(frame)
	
	-- Extract the passed 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 display = frame.args.display
	local displayListOverride = false
	if display == "list" then
		displayListOverride = true
	end
	
	-- At least one parameter is required.
	if productName == "" and buildingName == "" and ingredientName == "" then
        return "The Recipe template requires that you specify at least one of the following: product, building, or ingredient."
    end
	
	-- Decide according to parameters
	if ingredientName and ingredientName ~= "" then
		return buildIngredientWikitext(ingredientName, displayListOverride)
	else
		if productName and productName ~= "" and buildingName and buildingName ~= "" then
			return buildProductBuildingWikitext(productName, buildingName, displayListOverride)
		else
			if productName and productName ~= "" then
				return buildProductWikitext(productName, displayListOverride)
			else
				if buildingName and buildingName ~= "" then
					return buildBuildingWikitext(buildingName, displayListOverride)
				else
					return "Error in Recipe template: Unknown parameter error."
				end
			end
		end
	end
end



---
-- Renders the table for the specified product.
--
-- @param productName the name of the product
-- @return the wikitext that renders the table
function buildProductWikitext(productName, displayListOverride)
	
	local targetRecipeMatches = RecipeData.getAllRecipesForProduct(productName)
	
	if not targetRecipeMatches then
		return "There are no recipes for product " .. productName .. "."
	end
	
	-- Start a wiki table the same way each time, and then populate each row.
	local wikiTable
	if displayListOverride then
		wikiTable = startList()
	else
		wikiTable = startNewWikiTable("Recipes for " .. productName .. ".")
	end
	
	-- Add the data retrieved to the wiki table
	for _, match in pairs(targetRecipeMatches) do
	
		local recipe = match[1]
		
		for _, workshop in ipairs(match[2]) do
			
			wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
		end
	end
	
	wikiTable = endWikiTable(wikiTable)

	return wikiTable
end



---
-- Renders the table for the specified building.
-- 
-- @param buildingName the name of the building
-- @return the wikitext that renders the table
function buildBuildingWikitext(buildingName, displayListOverride)
	
	-- Ignore the workshop also returned.
	local _, targetRecipeMatches = RecipeData.getAllRecipesAtBuilding(buildingName)
	
	if not targetRecipeMatches then
		return "There are no recipes in the building " .. buildingName .. "."
	end
	
	-- Start a wiki table the same way each time, and then populate each row.
	local wikiTable
	if displayListOverride then
		wikiTable = startList()
	else
		wikiTable = startNewWikiTable("Recipes in the " .. buildingName .. ".")
	end
	
	-- Add the data retrieved to the wiki table
	for _, match in pairs(targetRecipeMatches) do
	
		local recipe = match[1]
		
		for _, workshop in ipairs(match[2]) do
		
			wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
		end
	end
	
	wikiTable = endWikiTable(wikiTable)

	return wikiTable
end



---
-- Renders the table for the specified product and building.
-- 
-- @param productName the name of the product
-- @param buildingName the name of the building
-- @return the wikitext that renders the table
function buildProductBuildingWikitext(productName, buildingName, displayListOverride)
	
	local targetRecipeMatches = RecipeData.getOneRecipeAtBuilding(productName, buildingName)
	
	if not targetRecipeMatches then
		return "There is no recipe for product " .. productName .. " in the building " .. buildingName .. "."
	end
	
	-- Start a wiki table the same way each time, and then populate each row.
	local wikiTable
	if displayListOverride then
		wikiTable = startList()
	else
		wikiTable = startNewWikiTable("Recipe for " .. productName .. " in the " .. buildingName .. ".")
	end
	
	-- Add the data retrieved to the wiki table
	for _, match in pairs(targetRecipeMatches) do
	
		local recipe = match[1]
		
		for _, workshop in ipairs(match[2]) do
		
			wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
		end
	end
	
	wikiTable = endWikiTable(wikiTable)

	return wikiTable
end



---
-- Renders the table for the specified ingredient.
--
-- @param ingredientName the name of the ingredient
-- @return the wikitext that renders the table
function buildIngredientWikitext(ingredientName, displayListOverride)
	
	-- Ignore the ingredient also returned
	local _, targetRecipeMatches = RecipeData.getAllRecipesWithIngredient(ingredientName)
	
	if not targetRecipeMatches then
		return "There are no recipes for ingredient " .. ingredientName .. "."
	end
	
	-- Start a wiki table the same way each time, and then populate each row.
	local wikiTable
	if displayListOverride then
		wikiTable = startList()
	else
		wikiTable = startNewWikiTable("Recipes that require " .. ingredientName .. ".")
	end
	
	-- Add the data retrieved to the wiki table
	for _, match in pairs(targetRecipeMatches) do
	
		local recipe = match[1]
		
		for _, workshop in ipairs(match[2]) do
		
			wikiTable = addRowToTable(wikiTable, recipe, workshop, displayListOverride)
		end
	end
	
	wikiTable = endWikiTable(wikiTable)

	return wikiTable
end



---
-- Adds a row to the existing wiki table based on the recipe and building 
-- provided.
--
-- The structures look like this:
-- recipe {
-- 		id
--		efficiency grade
--		production time
--		product stack size
--		product good's id
--		ingredients {
--			options for ingredient #1 {
--				{ option #1 good's stack size, option #1 good's id }
--				{ option #2 good's stack size, option #2 good's id }
--				{ option #3 good's stack size, option #3 good's id }
--				etc.
--			}
--			options for ingredient #2 {
--				same as above
--			}
--			options for ingredient #3 {
--				same as above
--			}
--		}
-- }
--
-- workshop {
-- 		id
--		display name
--		description
--		category
--		size x
--		size y
--		city score
--		movable
--		initially essential
--		storage
--		construction time
--		required goods {
--			required good #1 stack size
--			required good #1 id
--			required good #2 stack size
--			required good #2 id
--			required good #3 stack size
--			required good #3 id
--		}
--		workplaces {
--			workplace #1
--			workplace #2
--			workplace #3
--			workplace #4
--		}
--		recipes {
--			recipe #1
--			recipe #2
--			recipe #3
--			recipe #4
--		}
-- }
--
-- @param wikiTable the table to continue adding to
-- @param recipe the recipe to pull data from
-- @param workshop the workshop to pull data from
-- @return wikiTable, augmented with the new row
function addRowToTable(wikiTable, recipe, workshop, displayListOverride)
	
	if not wikiTable then
		error("Wikitable in-progress is not set up correctly.")
	else
		if not recipe or not workshop then
			error("Recipe and workshop to display not set up correctly.")
		end
	end
	
	-- Get everything extracted and ready to go for easier code below
	local buildingName = workshop[INDEX_WORKSHOP_NAME]
	-- Do not add the extension here
	local buildingIconFile = workshop[INDEX_WORKSHOP_ID] .. "_icon"
	
	local recipeGrade = recipe[INDEX_RECIPE_GRADE]
	local recipeProdTime = recipe[INDEX_RECIPE_PRODTIME]
	local recipeProductStackSize = recipe[INDEX_RECIPE_PRODUCT_STACK_SIZE]
	local recipeProductID = recipe[INDEX_RECIPE_PRODUCT_ID]
	
	-- Now build the table tags. We're going to add everything to this row, so
	-- save the row itself to keep the hierarchy straight.
	local wikiTableRow = wikiTable:tag('tr')
	wikiTableRow:newline()
	
	-- Simple name and stars if display overriding
	if displayListOverride then
		wikiTableRow:tag("td")
			:wikitext("[[" .. buildingName .. "]]" .. NBSP)
			:wikitext("(" .. GRADE_TO_STARS[recipeGrade] .. ")" .. BR)
		:allDone():newline()
		return wikiTable
	end
	
	wikiTableRow:tag('td')
		:wikitext(writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime))
	:done():newline()
	
	-- Skip empty cells of ingredients in this recipe
	local blanks = 0
	for i, tableToCheck in ipairs(recipe[INDEX_RECIPE_INGREDIENTS]) do
		if #tableToCheck == 0 then
			blanks = blanks + 1
		end
	end
	for i = 1,blanks do
		wikiTableRow:tag('td'):done():newline()
	end
	
	-- Then fill in the rest with the remaining ingredients, with inner tables
	-- to keep things aligned well
	for _, optionsGroup in ipairs(recipe[INDEX_RECIPE_INGREDIENTS]) do
		
		-- Skip blank groups; we already inserted blanks earlier.
		if #optionsGroup > 0 then
			
			local innerTable = wikiTableRow:tag('td'):tag('table')
			innerTable:newline()
			
			-- Loop through each option, but only up until they are blank
			for _, option in ipairs(optionsGroup) do
			
				local ingredientStackSize = option[1]
				local ingredientGoodID = option[2]
				
				innerTable:tag('tr')
					:tag('td'):wikitext(BOLD .. ingredientStackSize .. BOLD):done()
					:tag('td'):wikitext(writeIngredientCellContent(ingredientGoodID)):done()
				:done()
				innerTable:wikitext("\n")
			end
		end
	end
	
	local productCell, productName = writeProductCellContent(recipeProductID)
	
	-- Sort on product name, not the stack size
	wikiTableRow:tag('td'):attr("data-sort-value", productName)
		:wikitext(BOLD .. recipeProductStackSize .. BOLD .. NBSP .. productCell)
	:done()
	wikiTableRow:wikitext("\n")
	wikiTableRow:done()
	
	wikiTable:wikitext("\n")
	
	return wikiTable
end



---
-- Writes an icon and link to a resource page.
--
-- @param ingredientGoodID the good ID to use to create the link
-- @return a string of wiki markup
function writeIngredientCellContent(ingredientGoodID)
	
	local goodName, goodIconFile = RecipeData.getGoodNameAndIcon(ingredientGoodID)
	
	local iconMarkup = "[[File:" .. goodIconFile .. WIKI_MED_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "#Product]]"
	local linkMarkup = "[[" .. goodName .. "#Product|" .. goodName .. "]]"
	
	return iconMarkup .. NBSP .. linkMarkup
end



---
-- Writes an icon and link to a resource page.
--
-- @param productGoodID the good ID to use to create the link
-- @return a string of wiki markup
-- @return the string name of the product
function writeProductCellContent(productGoodID)
	
	local goodName, goodIconFile = RecipeData.getGoodNameAndIcon(productGoodID)
	
	local iconMarkup = "[[File:" .. goodIconFile .. WIKI_LARGE_ICON_MARKUP .. goodName .. "|link=" .. goodName .. "#Ingredient]]"
	local linkMarkup = "[[" .. goodName .. "#Ingredient|" .. goodName .. "]]"
	
	return iconMarkup .. NBSP .. linkMarkup, goodName
end



---
-- Writes the contents of the building cell and returns a string to be used 
-- in a call to wikitext()
--
-- @param buildingIconFile icon filename, excluding 'File:', including extension
-- @param buildingName plain text name of the building to display
-- @param recipeGrade efficiency of the recipe
-- @param recipeProdTime production time in seconds
-- @return a string of wiki markup
function writeBuildingCellContent(buildingIconFile, buildingName, recipeGrade, recipeProdTime)

	local iconMarkup = "[[File:" .. buildingIconFile .. WIKI_LARGE_ICON_MARKUP .. buildingName .. "|link=" .. buildingName .. "]]"
	local linkMarkup = "[[" .. buildingName .. "]]"
	
	local grade = GRADE_TO_STARS[recipeGrade]
	if not grade then
		error("Incorrectly formatted efficiency grade: " .. recipeGrade)
	end
	
	local innerTable = mw.ustring.format("<table><tr><td>%s</td><td>%s<br />%s<br />%s</td></tr></table>", iconMarkup, linkMarkup, grade, formatTime(recipeProdTime))
	
	return innerTable
end



---
-- Formats seconds as mm:ss format, with leading zeros where necessary.
--
-- @param seconds number of seconds to reformat
-- @return a string representing mm:ss time format
function formatTime(seconds)

	local minutes = math.floor(seconds/60)
	local remainingSeconds = seconds % 60
	return string.format("%02d:%02d", minutes, remainingSeconds)
end



---
-- Start all the tables the same way so they look the same

-- @param the caption, if wanted
-- @return the wikitext
function startNewWikiTable(caption)
	
	local wikiTable = mw.html.create('table'):addClass(CSS_CLASS_WIKITABLE)
	wikiTable:newline()
	
	if caption then
		wikiTable:tag('caption'):wikitext(caption):done():newline()
	end
	
	wikiTable:tag('tr')
		:tag('th'):wikitext("Building"):done()
		:tag('th'):wikitext("Ingredient #1 options"):done()
		:tag('th'):wikitext("Ingredient #2 options"):done()
		:tag('th'):wikitext("Ingredient #3 options"):done()
		:tag('th'):wikitext("Product"):done()
	:done():newline()
	
	return wikiTable
end



---
-- Start the simple list display
-- 
-- @return the wikitext
function startList()
	
	local wikiTable = mw.html.create('table')
	wikiTable:newline()
	
	return wikiTable
end



---
-- Finishes the wikitext by closing out remaining tags.
--
-- @paramn wikiTable the table to close out
-- @return the wikitext to display
function endWikiTable(wikiTable)
	
	-- There might be more later.
	wikiTable:done()
	
	return wikiTable
end



return Recipe