# Module:Template parameter value

local p = {}  
local PrepareText = require("Module:Wikitext Parsing").PrepareText  

local function getTitle(title)  
local success, titleObj = pcall(mw.title.new, title)  
if success then return titleObj  
else return nil end  
end  

--string.gmatch will check the largest block it can without re-scanning whats inside, but we need whats inside  
local function matchAllTemplates(str)  
local matches = {}  
for template in string.gmatch(str, "{%b{}}") do  
table.insert(matches, template)  
local innerContent = string.sub(template, 3, -3)  
for _,subtemplate in next,matchAllTemplates(innerContent) do  
table.insert(matches, subtemplate)  
end  
end  
return matches  
end  

--Forked version of getParameters from Module:Transcluder with extra features removed  
local function escapeString(str)  
return string.gsub(str, '[%^%$%(%)%.%[%]%*%+%-%?%%]', '%%%0')  
end  
local function getParameters(template)  
local parameters, parameterOrder = {}, {}  
local params = string.match(template, '')  
if params then  
local count = 0  
-- Temporarily replace pipes in subtemplates and wikilinks to avoid chaos  
for subtemplate in string.gmatch(params, '{%b{}}') do  
params = string.gsub(params, escapeString(subtemplate), string.gsub(subtemplate, ".", {["%"]="%%", ["|"]="@@:@@", ["="]="@@_@@"}) )  
end  
for wikilink in string.gmatch(params, '%[%b[]%]') do  
params = string.gsub(params, escapeString(wikilink), string.gsub(wikilink, ".", {["%"]="%%", ["|"]="@@:@@", ["="]="@@_@@"}) )  
end  
for parameter in mw.text.gsplit(params, '|') do  
local parts = mw.text.split(parameter, '=')  
local key = mw.text.trim(parts[1])  
local value  
if #parts == 1 then  
value = key  
count = count + 1  
key = tostring(count)  
else  
value = mw.text.trim(table.concat(parts, '=', 2))  
end  
value = string.gsub(string.gsub(value, '@@:@@', '|'), '@@_@@', '=')  
key = string.gsub(string.gsub(key, '@@:@@', '|'), '@@_@@', '=')  
table.insert(parameterOrder, key)  
parameters[key] = value  
end  
end  
return parameters, parameterOrder  
end  

-- Returns a table containing parameters and a table with the order in which each of their values were found.  
-- Since this considers all subtemplates, a single parameter is expected to have multiple values.  
-- E.g.  
-- Would return {X={"", "Value", "Yes"}, Y={"Other value", "P"}}  
local function getAllParameters(template, ignore_blank, only_subtemplates)  
local parameterTree = setmetatable({}, {  
__index = function(self,key)  
rawset(self,key,{})  
return rawget(self,key)  
end  
})  
local params, paramOrder = getParameters(template)  
for _,key in ipairs(paramOrder) do  
local value = params[key]  
if not ignore_blank or value ~= "" then  
if not only_subtemplates then  
table.insert(parameterTree[key], value) --Insert the initial value into the tree  
end  
for subtemplate in string.gmatch(value, "{%b{}}") do --And now check for subvalues  
local subparams = getAllParameters(subtemplate, ignore_blank)  
for subkey,subset in next,subparams do  
for _,subvalue in ipairs(subset) do  
table.insert(parameterTree[subkey], subvalue) --And add any we find to our tree  
end  
end  
end  
end  
end  
return parameterTree  
end  

--Module entry point. Returns a success boolean and either the target template or why it failed  
function p.getTemplate(page, templates, options)  
if not templates then --Required parameters  
return false, "Missing required parameter 'templates'"  
end  
options = options or {}  

local template_index = tonumber(options.template_index) or 1  
local treat_as_regex = options.treat_as_regex or false  
if type(templates) == "string" then  
-- TODO: Find a good way to allow specifying multiple templates via template invocation  
-- (Modules can just provide a table so no concerns there)  
-- Comma splitting is a bad idea (lots of templates have a comma in their name)  
templates = {templates}  
end  

local title = getTitle(page)  
if title == nil then  
return false, "Requested title doesn't exist"  
end  
local content = PrepareText(title:getContent or "")  

local foundTemplates = 0  
for _,template in next,matchAllTemplates(content) do  
for _,wantedTemplate in pairs(templates) do  
if not treat_as_regex then  
wantedTemplate = escapeString(wantedTemplate)  
end  
local firstLetter = string.sub(wantedTemplate, 1, 1)  
local firstUpper, firstLower = firstLetter:upper, firstLetter:lower  
if firstUpper ~= firstLower then  
wantedTemplate = "[" .. firstUpper .. firstLower .. "]" .. string.sub(wantedTemplate, 2)  
end  
if string.match(template, "^  
--Module entry point. Returns a success boolean and either the target parameter's value or why it failed  
function p.getParameter(page, templates, parameter, options)  
if not (templates and parameter) then --Required parameters  
return false, "Missing required parameters 'templates' and 'parameter'"  
end  
parameter = tostring(parameter) --Force consistency  
options = options or {}  

local success, text = p.getTemplate(page, templates, options)  
if not success then  
return success, text  
else  
local parameter_index = tonumber(options.parameter_index) or 1  
local ignore_subtemplates = options.ignore_subtemplates or false  
local only_subtemplates = options.only_subtemplates or false  
local ignore_blank = options.ignore_blank or false  

local value  
if ignore_subtemplates then  
value = getParameters(text)[parameter] or ""  
else  
local params = getAllParameters(text, ignore_blank, only_subtemplates)  
value = params[parameter][parameter_index] or ""  
end  

value = string.gsub(value, "</?%a*include%a*>", "")  
value = mw.text.trim(value) --technically wrong in some cases but not a big issue  
return true, mw.text.decode(value) --decode due to PrepareText  
end  
end  

--Template entry point. Returns either "yes" or nothing depending on if the wanted template is found  
--Will return error text if no template is provided  
function p.hasTemplate  
local args = require('Module:Arguments').getArgs  
local yesno = require("Module:Yesno")  
local page = args[1] or args.page  
local template = args[2] or args.template  
local template_index = tonumber(args[3] or args.N) or 1  
if not template or template == "" then  
return '<span>No template provided for hasTemplate</span>'  
end  
local follow = yesno(args.follow) or false  
if follow then  
page = require("Module:Redirect").luaMain(page)  
end  
local options = {  
template_index = template_index,  
treat_as_regex = yesno(args.treat_as_regex) or false,  
}  

local success, _ = p.getTemplate(page, template, options)  
return success and "yes" or ""  
end  

--Template entry point for getParameter. Returns an empty string upon failure  
function p.main  
local args = require('Module:Arguments').getArgs(, {  
wrappers = 'Template:Template parameter value'  
})  
local yesno = require("Module:Yesno")  
local options = {  
template_index = args[3] or args.template_index,  
parameter_index = args[5] or args.parameter_index,  
ignore_subtemplates = yesno(args.ignore_subtemplates or args.ist) or false,  
only_subtemplates = yesno(args.only_subtemplates) or false,  
ignore_blank = yesno(args.ignore_blank) or false,  
treat_as_regex = yesno(args.treat_as_regex) or false,  
}  
local page = args[1] or args.page  
local template = args[2] or args.template  
local parameter = args[4] or args.parameter  
local success, result = p.getParameter(page, template, parameter, options)  
if not success then  
return ""  
else  
if args.dontprocess then  
return result  
else  
return :preprocess(result)  
end  
end  
end  

--Backwards compatability  
p.getValue = p.getParameter  
--Potentially useful module entry points  
p.matchAllTemplates = matchAllTemplates  
p.getParameters = getParameters  
p.getAllParameters = getAllParameters  

return p