require('Module:No globals');
local getArgs = require ('Module:Arguments').getArgs;  
local amendment_pattern = '%s*%(amended version of (%d%d%d%d) assessment%)';
local errata_pattern = '%s*%(errata version published in (%d%d%d%d)%)';  
--2 templates cite single sources; when the identifiers in |doi=, |id=, and |page= are different from each other
then the template is attempting to cite multiple sources. This function evaluates the identifier portions of these
parameters. returns seven values: identifyier parts (or nil when parameter not used) and a message (nil on success,
error message else)  
the identifier portions of the several parameters must be properly formed  
local function iucn_identifiers_get (args)
local doi_taxon_ID, doi_assesment_ID
local page_taxon_ID, page_assesment_ID
local url_taxon_ID, url_assesment_ID
local msg  
if args.doi then
doi_taxon_ID, doi_assesment_ID = args.doi:match ('[Tt](%d+)[Aa](%d+)%.e[ns]$')
if not doi_taxon_ID then
msg = 'malformed |doi= identifier'
end
end
if args.page then
page_taxon_ID, page_assesment_ID = args.page:match ('^[eE]%.[Tt](%d+)[Aa](%d+)$')
if not page_taxon_ID then
msg = 'malformed |page= identifier'
end
end
if args.url then
if args.url:match ('https://www.iucnredlist.org/species/') then -- must be a 'new-form' url
url_taxon_ID, url_assesment_ID = args.url:match ('/species/(%d+)/(%d+)')
if not url_taxon_ID then
msg = 'malformed |url= identifier'
end
end
end  
if not msg then
if doi_taxon_ID and page_taxon_ID then
if (doi_taxon_ID ~= page_taxon_ID or ((doi_assesment_ID ~= page_assesment_ID) and not args.errata)) then
msg = '|doi= / |page= mismatch'
end
end
if doi_taxon_ID and url_taxon_ID then
if (doi_taxon_ID ~= url_taxon_ID or ((doi_assesment_ID ~= url_assesment_ID) and not args.errata)) then
msg = '|doi= / |url= mismatch'
end
end  
if page_taxon_ID and url_taxon_ID then
if (page_taxon_ID ~= url_taxon_ID or ((page_assesment_ID ~= url_assesment_ID) and not args.errata)) then
msg = '|page= / |url= mismatch'
end
end
end  
if msg then
msg = '<span>: error: ' .. msg .. ' (help)</span>'
end  
return doi_taxon_ID, doi_assesment_ID, page_taxon_ID, page_assesment_ID, msg
end  
--volume= (if present) against year in |date= or |year= (if present) against volume in |doi= (if present)  
returns nil if all that are present are correct; message else  
local function iucn_volume_check (args)
local vol = args.volume;
local date = args.date or args.year;
local doi = args.doi and args.doi:match ('[Ii][Uu][Cc][Nn]%.[Uu][Kk]%.(%d%d%d%d)')
local msg  
if vol and date then
msg = (vol ~= date) and '|volume= / |date= mismatch' or msg
end
if vol and doi then
msg = ((vol ~= doi) and not args.amends) and '|volume= / |doi= mismatch' or msg
end
if date and doi then
msg = ((doi ~= date) and not args.amends) and '|date= / |doi= mismatch' or msg
end  
return msg
end  
----------------------------< C I T E >----------------------------------------------------------------------  
Wraps :
takes cite journal parameters but updates old style url using electronic page number
page should be in format e.T13922A45199653
the url uses 13922/45199653
so we need to extract the number between T and A (taxon ID) and the number after A (assessment ID)
the target url is https://www.iucnredlist.org/species/13922/45199653
usage:
template:  
local function cite
local error_msgs = {}; -- holds error messages for rendering
local maint_msgs = {}; -- holds hidden maint messages for rendering
local namespace = mw.title.getCurrentTitle.namespace; -- used for categorization
local args = getArgs ; -- local copy of template arguments  
local missing_title = not args.title -- special case that results from script writing template from bare iucn url
-- don't duplicate cs1|2 error message; don't duplicate error cat
-- TODO: remove this when the error category has been cleared of missing title errors  
if args.title and (args.title:match (errata_pattern) or args.title:match (amendment_pattern)) then
table.insert (maint_msgs, 'title has extraneous text'); -- announce that this template has has errata or amendment text
end  
local doi_taxon_ID, doi_assesment_ID -- all of these contain the same identifying info in slightly
local page_taxon_ID, page_assesment_ID -- different forms. when any combination of these is present,
local msg -- this holds error messages; nil on success  
doi_taxon_ID, doi_assesment_ID, page_taxon_ID, page_assesment_ID, msg = iucn_identifiers_get (args);
if msg then
table.insert (error_msgs, msg); -- malformed or mismatched identifiers
end
args.id = nil -- unset; not supported  
local url_taxon_ID = page_taxon_ID or doi_taxon_ID; -- select for use in url that we will create
local url_assesment_ID = page_assesment_ID or doi_assesment_ID  
local url = args.url
if url then
if url:find ('iucnredlist.org/details/', 1, true) then -- old-form url
if url_taxon_ID then -- when there is an identifier
url = nil -- unset; we'll create new url below
else -- here when old-form but no identifier that we can use to create new url
args.url = args.url:gsub ("http:", "https:") -- sometimes works with redirect on iucn site
end
table.insert (maint_msgs, 'old-form url') -- announce that this template has has an old-form url
elseif url:find ('iucnredlist.org/species/', 1, true) then -- new-form url
-- table.insert (maint_msgs, 'new-form url') --TODO: restore this line when most new-form urls have been removed from article space -- announce that this template has has an new-form url
else
table.insert (maint_msgs, 'unknown url') -- announce that this template has has some sort of url we don't recognize
end
end  
if not url then -- when no url or unset old-form url
if url_taxon_ID then
args.url = "https://www.iucnredlist.org/species/" .. url_taxon_ID .. '/' .. url_assesment_ID
else
table.insert (maint_msgs, 'no identifier') -- TODO: raise this to error status?
end
end  
-- add journal if not provided (TODO decide if this should override provided value)
if not args['journal'] and not args['work'] then
args['journal'] = "IUCN Red List of Threatened Species"
end  
msg = iucn_volume_check (args); -- |volume=, |year= (|date=), |doi= must all refer to the same volume
if msg then
table.insert (maint_msgs, msg);
end  
if not args.volume and (args.year or args.date) then
args.volume = args.year or args.date
end  
if args.errata then
args['orig-date'] = 'errata version of ' .. (args.year or args.date or args.volume) .. ' assessment';
args.date = args.errata; -- update publication data to errata year
args.year = nil; -- unset these as no longer needed
args.errata = nil;
elseif args.amends then
args['orig-date'] = 'amended version of ' .. args.amends .. ' assessment';
args.amends = nil; -- unset as no longer needed
end
-- add free-to-read icon to mark a correctly formed doi
args['doi-access'] = args.doi and args.doi:match ('10%.2305/[Ii][Uu][Cc][Nn].+[Tt]%d+[Aa]%d+%.[Ee][NnSs]') and 'free' or nil  
return :expandTemplate{ title = 'cite journal', args = args } .. -- the template
(((0 == #error_msgs) and missing_title) and (*) or *) .. -- special case to not duplicate cs1|2 err msg or cite iucn error cat
((0 < #error_msgs) and table.concat (error_msgs, ', ') or '') .. -- the error messages
(((0 < #error_msgs) and (0 == namespace)) and (*) or *) .. -- error category when in mainspace
((0 < #maint_msgs) and ('<span>' .. table.concat (maint_msgs, ', ') .. '</span>') or '') .. -- the maint messages
(((0 < #maint_msgs) and (0 == namespace)) and (*) or *) -- maint category when in mainspace
end  
--authorn= parameters from the list of names provided in the raw iucn citation. names
must have the form: Surname, I. (more than one 'I.' pair allowed but no spaces between I. pairs)  
assumes that parenthetical text at the end of the author-name-list is a collaboration
Name, I.I., & Name, I.I. (Colaboration name)  
local function author_names_get (raw_iucn_cite)
local list = {}; -- table that holds name list parts