Module:OrdersController: Difference between revisions

From Against the Storm Official Wiki
In progress
 
Merge from OrdersController2 to implement filtering by settlement difficulty
 
(20 intermediate revisions by the same user not shown)
Line 20: Line 20:
local ARG_ID_LIST = "id"
local ARG_ID_LIST = "id"
local ARG_NAME_LIST = "name"
local ARG_NAME_LIST = "name"
local ARG_REQUIREMENTS_LIST = "requirements"
local ARG_REWARDS_LIST = "rewards"
local ARG_DESCRIPTION_LIST = "description"
local ARG_DESCRIPTION_LIST = "description"
local ARG_SEARCH_ALL_LIST = "search"
local ARG_SEARCH_ALL_LIST = "search"
local ARG_TIER = "tier"
local ARG_TIER = "tier"
local ARG_IS_TIMED = "timed"
local ARG_TIMED = "timed"
local ARG_VALID_DIFFICULTIES = "difficulty"
local ARG_DIFFICULTY = "difficulty"
local ARG_SETTLEMENT_DIFFICULTY = "settlement"
local ARG_EXCLUDE_LIST = "exclude"
local ARG_EXCLUDE_LIST = "exclude"
local ARG_CAPTION = "caption"
local ARG_CAPTION = "caption"
Line 33: Line 36:
local ARG_DISPLAY_OVERRIDE_OPTION_INLINE = "inline"
local ARG_DISPLAY_OVERRIDE_OPTION_INLINE = "inline"
local ARG_LIST_TYPE = "list_type"
local ARG_LIST_TYPE = "list_type"
local ARG_COLLAPSED = "collapsed"


local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
Line 69: Line 73:
---@param difficultyToCheck string a difficulty to filter to, needs to be normalized
---@param difficultyToCheck string a difficulty to filter to, needs to be normalized
---@param excludeList table an array of IDs that should not be added
---@param excludeList table an array of IDs that should not be added
local function loadOrderIDs(newIDs, tierToCheck, timedToCheck, difficultyToCheck, excludeList)
local function loadOrderIDs(newIDs, tierToCheck, timedToCheck, difficultyToCheck, settlementToCheck, excludeList)


     for _, id in ipairs(newIDs) do
     for _, id in ipairs(newIDs) do
Line 78: Line 82:
             local isTierMatch = not tierToCheck or "" == tierToCheck or OrdersData.isTierMatchByID(id, tierToCheck)
             local isTierMatch = not tierToCheck or "" == tierToCheck or OrdersData.isTierMatchByID(id, tierToCheck)
             local isTimedMatch = not timedToCheck or "" == timedToCheck or OrdersData.isTimedMatchByID(id, timedToCheck)
             local isTimedMatch = not timedToCheck or "" == timedToCheck or OrdersData.isTimedMatchByID(id, timedToCheck)
             local isDifficultyMatch = not difficultyToCheck or "" == difficultyToCheck or OrdersData.isAnySource(id, difficultyToCheck)
             local isDifficultyMatch = not difficultyToCheck or "" == difficultyToCheck or OrdersData.isDifficultyMatchByID(id, difficultyToCheck)
            local isSettlementMatch = not settlementToCheck or "" == settlementToCheck or OrdersData.isSettlementMatchByID(id, settlementToCheck)


             if isTierMatch and isTimedMatch and isDifficultyMatch then
             if isTierMatch and isTimedMatch and isDifficultyMatch and isSettlementMatch then
                 table.insert(orderIDs, id)
                 table.insert(orderIDs, id)
                 isIDAlreadyAdded[id:lower()] = true
                 isIDAlreadyAdded[id:lower()] = true
Line 88: Line 93:
end
end


--- Requests the desired lists of IDs from Perks data models, applies some post-processing, and stores them in the member variable perkIDs.
--- Requests the desired lists of IDs from Orders data models, applies some post-processing, and stores them in the member variable orderIDs.
---
---
---@param selectList table array of ids of the cornerstone to include
---@param selectList table array of ids of the cornerstone to include
Line 96: Line 101:
---@param source string the source of cornerstones
---@param source string the source of cornerstones
---@param excludeList table list of ids
---@param excludeList table list of ids
local function loadIDs(selectList, searchNameList, searchDescriptionsList, rarity, source, excludeList)
local function loadIDs(selectList, searchNameList, searchRequirementsList, searchRewardsList, tier, timed, difficulty, settlement, excludeList)


     perkIDs = {}
     orderIDs = {}
     isIDAlreadyAdded = {}
     isIDAlreadyAdded = {}
     local attemptedSearch = false
     local attemptedSearch = false
Line 104: Line 109:
     -- Load the selected IDs
     -- Load the selected IDs
     if selectList then
     if selectList then
         loadPerkIDs(selectList, rarity, source, excludeList)
         loadOrderIDs(selectList, tier, timed, difficulty, settlement, excludeList)
         -- It should always be true, but it can be blank.
         -- It should always be true, but it can be blank.
         if #selectList > 0 then
         if #selectList > 0 then
Line 114: Line 119:
     if searchNameList and #searchNameList > 0 then
     if searchNameList and #searchNameList > 0 then
         for _, searchName in ipairs(searchNameList) do
         for _, searchName in ipairs(searchNameList) do
             local newIDs = PerksData.getAllPerkIDsWhereName(searchName)
             local newIDs = OrdersData.getAllOrdersIDsWhereName(searchName)
             loadPerkIDs(newIDs, rarity, source, excludeList)
             loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
             attemptedSearch = true
             attemptedSearch = true
         end
         end
     end
     end
 
   
     -- Add any description matches
     -- Add any requirements matches
     if searchDescriptionsList and #searchDescriptionsList > 0 then
     if searchRequirementsList and #searchRequirementsList > 0 then
         for _, searchDescription in ipairs(searchDescriptionsList) do
         for _, searchRequirements in ipairs(searchRequirementsList) do
             local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDescription)
             local newIDs = OrdersData.getAllOrdersIDsWhereRequirements(searchRequirements)
             loadPerkIDs(newIDs, rarity, source, excludeList)
             loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
             attemptedSearch = true
             attemptedSearch = true
         end
         end
     end
     end
 
   
     -- Or, if no searching was attempted (regardless of result), show all from the selected rarities and sources. No need to do this more expensive call unless it's the only filtering, because loadPerkIDs already filters out by rarity and source while looping.
     -- Add any rewards matches
     if not attemptedSearch then
     if searchRewardsList and #searchRewardsList > 0 then
         if (rarityFilter and "" ~= rarityFilter) or (sourceFilter and "" ~= sourceFilter) then
         for _, searchRewards in ipairs(searchRewardsList) do
             local newIDs = PerksData.getAllPerkIDsFilteredByRarityAndSource(rarity, source)
             local newIDs = OrdersData.getAllOrdersIDsWhereRewards(searchRewards)
             loadPerkIDs(newIDs, rarity, source, excludeList)
             loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
             attemptedSearch = true
             attemptedSearch = true
         end
         end
Line 140: Line 145:
     -- Or, if no searching was even attempted (regardless of result), show them all.
     -- Or, if no searching was even attempted (regardless of result), show them all.
     if not attemptedSearch then
     if not attemptedSearch then
         local newIDs = PerksData.getAllPerkIDs()
         local newIDs = OrdersData.getAllOrderIDs()
         loadPerkIDs(newIDs, rarity, source, excludeList)
         loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
     end
     end


    if #perkIDs > 0 then
     return orderIDs
        -- Alphabetize by name.
        table.sort(perkIDs, PerksData.compareNames)
    end
 
     return perkIDs
end
end


Line 157: Line 157:
---
---
---@param desiredCaption string the caption requested, if any
---@param desiredCaption string the caption requested, if any
---@param selectList table array of ids of the perks to include
---@param selectList table array of ids of the orders to include
---@param searchNameList table array of search criteria for names
---@param searchNameList table array of search criteria for names
---@param searchDescriptionList table array of search criteria for descriptions
---@param searchDescriptionList table array of search criteria for descriptions
Line 164: Line 164:
---@param excludeList table array of ids that were excluded
---@param excludeList table array of ids that were excluded
---@return string an appropriate caption, or blank if it should be default
---@return string an appropriate caption, or blank if it should be default
local function resolveCaption(desiredCaption, selectList, searchNameList, searchDescriptionList, rarity, source, excludeList)
local function resolveCaption(desiredCaption, selectList, searchNameList, tier, timed, difficulty, settlement, excludeList)


     -- No matter what, if the author provided a caption, use that.
     -- No matter what, if the author provided a caption, use that.
Line 173: Line 173:
     -- If no selection, delegate to template.
     -- If no selection, delegate to template.
     local addedToText = false
     local addedToText = false
    desiredCaption = ""
if not settlement or settlement == "" then
    desiredCaption = desiredCaption .. #orderIDs
    end


     desiredCaption = "" .. #perkIDs
     if timed and timed ~= "" and timed == "true" then
        desiredCaption = desiredCaption .. " Timed"
        addedToText = true
    end
   
    if timed and timed ~= "" and timed == "false" then
        desiredCaption = desiredCaption .. " Non-Timed"
        addedToText = true
    end


     if rarity and rarity ~= "" then
     if tier and tier ~= "" then
         desiredCaption = desiredCaption .. " " .. rarity .. " Perk"
         desiredCaption = desiredCaption .. " Tier " .. tier .. " Order"
         addedToText = true
         addedToText = true
     else
     else
         desiredCaption = desiredCaption .. " Perk"
         desiredCaption = desiredCaption .. " Order"
         -- did not addedToText, this is just default
         -- did not addedToText, this is just default
     end
     end


     if #perkIDs > 1 then
     if #orderIDs > 1 then
         desiredCaption = desiredCaption .. "s"
         desiredCaption = desiredCaption .. "s"
    end
    if source and source ~= "" then
        desiredCaption = desiredCaption .. " from " .. source .. "s"
        addedToText = true
     end
     end


Line 214: Line 222:
     end
     end


     if searchDescriptionList and #searchDescriptionList > 0 then
     if difficulty and difficulty ~= "" then
         if addedToText then
         desiredCaption = desiredCaption .. " of difficulty " .. difficulty
            desiredCaption = desiredCaption .. " and"
         addedToText = true
         end
    end
        if #searchDescriptionList == 1 and searchDescriptionList[1] then
 
            desiredCaption = desiredCaption .. " mentioning '" .. searchDescriptionList[1] .. "'"
    if settlement and settlement ~= "" then
        else
        desiredCaption = desiredCaption .. " that can be drawn on " .. settlement .. " difficulty"
            desiredCaption = desiredCaption .. " by searching descriptions"
        end
         addedToText = true
         addedToText = true
     end
     end
Line 242: Line 248:
---
---
---@param desiredCaption string the caption for the table, if any
---@param desiredCaption string the caption for the table, if any
---@param viewParameters table a table of parameters made by and for PerksView
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled so far by the view module
---@return string the wiki markup assembled so far by the view module
local function makeMarkupForTableStart(desiredCaption, viewParameters)
local function makeMarkupForTableStart(desiredCaption, viewParameters, collapsed)


     return PerksView.startTable(desiredCaption, viewParameters)
     return OrdersView.startTable(desiredCaption, viewParameters, collapsed)
end
end


--- Calls the view to render a single row of the table with data based on the provided identifier.
--- Calls the view to render a single row of the table with data based on the provided identifier.
---
---
---@param id string the unique identifier of a Perk or Cornerstone to use to add data to a new table row
---@param id string the unique identifier of a Order to use to add data to a new table row
---@param viewParameters table a table of parameters made by and for PerksView
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled so far by the view module
---@return string the wiki markup assembled so far by the view module
local function makeMarkupPerRow(id, viewParameters)
local function makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, totalRows)


     local name = PerksData.getNameByID(id)
     local name = OrdersData.getNameByID(id)
     local rarity = PerksData.getRarityByID(id)
     local tier = OrdersData.getTiers(id)[1]
     local description = ControllerUtilities.removeEnclosingDoubleQuotes(ControllerUtilities.findAndReplaceSpriteTagsWithFiles(PerksData.getDescriptionByID(id), currentFrame))
     local timed = OrdersData.getTimed(id)
    local timeLimit = OrdersData.getTimeLimit(id)
    local difficulty = OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow)
    local requirements = ControllerUtilities.findAndReplaceSpriteTagsWithFiles(OrdersData.getFormattedRequirementsByIDAndRowNum(id, difficultyRow), currentFrame)
    local rewards = ControllerUtilities.findAndReplaceSpriteTagsWithFiles(OrdersData.getRewardsFormattedByID(id), currentFrame)
    local rowSpan = totalRows


    local isSourceAltar = PerksData.isFromAltarByID(id) and DISPLAY_YES
    local isSourceCornerstone = PerksData.isFromCornerstoneByID(id) and DISPLAY_YES
    local isSourceOrder = PerksData.isFromOrderByID(id) and DISPLAY_YES
    local isSourceRelic = PerksData.isFromEventByID(id) and DISPLAY_YES
    local isSourceTrader = PerksData.isFromTraderByID(id) and DISPLAY_YES
    local isSourceDecoration = PerksData.isFromDecorationByID(id) and DISPLAY_YES
    local isSourcePort = PerksData.isFromPortByID(id) and DISPLAY_YES
    local isSourceRacial = PerksData.isFromRacialByID(id) and DISPLAY_YES
    local isSourceInstitution = PerksData.isFromInstitutionByID(id) and DISPLAY_YES
    local isSourceBiome = PerksData.isFromBiomeByID(id) and DISPLAY_YES


    local price = PerksData.getPriceByID(id)
     return OrdersView.addRow(id, name, tier, timed, timeLimit, difficulty, requirements, rewards, viewParameters, isExtraRow, rowSpan)
 
     return PerksView.addRow(id, name, rarity, description, isSourceAltar, isSourceCornerstone, isSourceOrder, isSourceRelic, isSourceTrader, isSourceDecoration, isSourcePort, isSourceRacial, isSourceInstitution, isSourceBiome, price, viewParameters)
end
end


--- Handles assembling the rows for all of the IDs in the member variable list altarIDs.
--- Handles assembling the rows for all of the IDs in the member variable list altarIDs.
---
---
---@param viewParameters table a table of parameters made by and for PerksView
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled by the view (other templates)
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableRows(viewParameters)
local function makeMarkupForTableRows(viewParameters, specifyDifficulty, difficulty, settlement)


     for _, id in ipairs(perkIDs) do
if specifyDifficulty then
        makeMarkupPerRow(id, viewParameters)
local isExtraRow = false
local case = ""
if difficulty and settlement then
case = "both"
elseif difficulty then
case = "difficultyOnly"
elseif settlement then
case = "settlementOnly"
else
error("Impossible parameter combination: specifyDifficulty is true but difficulty and settlement are nil.")
end
    for _, id in ipairs(orderIDs) do
    isExtraRow = false
    local orderTier = OrdersData.getTiers(id)
    if case == "both" then
    local totalRows = OrdersData.getNumberOfDifficulties(id)
        for difficultyRow = 1, totalRows do
        if (OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow)):find(difficulty) and OrdersData.isSettlementMatchSpecificOrder(OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow), orderTier, settlement) then
        makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, 1)
        end
        end
    end
    if case == "difficultyOnly" then
    local totalRows = OrdersData.getNumberOfDifficulties(id)
        for difficultyRow = 1, totalRows do
        if (OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow)):find(difficulty) then
        makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, 1)
        end
        end
    end
    if case == "settlementOnly" then
    local totalRows = OrdersData.getNumberOfDifficulties(id)
        for difficultyRow = 1, totalRows do
        if OrdersData.isSettlementMatchSpecificOrder(OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow), orderTier, settlement) then
        makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, 1)
        end
        end
        end
    end
     else
    local isExtraRow = false
    for _, id in ipairs(orderIDs) do
    isExtraRow = false
    local totalRows = OrdersData.getNumberOfDifficulties(id)
        for i = 1, totalRows do
        makeMarkupPerRow(id, viewParameters, i, isExtraRow, totalRows)
        isExtraRow = true
        end
    end
     end
     end
end
end
Line 292: Line 339:
---
---
---@param desiredCaption string the caption for the table
---@param desiredCaption string the caption for the table
---@param viewParameters table a table of parameters made by and for PerksView
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled by the view (other templates)
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(desiredCaption, viewParameters)
local function renderTable(desiredCaption, viewParameters, specifyDifficulty, collapsed, difficulty, settlement)


     makeMarkupForTableStart(desiredCaption, viewParameters)
     makeMarkupForTableStart(desiredCaption, viewParameters, collapsed)
     makeMarkupForTableRows(viewParameters)
     makeMarkupForTableRows(viewParameters, specifyDifficulty, difficulty, settlement)


     return PerksView.finalize()
     return OrdersView.finalize()
end
end


Line 325: Line 372:


     local markup = ""
     local markup = ""
     for _, id in ipairs(perkIDs) do
     for _, id in ipairs(orderIDs) do
         markup = markup .. makeMarkupPerListItem(id, listType, isShowingID)
         markup = markup .. makeMarkupPerListItem(id, listType, isShowingID)
     end
     end
Line 367: Line 414:


     local markup = ""
     local markup = ""
     for i, id in ipairs(perkIDs) do
     for i, id in ipairs(orderIDs) do
         if i > 1 then
         if i > 1 then
             markup = markup .. ", "
             markup = markup .. ", "
Line 391: Line 438:
--region Public methods
--region Public methods


function PerksController.main(frame)
function OrdersController.main(frame)


     -- Selection parameters, normalized for exclude list
     -- Selection parameters, normalized for exclude list
Line 397: Line 444:
     local excludeList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_EXCLUDE_LIST], true, true)
     local excludeList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_EXCLUDE_LIST], true, true)
     local searchNameList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_NAME_LIST], false)
     local searchNameList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_NAME_LIST], false)
     local searchDescriptionList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_DESCRIPTION_LIST], false)
     local searchRequirementsList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_REQUIREMENTS_LIST], false)
    local searchRewardsList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_REWARDS_LIST], false)
     local searchAllList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_SEARCH_ALL_LIST], false)
     local searchAllList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_SEARCH_ALL_LIST], false)
    local tierFilter = OrdersData.normalizeTierText(frame.args[ARG_TIER])
    local timedFilter = OrdersData.normalizeTimedText(frame.args[ARG_TIMED])
    local difficultyFilter = OrdersData.normalizeDifficultyText(frame.args[ARG_DIFFICULTY])
    local settlementFilter = OrdersData.normalizeSettlementDifficultyText(frame.args[ARG_SETTLEMENT_DIFFICULTY])


    local rarityFilter = PerksData.normalizeRarityText(frame.args[ARG_RARITY])
---- SettlementFilter should be used independently of other difficulty filter, error if difficulty filter is applied.
    local sourceFilter = PerksData.normalizeSourceText(frame.args[ARG_SOURCE])
if settlementFilter then
 
if difficultyFilter then
     -- Substitute the search all terms for name or description, if they weren't provided but search-all was.
error("Order difficulty filtering cannot be applied when filtering by settlement difficulty.")
end
end
-- If a difficulty filter or settlement difficulty filter is applied, then only the relevant difficulties should be shown
if difficultyFilter or settlementFilter then
specifyDifficulty = true
end
     -- Substitute the search all terms for each non-provided list.
     if searchAllList and #searchAllList > 0 then
     if searchAllList and #searchAllList > 0 then
         if #searchNameList < 1 then
         if #searchNameList < 1 then
             searchNameList = searchAllList
             searchNameList = searchAllList
         end
         end
         if #searchDescriptionList < 1 then
         if #searchRequirementsList < 1 then
             searchDescriptionList = searchAllList
             searchRequirementsList = searchAllList
        end
        if #searchRewardsList < 1 then
            searchRequirementsList = searchAllList
         end
         end
     end
     end
Line 418: Line 482:
     local listType = frame.args[ARG_LIST_TYPE]
     local listType = frame.args[ARG_LIST_TYPE]
     local isShowingID = ARG_SHOW_FLAG_VALUE == frame.args[ARG_SHOW_ID]
     local isShowingID = ARG_SHOW_FLAG_VALUE == frame.args[ARG_SHOW_ID]
    local collapsed = frame.args[ARG_COLLAPSED]


     -- Let the View module itself determine how to use the template parameters relating to the view.
     -- Let the View module itself determine how to use the template parameters relating to the view.
     local viewParameters = PerksView.constructViewParametersFromTemplateFrame(frame)
     local viewParameters = OrdersView.constructViewParametersFromTemplateFrame(frame)


     currentFrame = frame
     currentFrame = frame


     loadIDs(selectList, searchNameList, searchDescriptionList, rarityFilter, sourceFilter, excludeList)
     loadIDs(selectList, searchNameList, searchRequirementsList, searchRewardsList, tierFilter, timedFilter, difficultyFilter, settlementFilter, excludeList)
     if #perkIDs < 1 then
 
         return "No matching Perks found."
     if #orderIDs < 1 then
         return "No matching Orders found."
     end
     end


Line 437: Line 503:


     -- Default display is table.
     -- Default display is table.
     desiredCaption = resolveCaption(desiredCaption, selectList, searchNameList, searchDescriptionList, rarityFilter, sourceFilter, excludeList)
     desiredCaption = resolveCaption(desiredCaption, selectList, searchNameList, tierFilter, timedFilter, difficultyFilter, settlementFilter, excludeList)


     return renderTable(desiredCaption, viewParameters)
     return renderTable(desiredCaption, viewParameters, specifyDifficulty, collapsed, difficultyFilter, settlementFilter)
end
end


--endregion
--endregion


return PerksController
return OrdersController

Latest revision as of 01:46, 21 October 2025

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

---
--- Serves the Orders searching template by capturing input and using it to control the display of data.
---
---@module OrdersController
local OrdersController = {}

--region Dependencies

local OrdersData = require("Module:OrdersData")
local OrdersView = require("Module:OrdersView")

local ControllerUtilities = require("Module:ControllerUtilities")

--endregion



--region Private constants

local ARG_ID_LIST = "id"
local ARG_NAME_LIST = "name"
local ARG_REQUIREMENTS_LIST = "requirements"
local ARG_REWARDS_LIST = "rewards"
local ARG_DESCRIPTION_LIST = "description"
local ARG_SEARCH_ALL_LIST = "search"
local ARG_TIER = "tier"
local ARG_TIMED = "timed"
local ARG_DIFFICULTY = "difficulty"
local ARG_SETTLEMENT_DIFFICULTY = "settlement"
local ARG_EXCLUDE_LIST = "exclude"
local ARG_CAPTION = "caption"
local ARG_SHOW_ID = "show_id"
local ARG_SHOW_FLAG_VALUE = "show"
local ARG_DISPLAY_OVERRIDE = "display"
local ARG_DISPLAY_OVERRIDE_OPTION_LIST = "list"
local ARG_DISPLAY_OVERRIDE_OPTION_INLINE = "inline"
local ARG_LIST_TYPE = "list_type"
local ARG_COLLAPSED = "collapsed"

local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
local TEMPLATE_PARAMETER_SHOW_ID = ARG_SHOW_ID
local TEMPLATE_PARAMETER_LIST_TYPE = ARG_LIST_TYPE
local TEMPLATE_PARAMETER_ICON_SIZE = "icon_size"
local TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT = "none"

local TEMPLATE_LIST_ITEM = "OrdersList/item"
local TEMPLATE_INLINE_LINK = "ol"

local DISPLAY_YES = "true"

--endregion



--region Private member variables

local currentFrame = {}

local orderIDs = {}
local isOrderAlreadyAdded = {}

--endregion



--region Private methods

--- Takes a list of possible IDs and only loads the valid ones of the specified tier, timed status, or difficulty to the member variable orderIDs. Skips all ids present in the exclude list.
---
---@param newIDs table an array of possible IDs to check before adding
---@param tierToCheck string a tier to filter to, needs to be normalized
---@param timedToCheck string a timed status to filter to, needs to be normalized
---@param difficultyToCheck string a difficulty to filter to, needs to be normalized
---@param excludeList table an array of IDs that should not be added
local function loadOrderIDs(newIDs, tierToCheck, timedToCheck, difficultyToCheck, settlementToCheck, excludeList)

    for _, id in ipairs(newIDs) do

        if OrdersData.isOrderIDValid(id) and not excludeList[id:lower()] and not isIDAlreadyAdded[id:lower()] then

            -- In this situation, nil values mean to skip the filter.
            local isTierMatch = not tierToCheck or "" == tierToCheck or OrdersData.isTierMatchByID(id, tierToCheck)
            local isTimedMatch = not timedToCheck or "" == timedToCheck or OrdersData.isTimedMatchByID(id, timedToCheck)
            local isDifficultyMatch = not difficultyToCheck or "" == difficultyToCheck or OrdersData.isDifficultyMatchByID(id, difficultyToCheck)
            local isSettlementMatch = not settlementToCheck or "" == settlementToCheck or OrdersData.isSettlementMatchByID(id, settlementToCheck)

            if isTierMatch and isTimedMatch and isDifficultyMatch and isSettlementMatch then
                table.insert(orderIDs, id)
                isIDAlreadyAdded[id:lower()] = true
            end
        end
    end
end

--- Requests the desired lists of IDs from Orders data models, applies some post-processing, and stores them in the member variable orderIDs.
---
---@param selectList table array of ids of the cornerstone to include
---@param searchNameList table array of search criteria for names
---@param searchDescriptionsList table array of search criteria for descriptions
---@param rarity string the rarity of the cornerstones
---@param source string the source of cornerstones
---@param excludeList table list of ids
local function loadIDs(selectList, searchNameList, searchRequirementsList, searchRewardsList, tier, timed, difficulty, settlement, excludeList)

    orderIDs = {}
    isIDAlreadyAdded = {}
    local attemptedSearch = false

    -- Load the selected IDs
    if selectList then
        loadOrderIDs(selectList, tier, timed, difficulty, settlement, excludeList)
        -- It should always be true, but it can be blank.
        if #selectList > 0 then
            attemptedSearch = true
        end
    end

    -- Add any name matches
    if searchNameList and #searchNameList > 0 then
        for _, searchName in ipairs(searchNameList) do
            local newIDs = OrdersData.getAllOrdersIDsWhereName(searchName)
            loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
            attemptedSearch = true
        end
    end
    
    -- Add any requirements matches
    if searchRequirementsList and #searchRequirementsList > 0 then
        for _, searchRequirements in ipairs(searchRequirementsList) do
            local newIDs = OrdersData.getAllOrdersIDsWhereRequirements(searchRequirements)
            loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
            attemptedSearch = true
        end
    end
    
    -- Add any rewards matches
    if searchRewardsList and #searchRewardsList > 0 then
        for _, searchRewards in ipairs(searchRewardsList) do
            local newIDs = OrdersData.getAllOrdersIDsWhereRewards(searchRewards)
            loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
            attemptedSearch = true
        end
    end

    -- Or, if no searching was even attempted (regardless of result), show them all.
    if not attemptedSearch then
        local newIDs = OrdersData.getAllOrderIDs()
        loadOrderIDs(newIDs, tier, timed, difficulty, settlement, excludeList)
    end

    return orderIDs
end

--- Ensures that if a caption is not defined, there is a suitable fallback in case the author has specified an id, name, or search term to use, so readers know what they're looking at.
---
--- If no caption was specified and also none of those filtering parameters, then leave it up to the template by returning a blank caption.
---
---@param desiredCaption string the caption requested, if any
---@param selectList table array of ids of the orders to include
---@param searchNameList table array of search criteria for names
---@param searchDescriptionList table array of search criteria for descriptions
---@param rarity string the rarity of the cornerstones
---@param source string the source of cornerstones
---@param excludeList table array of ids that were excluded
---@return string an appropriate caption, or blank if it should be default
local function resolveCaption(desiredCaption, selectList, searchNameList, tier, timed, difficulty, settlement, excludeList)

    -- No matter what, if the author provided a caption, use that.
    if desiredCaption and desiredCaption ~= "" then
        return desiredCaption
    end

    -- If no selection, delegate to template.
    local addedToText = false
    desiredCaption = ""

	if not settlement or settlement == "" then
    	desiredCaption = desiredCaption .. #orderIDs
    end

    if timed and timed ~= "" and timed == "true" then
        desiredCaption = desiredCaption .. " Timed"
        addedToText = true
    end
    
    if timed and timed ~= "" and timed == "false" then
        desiredCaption = desiredCaption .. " Non-Timed"
        addedToText = true
    end

    if tier and tier ~= "" then
        desiredCaption = desiredCaption .. " Tier " .. tier .. " Order"
        addedToText = true
    else
        desiredCaption = desiredCaption .. " Order"
        -- did not addedToText, this is just default
    end

    if #orderIDs > 1 then
        desiredCaption = desiredCaption .. "s"
    end

    if selectList and #selectList > 0 then
        if #selectList == 1 then
            desiredCaption = desiredCaption .. " with ID '" .. selectList[1] .. "'"
        else
            desiredCaption = desiredCaption .. " with selected IDs"
        end
        addedToText = true
    end

    if searchNameList and #searchNameList > 0 then
        if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        if #searchNameList == 1 and searchNameList[1] then
            desiredCaption = desiredCaption .. " named '" .. searchNameList[1] .. "'"
        else
            desiredCaption = desiredCaption .. " with specific names"
        end
        addedToText = true
    end

    if difficulty and difficulty ~= "" then
        desiredCaption = desiredCaption .. " of difficulty " .. difficulty
        addedToText = true
    end

    if settlement and settlement ~= "" then
        desiredCaption = desiredCaption .. " that can be drawn on " .. settlement .. " difficulty"
        addedToText = true
    end

    if excludeList and #excludeList > 0 then
        desiredCaption = desiredCaption .. " (with exclusions)"
        addedToText = true
    end

    if addedToText then
        return desiredCaption
    else
        -- Delegate to the template for the default
        return ""
    end
end

--- Calls the view to render the beginning of the table.
---
---@param desiredCaption string the caption for the table, if any
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled so far by the view module
local function makeMarkupForTableStart(desiredCaption, viewParameters, collapsed)

    return OrdersView.startTable(desiredCaption, viewParameters, collapsed)
end

--- Calls the view to render a single row of the table with data based on the provided identifier.
---
---@param id string the unique identifier of a Order to use to add data to a new table row
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled so far by the view module
local function makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, totalRows)

    local name = OrdersData.getNameByID(id)
    local tier = OrdersData.getTiers(id)[1]
    local timed = OrdersData.getTimed(id)
    local timeLimit = OrdersData.getTimeLimit(id)
    local difficulty = OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow)
    local requirements = ControllerUtilities.findAndReplaceSpriteTagsWithFiles(OrdersData.getFormattedRequirementsByIDAndRowNum(id, difficultyRow), currentFrame)
    local rewards = ControllerUtilities.findAndReplaceSpriteTagsWithFiles(OrdersData.getRewardsFormattedByID(id), currentFrame)
    local rowSpan = totalRows


    return OrdersView.addRow(id, name, tier, timed, timeLimit, difficulty, requirements, rewards, viewParameters, isExtraRow, rowSpan)
end

--- Handles assembling the rows for all of the IDs in the member variable list altarIDs.
---
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableRows(viewParameters, specifyDifficulty, difficulty, settlement)

	if specifyDifficulty then
		local isExtraRow = false
		local case = ""
		if difficulty and settlement then
			case = "both"
		elseif difficulty then
			case = "difficultyOnly"
		elseif settlement then
			case = "settlementOnly"
		else
			error("Impossible parameter combination: specifyDifficulty is true but difficulty and settlement are nil.")
		end
    	for _, id in ipairs(orderIDs) do
    		isExtraRow = false
    		local orderTier = OrdersData.getTiers(id)
    		if case == "both" then
    			local totalRows = OrdersData.getNumberOfDifficulties(id)
        		for difficultyRow = 1, totalRows do
        			if (OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow)):find(difficulty) and OrdersData.isSettlementMatchSpecificOrder(OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow), orderTier, settlement) then
        				makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, 1)
        			end
        		end
    		end
    		if case == "difficultyOnly" then
    			local totalRows = OrdersData.getNumberOfDifficulties(id)
        		for difficultyRow = 1, totalRows do
        			if (OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow)):find(difficulty) then
        				makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, 1)
        			end
        		end
    		end
    		if case == "settlementOnly" then
    			local totalRows = OrdersData.getNumberOfDifficulties(id)
        		for difficultyRow = 1, totalRows do
        			if OrdersData.isSettlementMatchSpecificOrder(OrdersData.getDifficultyByIDAndRowNum(id, difficultyRow), orderTier, settlement) then
        				makeMarkupPerRow(id, viewParameters, difficultyRow, isExtraRow, 1)
        			end
        		end
        	end
    	end
    else
    	local isExtraRow = false
    	for _, id in ipairs(orderIDs) do
    		isExtraRow = false
    		local totalRows = OrdersData.getNumberOfDifficulties(id)
        	for i = 1, totalRows do
        		makeMarkupPerRow(id, viewParameters, i, isExtraRow, totalRows)
        		isExtraRow = true
        	end
    	end
    end
end

---Calls the view to render the content and appends it to the wiki markup string that will replace this controller's template.
-----
----- Calls several methods to build pieces of the table's markup based on the author's requests.
---
---@param desiredCaption string the caption for the table
---@param viewParameters table a table of parameters made by and for OrdersView
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(desiredCaption, viewParameters, specifyDifficulty, collapsed, difficulty, settlement)

    makeMarkupForTableStart(desiredCaption, viewParameters, collapsed)
    makeMarkupForTableRows(viewParameters, specifyDifficulty, difficulty, settlement)

    return OrdersView.finalize()
end

---makeMarkupPerListItem
---@param id string the id of the cornerstone to display
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupPerListItem(id, listType, isShowingID)

    local templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_LIST_TYPE] = listType,
        [TEMPLATE_PARAMETER_SHOW_ID] = isShowingID and ARG_SHOW_FLAG_VALUE
    }

    return currentFrame:expandTemplate{ title = TEMPLATE_LIST_ITEM, args = templateParameters }
end

---makeMarkupForListItems
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForListItems(listType, isShowingID)

    local markup = ""
    for _, id in ipairs(orderIDs) do
        markup = markup .. makeMarkupPerListItem(id, listType, isShowingID)
    end

    return markup
end

---renderList
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates
local function renderList(listType, isShowingID)

    return makeMarkupForListItems(listType, isShowingID)
end

---makeMarkupPerLink
---@param id string the id of the cornerstone to display
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupPerLink(id, isShowingID)

    local templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_ICON_SIZE] = TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT,
    }

    local link = currentFrame:expandTemplate{ title = TEMPLATE_INLINE_LINK, args = templateParameters }

    if isShowingID then
        return link .. " ('" .. id .. "')"
    else
        return link
    end
end

---makeMarkupForInlineLinks
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForInlineLinks(isShowingID)

    local markup = ""
    for i, id in ipairs(orderIDs) do
        if i > 1 then
            markup = markup .. ", "
        end
        markup = markup .. makeMarkupPerLink(id, isShowingID)
    end

    return markup
end

---renderInline
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function renderInlineLinks(isShowingID)

    return makeMarkupForInlineLinks(isShowingID)
end

--endregion



--region Public methods

function OrdersController.main(frame)

    -- Selection parameters, normalized for exclude list
    local selectList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_ID_LIST], false)
    local excludeList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_EXCLUDE_LIST], true, true)
    local searchNameList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_NAME_LIST], false)
    local searchRequirementsList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_REQUIREMENTS_LIST], false)
    local searchRewardsList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_REWARDS_LIST], false)
    local searchAllList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_SEARCH_ALL_LIST], false)
    local tierFilter = OrdersData.normalizeTierText(frame.args[ARG_TIER])
    local timedFilter = OrdersData.normalizeTimedText(frame.args[ARG_TIMED])
    local difficultyFilter = OrdersData.normalizeDifficultyText(frame.args[ARG_DIFFICULTY])
    local settlementFilter = OrdersData.normalizeSettlementDifficultyText(frame.args[ARG_SETTLEMENT_DIFFICULTY])

	---- SettlementFilter should be used independently of other difficulty filter, error if difficulty filter is applied.
	if settlementFilter then
		if difficultyFilter then
			error("Order difficulty filtering cannot be applied when filtering by settlement difficulty.")
		end
	end
	
	-- If a difficulty filter or settlement difficulty filter is applied, then only the relevant difficulties should be shown
	if difficultyFilter or settlementFilter then
		specifyDifficulty = true
	end
	
    -- Substitute the search all terms for each non-provided list.
    if searchAllList and #searchAllList > 0 then
        if #searchNameList < 1 then
            searchNameList = searchAllList
        end
        if #searchRequirementsList < 1 then
            searchRequirementsList = searchAllList
        end
        if #searchRewardsList < 1 then
            searchRequirementsList = searchAllList
        end
    end

    -- Display parameters
    local desiredCaption = frame.args[ARG_CAPTION]
    local displayOverride = frame.args[ARG_DISPLAY_OVERRIDE]
    local listType = frame.args[ARG_LIST_TYPE]
    local isShowingID = ARG_SHOW_FLAG_VALUE == frame.args[ARG_SHOW_ID]
    local collapsed = frame.args[ARG_COLLAPSED]

    -- Let the View module itself determine how to use the template parameters relating to the view.
    local viewParameters = OrdersView.constructViewParametersFromTemplateFrame(frame)

    currentFrame = frame

    loadIDs(selectList, searchNameList, searchRequirementsList, searchRewardsList, tierFilter, timedFilter, difficultyFilter, settlementFilter, excludeList)

    if #orderIDs < 1 then
        return "No matching Orders found."
    end

    if displayOverride == ARG_DISPLAY_OVERRIDE_OPTION_LIST then
        return renderList(listType, isShowingID)
    end
    if displayOverride == ARG_DISPLAY_OVERRIDE_OPTION_INLINE then
        return renderInlineLinks(isShowingID)
    end

    -- Default display is table.
    desiredCaption = resolveCaption(desiredCaption, selectList, searchNameList, tierFilter, timedFilter, difficultyFilter, settlementFilter, excludeList)

    return renderTable(desiredCaption, viewParameters, specifyDifficulty, collapsed, difficultyFilter, settlementFilter)
end

--endregion

return OrdersController