Module:PerksController: Difference between revisions

From Against the Storm Official Wiki
(adding support for changing number of columns that the Sources header spans)
(Updated to call the new PerksView module instead of all the templates that were way too expensive)
 
(6 intermediate revisions by the same user not shown)
Line 4: Line 4:
---@module PerksController
---@module PerksController
local PerksController = {}
local PerksController = {}


--region Dependencies
--region Dependencies


local PerksData = require("Module:PerksData")
local PerksData = require("Module:PerksData")
local PerksView = require("Module:PerksView")


local ControllerUtilities = require("Module:ControllerUtilities")
local ControllerUtilities = require("Module:ControllerUtilities")
Line 20: Line 19:


local ARG_ID_LIST = "id"
local ARG_ID_LIST = "id"
local ARG_NAME = "name"
local ARG_NAME_LIST = "name"
local ARG_DESCRIPTION = "description"
local ARG_DESCRIPTION_LIST = "description"
local ARG_SEARCH_ALL_LIST = "search"
local ARG_RARITY = "rarity"
local ARG_RARITY = "rarity"
local ARG_SOURCE = "source"
local ARG_SOURCE = "source"
local ARGS_EXCLUDE_LIST = "exclude"
local ARG_EXCLUDE_LIST = "exclude"
local ARG_CAPTION = "caption"
local ARG_CAPTION = "caption"
local ARG_SKIP_SOURCES = "skip_sources"
local ARG_SKIP_FLAG_VALUE = "skip"
local ARG_SHOW_ID = "show_id"
local ARG_SHOW_ID = "show_id"
local ARG_SHOW_RARITY = "show_rarity"
local ARG_SHOW_DESCRIPTION = "show_description"
local ARG_SHOW_SOURCE_ALTAR = "show_source_altar"
local ARG_SHOW_SOURCE_CORNERSTONE = "show_source_cornerstone"
local ARG_SHOW_SOURCE_ORDER = "show_source_order"
local ARG_SHOW_SOURCE_RELIC = "show_source_relic"
local ARG_SHOW_SOURCE_TRADER = "show_source_trader"
local ARG_SHOW_PRICE = "show_price"
local ARG_SHOW_FLAG_VALUE = "show"
local ARG_SHOW_FLAG_VALUE = "show"
local ARG_DISPLAY_OVERRIDE = "display"
local ARG_DISPLAY_OVERRIDE = "display"
Line 43: Line 33:
local ARG_LIST_TYPE = "list_type"
local ARG_LIST_TYPE = "list_type"


local TEMPLATE_PARAMETER_CAPTION = ARG_CAPTION
local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
local TEMPLATE_PARAMETER_RARITY = ARG_RARITY
local TEMPLATE_PARAMETER_DESC = ARG_DESCRIPTION
local TEMPLATE_PARAMETER_SOURCE_ALTAR = "is_from_altar"
local TEMPLATE_PARAMETER_SOURCE_CORNERSTONE = "is_from_cornerstone"
local TEMPLATE_PARAMETER_SOURCE_ORDER = "is_from_order"
local TEMPLATE_PARAMETER_SOURCE_RELIC = "is_from_relic"
local TEMPLATE_PARAMETER_SOURCE_TRADER = "is_from_trader"
local TEMPLATE_PARAMETER_NUM_SOURCES = "num_sources_shown"
local TEMPLATE_PARAMETER_PRICE = "price"
local TEMPLATE_PARAMETER_SHOW_ID = ARG_SHOW_ID
local TEMPLATE_PARAMETER_SHOW_ID = ARG_SHOW_ID
local TEMPLATE_PARAMETER_LIST_TYPE = ARG_LIST_TYPE
local TEMPLATE_PARAMETER_LIST_TYPE = ARG_LIST_TYPE
Line 59: Line 39:
local TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT = "none"
local TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT = "none"


local TEMPLATE_TABLE_BASE = "PerksCornerstonesTable"
local TEMPLATE_TABLE_SUFFIX_ROW = "/row"
local TEMPLATE_TABLE_SUFFIX_END = "/end"
local TEMPLATE_TABLE_SKIP_SOURCES_VARIANT = "/SkipSources"
local TEMPLATE_LIST_ITEM = "PerksCornerstonesList/item"
local TEMPLATE_LIST_ITEM = "PerksCornerstonesList/item"
local TEMPLATE_INLINE_LINK = "pl"
local TEMPLATE_INLINE_LINK = "pl"
Line 95: Line 71:
     for _, id in ipairs(newIDs) do
     for _, id in ipairs(newIDs) do


         if PerksData.isPerkIDValid(id) and not excludeList[id] and not isIDAlreadyAdded[id] then
         if PerksData.isPerkIDValid(id) and not excludeList[id:lower()] and not isIDAlreadyAdded[id:lower()] then


             -- In this situation, nil values mean to skip the filter.
             -- In this situation, nil values mean to skip the filter.
Line 103: Line 79:
             if isRarityMatch and isSourceMatch then
             if isRarityMatch and isSourceMatch then
                 table.insert(perkIDs, id)
                 table.insert(perkIDs, id)
                 isIDAlreadyAdded[id] = true
                 isIDAlreadyAdded[id:lower()] = true
             end
             end
         end
         end
Line 111: Line 87:
--- 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 Perks data models, applies some post-processing, and stores them in the member variable perkIDs.
---
---
---@param selectList table list of ids of the cornerstone to include
---@param selectList table array of ids of the cornerstone to include
---@param searchName string search term to look in cornerstone names
---@param searchNameList table array of search criteria for names
---@param searchDesc string search term to look in cornerstone descriptions
---@param searchDescriptionsList table array of search criteria for descriptions
---@param rarity string the rarity of the cornerstones
---@param rarity string the rarity of the cornerstones
---@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
function PerksController.loadIDs(selectList, searchName, searchDesc, rarity, source, excludeList)
local function loadIDs(selectList, searchNameList, searchDescriptionsList, rarity, source, excludeList)


     perkIDs = {}
     perkIDs = {}
Line 133: Line 109:


     -- Add any name matches
     -- Add any name matches
     if searchName and searchName ~= "" then
     if searchNameList and #searchNameList > 0 then
         local newIDs = PerksData.getAllPerkIDsWhereName(searchName)
         for _, searchName in ipairs(searchNameList) do
        loadPerkIDs(newIDs, rarity, source, excludeList)
            local newIDs = PerksData.getAllPerkIDsWhereName(searchName)
        attemptedSearch = true
            loadPerkIDs(newIDs, rarity, source, excludeList)
            attemptedSearch = true
        end
     end
     end


     -- Add any description matches
     -- Add any description matches
     if searchDesc and searchDesc ~= "" then
     if searchDescriptionsList and #searchDescriptionsList > 0 then
         local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDesc)
         for _, searchDescription in ipairs(searchDescriptionsList) do
        loadPerkIDs(newIDs, rarity, source, excludeList)
            local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDescription)
        attemptedSearch = true
            loadPerkIDs(newIDs, rarity, source, excludeList)
            attemptedSearch = true
        end
     end
     end


     -- Or, if no searching was attempted (regardless of result), show all from the selected rarities and sources.
     -- 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.
     if not attemptedSearch then
     if not attemptedSearch then
         local newIDs = PerksData.getAllPerkIDsFilteredByRarityAndSource(rarity, source)
         if (rarityFilter and "" ~= rarityFilter) or (sourceFilter and "" ~= sourceFilter) then
        loadPerkIDs(newIDs, rarity, source, excludeList)
            local newIDs = PerksData.getAllPerkIDsFilteredByRarityAndSource(rarity, source)
        attemptedSearch = true
            loadPerkIDs(newIDs, rarity, source, excludeList)
            attemptedSearch = true
        end
     end
     end


Line 156: Line 138:
     if not attemptedSearch then
     if not attemptedSearch then
         local newIDs = PerksData.getAllPerkIDs()
         local newIDs = PerksData.getAllPerkIDs()
         loadStormforgedIDs(newIDs, excludeList)
         loadPerkIDs(newIDs, rarity, source, excludeList)
     end
     end


Line 172: Line 154:
---
---
---@param desiredCaption string the caption requested, if any
---@param desiredCaption string the caption requested, if any
---@param selectList table list of ids of the cornerstone to include
---@param selectList table array of ids of the perks to include
---@param searchName string the second search parameter, a name
---@param searchNameList table array of search criteria for names
---@param searchDesc string the third search parameter, a term in a description
---@param searchDescriptionList table array of search criteria for descriptions
---@param rarity string the rarity of the cornerstones
---@param rarity string the rarity of the cornerstones
---@param source string the source of 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
---@return string an appropriate caption, or blank if it should be default
local function resolveCaption(desiredCaption, selectList, searchName, searchDesc, rarity, source, excludeList)
local function resolveCaption(desiredCaption, selectList, searchNameList, searchDescriptionList, rarity, source, excludeList)


     -- No matter what, if the author provided a caption, use that.
     -- No matter what, if the author provided a caption, use that.
Line 191: Line 174:


     if rarity and rarity ~= "" then
     if rarity and rarity ~= "" then
         desiredCaption = desiredCaption .. " " .. rarity .. " Perks"
         desiredCaption = desiredCaption .. " " .. rarity .. " Perk"
         addedToText = true
         addedToText = true
     else
     else
         desiredCaption = desiredCaption .. " Perks"
         desiredCaption = desiredCaption .. " Perk"
         addedToText = true
         -- did not addedToText, this is just default
    end
 
    if #perkIDs > 1 then
        desiredCaption = desiredCaption .. "s"
     end
     end


Line 212: Line 199:
     end
     end


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


     if searchDesc and searchDesc ~= "" then
     if searchDescriptionList and #searchDescriptionList > 0 then
         if addedToText then
         if addedToText then
             desiredCaption = desiredCaption .. " and"
             desiredCaption = desiredCaption .. " and"
         end
         end
         desiredCaption = desiredCaption .. " mentioning '" .. searchDesc .. "'"
         if #searchDescriptionList == 1 and searchDescriptionList[1] then
            desiredCaption = desiredCaption .. " mentioning '" .. searchDescriptionList[1] .. "'"
        else
            desiredCaption = desiredCaption .. " by searching descriptions"
        end
         addedToText = true
         addedToText = true
     end
     end
Line 241: Line 236:
end
end


---resolveTableStartTemplate
--- Calls the view to render the beginning of the table.
---@param isSkippingSources boolean true if skipping variant should be used
---@return string the full title of the template, for expandTemplate
local function resolveTableStartTemplate(isSkippingSources)
 
    local suffixIfSkipping = (isSkippingSources and TEMPLATE_TABLE_SKIP_SOURCES_VARIANT) or ""
 
    return TEMPLATE_TABLE_BASE .. suffixIfSkipping
end
 
---resolveTableRowTemplate
---@param isSkippingSources boolean true if skipping variant should be used
---@return string the full title of the template, for expandTemplate
local function resolveTableRowTemplate(isSkippingSources)
 
    local suffixIfSkipping = (isSkippingSources and TEMPLATE_TABLE_SKIP_SOURCES_VARIANT) or ""
 
    return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_ROW .. suffixIfSkipping
end
 
---resolveEndTemplate
---@return string the template name to use
local function resolveTableEndTemplate()
 
    return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_END
end
 
--- Calls the view (other templates) to render the beginning of the table.
---
---
------@param desiredCaption string the caption for the table, if any
---@param desiredCaption string the caption for the table, if any
---@param isSkippingSources boolean true if skipping variant should be used
---@param viewParameters table a table of parameters made by and for PerksView
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
---@return string the wiki markup assembled so far by the view module
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableStart(desiredCaption, viewParameters)
local function makeMarkupForTableStart(desiredCaption, isSkippingSources, argsForTableTemplate)
 
    argsForTableTemplate[TEMPLATE_PARAMETER_CAPTION] = desiredCaption
 
    -- Count how many source columns should be shown.
    local sources = {
        ARG_SHOW_SOURCE_ALTAR,
        ARG_SHOW_SOURCE_CORNERSTONE,
        ARG_SHOW_SOURCE_ORDER,
        ARG_SHOW_SOURCE_RELIC,
        ARG_SHOW_SOURCE_TRADER
    }
    local numSourcesShown = 0
    for _, index in ipairs(sources) do
        if ARG_SHOW_FLAG_VALUE == argsForTableTemplate[index] then
            numSourcesShown = numSourcesShown + 1
        end
    end
    argsForTableTemplate[TEMPLATE_PARAMETER_NUM_SOURCES] = numSourcesShown
 
    return currentFrame:expandTemplate{ title = resolveTableStartTemplate(isSkippingSources), args = argsForTableTemplate }


    return PerksView.startTable(desiredCaption, viewParameters)
end
end


--- Calls the view (other templates) 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 Perk or Cornerstone to use to add data to a new table row
---@param isSkippingSources boolean true if skipping variant should be used
---@param viewParameters table a table of parameters made by and for PerksView
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
---@return string the wiki markup assembled so far by the view module
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupPerRow(id, viewParameters)
local function makeMarkupPerRow(id, isSkippingSources, argsForTableTemplate)
 
    local name = PerksData.getNameByID(id)
    local rarity = PerksData.getRarityByID(id)
    local description = ControllerUtilities.removeEnclosingDoubleQuotes(ControllerUtilities.findAndReplaceSpriteTagsWithFiles(PerksData.getDescriptionByID(id), currentFrame))


     argsForTableTemplate[TEMPLATE_PARAMETER_ID] = id
     local isSourceAltar = PerksData.isFromAltarByID(id) and DISPLAY_YES
     argsForTableTemplate[TEMPLATE_PARAMETER_RARITY] = PerksData.getRarityByID(id)
    local isSourceCornerstone = PerksData.isFromCornerstoneByID(id) and DISPLAY_YES
     argsForTableTemplate[TEMPLATE_PARAMETER_DESC] = PerksData.getDescriptionByID(id)
     local isSourceOrder = PerksData.isFromOrderByID(id) and DISPLAY_YES
     argsForTableTemplate[TEMPLATE_PARAMETER_PRICE] = PerksData.getPriceByID(id)
     local isSourceRelic = PerksData.isFromEventByID(id) and DISPLAY_YES
     local isSourceTrader = PerksData.isFromTraderByID(id) and DISPLAY_YES


     -- This is default, but adds a lot of extra template overhead if it's not needed, so only add it if necessary.
     local price = PerksData.getPriceByID(id)
    if not isSkippingSources then
        argsForTableTemplate[TEMPLATE_PARAMETER_SOURCE_ALTAR] = PerksData.isFromAltarByID(id) and DISPLAY_YES
        argsForTableTemplate[TEMPLATE_PARAMETER_SOURCE_CORNERSTONE] = PerksData.isFromCornerstoneByID(id) and DISPLAY_YES
        argsForTableTemplate[TEMPLATE_PARAMETER_SOURCE_ORDER] = PerksData.isFromOrderByID(id) and DISPLAY_YES
        argsForTableTemplate[TEMPLATE_PARAMETER_SOURCE_RELIC] = PerksData.isFromEventByID(id) and DISPLAY_YES
        argsForTableTemplate[TEMPLATE_PARAMETER_SOURCE_TRADER] = PerksData.isFromTraderByID(id) and DISPLAY_YES
    end


     return currentFrame:expandTemplate{ title = resolveTableRowTemplate(isSkippingSources), args = argsForTableTemplate }
     return PerksView.addRow(id, name, rarity, description, isSourceAltar, isSourceCornerstone, isSourceOrder, isSourceRelic, isSourceTrader, 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 isSkippingSources boolean true if skipping sources columns
---@param viewParameters table a table of parameters made by and for PerksView
---@param argsForTableTemplate table a table of arguments to pass through to the view templates
---@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(isSkippingSources, argsForTableTemplate)
local function makeMarkupForTableRows(viewParameters)


    local markup = ""
     for _, id in ipairs(perkIDs) do
     for _, id in ipairs(perkIDs) do
         markup = markup .. makeMarkupPerRow(id, isSkippingSources, argsForTableTemplate)
         makeMarkupPerRow(id, viewParameters)
     end
     end
    return markup
end
end


--- Calls the view (other templates) to render the end of the table.
---Calls the view to render the content and appends it to the wiki markup string that will replace this controller's template.
---
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableEnd()
 
    return currentFrame:expandTemplate{ title = resolveTableEndTemplate(), args = {} }
end
 
---Calls the view (other templates) 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.
----- 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 desiredCaption string the caption for the table
---@param isSkippingSources boolean true if skipping sources columns
---@param viewParameters table a table of parameters made by and for PerksView
---@param argsForTableTemplate table a table of arguments to pass through to the view templates
---@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, isSkippingSources, argsForTableTemplate)
local function renderTable(desiredCaption, viewParameters)


     local startMarkup = makeMarkupForTableStart(desiredCaption, isSkippingSources, argsForTableTemplate)
     makeMarkupForTableStart(desiredCaption, viewParameters)
     local rowsMarkup = makeMarkupForTableRows(isSkippingSources, argsForTableTemplate)
     makeMarkupForTableRows(viewParameters)


     return startMarkup .. rowsMarkup .. makeMarkupForTableEnd()
     return PerksView.finalize()
end
end


Line 453: Line 385:
function PerksController.main(frame)
function PerksController.main(frame)


     -- Selection parameters
     -- Selection parameters, normalized for exclude list
     local selectList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_ID_LIST], false)
     local selectList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_ID_LIST], false)
     local excludeList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARGS_EXCLUDE_LIST], true)
     local excludeList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_EXCLUDE_LIST], true, true)
     local searchName = frame.args[ARG_NAME]
     local searchNameList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_NAME_LIST], false)
     local searchDesc = frame.args[ARG_DESCRIPTION]
     local searchDescriptionList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_DESCRIPTION_LIST], false)
    local searchAllList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_SEARCH_ALL_LIST], false)
 
     local rarityFilter = PerksData.normalizeRarityText(frame.args[ARG_RARITY])
     local rarityFilter = PerksData.normalizeRarityText(frame.args[ARG_RARITY])
     local sourceFilter = PerksData.normalizeSourceText(frame.args[ARG_SOURCE])
     local sourceFilter = PerksData.normalizeSourceText(frame.args[ARG_SOURCE])
    -- Substitute the search all terms for name or description, if they weren't provided but search-all was.
    if searchAllList and #searchAllList > 0 then
        if #searchNameList < 1 then
            searchNameList = searchAllList
        end
        if #searchDescriptionList < 1 then
            searchDescriptionList = searchAllList
        end
    end


     -- Display parameters
     -- Display parameters
Line 466: Line 410:
     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 isSkippingSources = ARG_SKIP_FLAG_VALUE == frame.args[ARG_SKIP_SOURCES]


     -- For clarity, copy each value instead of reusing the whole args table.
     -- Let the View module itself determine how to use the template parameters relating to the view.
     local argumentsToPassThroughToTable = {
     local viewParameters = PerksView.constructViewParametersFromTemplateFrame(frame)
        [ARG_SKIP_SOURCES] = frame.args[ARG_SKIP_SOURCES],
        [ARG_SHOW_ID] = frame.args[ARG_SHOW_ID],
        [ARG_SHOW_RARITY] = frame.args[ARG_SHOW_RARITY],
        [ARG_SHOW_DESCRIPTION] = frame.args[ARG_SHOW_DESCRIPTION],
        [ARG_SHOW_SOURCE_ALTAR] = frame.args[ARG_SHOW_SOURCE_ALTAR],
        [ARG_SHOW_SOURCE_CORNERSTONE] = frame.args[ARG_SHOW_SOURCE_CORNERSTONE],
        [ARG_SHOW_SOURCE_ORDER] = frame.args[ARG_SHOW_SOURCE_ORDER],
        [ARG_SHOW_SOURCE_RELIC] = frame.args[ARG_SHOW_SOURCE_RELIC],
        [ARG_SHOW_SOURCE_TRADER] = frame.args[ARG_SHOW_SOURCE_TRADER],
        [ARG_SHOW_PRICE] = frame.args[ARG_SHOW_PRICE]
    }


     currentFrame = frame
     currentFrame = frame


     PerksController.loadIDs(selectList, searchName, searchDesc, rarityFilter, sourceFilter, excludeList)
     loadIDs(selectList, searchNameList, searchDescriptionList, rarityFilter, sourceFilter, excludeList)
     if #perkIDs < 1 then
     if #perkIDs < 1 then
         return "No matching Perks found."
         return "No matching Perks found."
Line 497: Line 429:


     -- Default display is table.
     -- Default display is table.
     desiredCaption = resolveCaption(desiredCaption, selectList, searchName, searchDesc, rarityFilter, sourceFilter, excludeList)
     desiredCaption = resolveCaption(desiredCaption, selectList, searchNameList, searchDescriptionList, rarityFilter, sourceFilter, excludeList)


     return renderTable(desiredCaption, isSkippingSources, argumentsToPassThroughToTable)
     return renderTable(desiredCaption, viewParameters)
end
end



Latest revision as of 01:07, 24 June 2024

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

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

--region Dependencies

local PerksData = require("Module:PerksData")
local PerksView = require("Module:PerksView")

local ControllerUtilities = require("Module:ControllerUtilities")

--endregion



--region Private constants

local ARG_ID_LIST = "id"
local ARG_NAME_LIST = "name"
local ARG_DESCRIPTION_LIST = "description"
local ARG_SEARCH_ALL_LIST = "search"
local ARG_RARITY = "rarity"
local ARG_SOURCE = "source"
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 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 = "PerksCornerstonesList/item"
local TEMPLATE_INLINE_LINK = "pl"

local DISPLAY_YES = "true"

--endregion



--region Private member variables

local currentFrame = {}

local perkIDs = {}
local isIDAlreadyAdded = {}

--endregion



--region Private methods

--- Takes a list of possible IDs and only loads the valid ones of the specified rarity or source to the member variable perkIDs. Skips all ids present in the exclude list.
---
---@param newIDs table an array of possible IDs to check before adding
---@param rarityToCheck string a rarity to filter to, needs to be normalized
---@param sourceToCheck string a source to filter to, needs to be normalized
---@param excludeList table an array of IDs that should not be added
local function loadPerkIDs(newIDs, rarityToCheck, sourceToCheck, excludeList)

    for _, id in ipairs(newIDs) do

        if PerksData.isPerkIDValid(id) and not excludeList[id:lower()] and not isIDAlreadyAdded[id:lower()] then

            -- In this situation, nil values mean to skip the filter.
            local isRarityMatch = not rarityToCheck or "" == rarityToCheck or PerksData.isRarityMatchByID(id, rarityToCheck)
            local isSourceMatch = not sourceToCheck or "" == sourceToCheck or PerksData.isAnySource(id, sourceToCheck)

            if isRarityMatch and isSourceMatch then
                table.insert(perkIDs, id)
                isIDAlreadyAdded[id:lower()] = true
            end
        end
    end
end

--- Requests the desired lists of IDs from Perks data models, applies some post-processing, and stores them in the member variable perkIDs.
---
---@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, searchDescriptionsList, rarity, source, excludeList)

    perkIDs = {}
    isIDAlreadyAdded = {}
    local attemptedSearch = false

    -- Load the selected IDs
    if selectList then
        loadPerkIDs(selectList, rarity, source, 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 = PerksData.getAllPerkIDsWhereName(searchName)
            loadPerkIDs(newIDs, rarity, source, excludeList)
            attemptedSearch = true
        end
    end

    -- Add any description matches
    if searchDescriptionsList and #searchDescriptionsList > 0 then
        for _, searchDescription in ipairs(searchDescriptionsList) do
            local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDescription)
            loadPerkIDs(newIDs, rarity, source, excludeList)
            attemptedSearch = true
        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.
    if not attemptedSearch then
        if (rarityFilter and "" ~= rarityFilter) or (sourceFilter and "" ~= sourceFilter) then
            local newIDs = PerksData.getAllPerkIDsFilteredByRarityAndSource(rarity, source)
            loadPerkIDs(newIDs, rarity, source, excludeList)
            attemptedSearch = true
        end
    end

    -- Or, if no searching was even attempted (regardless of result), show them all.
    if not attemptedSearch then
        local newIDs = PerksData.getAllPerkIDs()
        loadPerkIDs(newIDs, rarity, source, excludeList)
    end

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

    return perkIDs
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 perks 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, searchDescriptionList, rarity, source, 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 = "" .. #perkIDs

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

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

    if source and source ~= "" then
        desiredCaption = desiredCaption .. " from " .. source .. "s"
        addedToText = true
    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 searchDescriptionList and #searchDescriptionList > 0 then
        if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        if #searchDescriptionList == 1 and searchDescriptionList[1] then
            desiredCaption = desiredCaption .. " mentioning '" .. searchDescriptionList[1] .. "'"
        else
            desiredCaption = desiredCaption .. " by searching descriptions"
        end
        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 PerksView
---@return string the wiki markup assembled so far by the view module
local function makeMarkupForTableStart(desiredCaption, viewParameters)

    return PerksView.startTable(desiredCaption, viewParameters)
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 Perk or Cornerstone to use to add data to a new table row
---@param viewParameters table a table of parameters made by and for PerksView
---@return string the wiki markup assembled so far by the view module
local function makeMarkupPerRow(id, viewParameters)

    local name = PerksData.getNameByID(id)
    local rarity = PerksData.getRarityByID(id)
    local description = ControllerUtilities.removeEnclosingDoubleQuotes(ControllerUtilities.findAndReplaceSpriteTagsWithFiles(PerksData.getDescriptionByID(id), currentFrame))

    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 price = PerksData.getPriceByID(id)

    return PerksView.addRow(id, name, rarity, description, isSourceAltar, isSourceCornerstone, isSourceOrder, isSourceRelic, isSourceTrader, price, viewParameters)
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 PerksView
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableRows(viewParameters)

    for _, id in ipairs(perkIDs) do
        makeMarkupPerRow(id, viewParameters)
    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 PerksView
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(desiredCaption, viewParameters)

    makeMarkupForTableStart(desiredCaption, viewParameters)
    makeMarkupForTableRows(viewParameters)

    return PerksView.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(perkIDs) 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(perkIDs) 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 PerksController.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 searchDescriptionList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_DESCRIPTION_LIST], false)
    local searchAllList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_SEARCH_ALL_LIST], false)

    local rarityFilter = PerksData.normalizeRarityText(frame.args[ARG_RARITY])
    local sourceFilter = PerksData.normalizeSourceText(frame.args[ARG_SOURCE])

    -- Substitute the search all terms for name or description, if they weren't provided but search-all was.
    if searchAllList and #searchAllList > 0 then
        if #searchNameList < 1 then
            searchNameList = searchAllList
        end
        if #searchDescriptionList < 1 then
            searchDescriptionList = 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]

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

    currentFrame = frame

    loadIDs(selectList, searchNameList, searchDescriptionList, rarityFilter, sourceFilter, excludeList)
    if #perkIDs < 1 then
        return "No matching Perks 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, searchDescriptionList, rarityFilter, sourceFilter, excludeList)

    return renderTable(desiredCaption, viewParameters)
end

--endregion

return PerksController