require('strict')

local p = {}
local title = mw.title.getCurrentTitle()
local namespace = title.namespace
local testcases = (string.sub(title.subpageText,1,9) == 'Exemplos para testes')

--[[==========================================================================]]
--[[                            Category functions                            ]]
--[[==========================================================================]]

local function addCat( cat )
    if cat then return '[[Categoria:'..cat..']]'..p.redCatLink(cat) end
    return ''
end

function p.getCatForId( id )
    local cat = ''
    if namespace == 0 then
        cat = '!Artigos enciclopédicos com identificadores '..id
    elseif namespace == 2 and not title.isSubpage then
        cat = '!Páginas de usuário com identificadores '..id
    else
        cat = '!Páginas de teor misto com identificadores '..id
    end
    return addCat(cat)
end

function p.redCatLink( cat ) --cat == 'Blah' (not 'Category:Blah', not '[[Categoria:Blah]]')
    if cat and cat ~= '' and
       testcases == false and
       mw.title.new(cat, 14).exists == false
    then
        return '' --'[[Categoria:Pages with red-linked authority control categories]]'
    end
    return ''
end

function p.createRow( id, rawValues, link, links, withUid, specialCat, prefix)
    local faultyCat = '!Artigos com identificadores com erro '..(specialCat or id)
    if links then -- all links[] use withUid = false; no check needed
        local row = ''
        if prefix then
            row = row..'*'..prefix
        end
        for i, l in ipairs( links ) do
            if i == 1 and not prefix then row = row..'*'
            else           row = row..'\n**' end
            if l then
                row = row..'<span class="uid">'..l..'</span>'
            else
                row = row..'<span class="error">O '..id..' id '..rawValues[i]..' não é válido.</span>'..addCat(faultyCat)
            end
        end
        return row..'\n'
    elseif link then -- All IDs that have a prefix support multiple identifiers, so prefix is not needed
        if withUid then
            return '*<span class="nowrap"><span class="uid">'..link..'</span></span>\n'
        end
        return '*<span class="nowrap">'..link..'</span>\n'
    end
    
    return '* <span class="error">O '..id..' id '..rawValues..' não é válido.</span>' ..addCat(faultyCat)..'\n'
end

--[[==========================================================================]]
--[[                      Property formatting functions                       ]]
--[[==========================================================================]]

-- If a link has a suitable entry in the global inter-wiki prefix table at [[:m:Interwiki_map]],
-- please consider routing through this prefix rather than as external link URL.
-- This will ease future maintenance as necessary updates to the link can be centrally carried out there rather than by updating this module.
-- The "external link" icon would disappear for such entries.

function p.aagLink( id, label)
    --P3372's format regex: \d+ (e.g. 1)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[https://www.aucklandartgallery.com/explore-art-and-ideas/artist/'..id..'/ '..(label or 'Auckland')..']'
end

function p.acmLink( id, label )
    --P864's format regex: \d{11} (e.g. 12345678901)
    if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[https://dl.acm.org/profile/'..id..' '..(label or 'Association for Computing Machinery')..']'
end

function p.adbLink( id, label )
    --P1907's format regex: [a-z][-a-z]+-([1-3]\d|[1-9])\d{0,3} (e.g. barton-sir-edmund-toby-71)
    if not id:match( '^[a-z][-a-z]+-[1-3]%d%d?%d?%d?$' ) and
       not id:match( '^[a-z][-a-z]+-[1-9]%d?%d?%d?$' ) then
        return false
    end
    return '[http://adb.anu.edu.au/biography/'..id..' '..(label or 'Austrália')..']'
end

function p.admiraltyLink(id,label)
    --P3562's format regex: [A-Q]\d{4}(\.\d+)? (e.g. D1204.1)
    if not id:match('^[A-Q]%d%d%d%d$') and
       not id:match('^[A-Q]%d%d%d%d%.%d+$') then
        return false
    end
    return id
end

function p.agsaLink( id, label )
    --P6804's format regex: [1-9]\d* (e.g. 3625)
    if not id:match( '^[1-9]%d*$' ) then
        return false
    end
    return '[https://www.agsa.sa.gov.au/collection-publications/collection/creators/_/'..id..'/ '..(label or 'Sul da Austrália')..']'
end

function p.ARLHSLink(id,label)
    --P2980's format regex: [A-Z]{3}\d{3,4}[A-Z]?| e.g. LAT023
    if not id:match('^[A-Z][A-Z][A-Z]%d%d%d%d?[A-Z]?$') then
        return false
    end
    return '[http://wlol.arlhs.com/lighthouse/'..id..'.html '..(label or 'ARLHS')..']'
end

function p.autoresuyLink( id, label )
    --P2558's format regex: [1-9]\d{0,4} (e.g. 12345)
    if not id:match( '^[1-9]%d?%d?%d?%d?$' ) then
        return false
    end
    return '[https://autores.uy/autor/'..id..' '..(label or 'Uruguai')..']'
end

function p.awrLink( id, label )
    --P4186's format regex: (([A-Z]{3}\d{4})|([A-Z]{2}\d{5}))[a-z] (e.g. PR00768b)
    if not id:match( '^[A-Z][A-Z][A-Z]%d%d%d%d[a-z]$' ) and
       not id:match( '^[A-Z][A-Z]%d%d%d%d%d[a-z]$' ) then
        return false
    end
    return '[http://www.womenaustralia.info/biogs/'..id..'.htm '..(label or 'Australian Women\'s Register')..']'
end

function p.bibsysLink( id, label )
    --P1015's format regex: [1-9]\d* or [1-9](\d{0,8}|\d{12}) (e.g. 1234567890123)
    --TODO: follow up @ [[d:Property talk:P1015#Discrepancy between the 2 regex constraints]] or escalate/investigate
    if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
       not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[https://authority.bibsys.no/authority/rest/authorities/html/'..id..' '..(label or 'Noruega')..']'
end

function p.bildLink( id, label )
    --P2092's format regex: \d+ (e.g. 1)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[https://www.bildindex.de/document/obj'..id..' '..(label or 'Bildindex (Alemanha)')..']'
end

function p.blbnbLink( id, label )
    --P4619's format regex: \d{9}* (e.g. 001027092)
    if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[http://acervo.bn.br/sophia_web/autoridade/detalhe/'..id..' '..(label or 'BLBNB (Brasil)')..']'
end

function p.bncLink( id, label )
    --P1890's format regex: \d{9} (e.g. 123456789)
    if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[http://www.bncatalogo.cl/F?func=direct&local_base=red10&doc_number='..id..' '..(label or 'Chile')..']'
end

function p.bneLink( id, label )
    --P950's format regex: (XX|FF|a)\d{4,7}|(bima|bimo|bica|bis[eo]|bivi|Mise|Mimo|Mima)\d{10} (e.g. XX1234567)
    if not id:match( '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and
       not id:match( '^a%d%d%d%d%d?%d?%d?$' ) and
       not id:match( '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and
       not id:match( '^Mi[sm][eoa]%d%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id='..id..' '..(label or 'Espanha')..']' --no https as of 9/2019
end

function p.bnfLink( id, label )
    --P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
    if not id:match( '^c?b?%d%d%d%d%d%d%d%d[0-9bcdfghjkmnpqrstvwxz]$' ) then
        return false
    end
    --Add cb prefix if it has been removed
    if not id:match( '^cb.+$' ) then
        id = 'cb'..id
    end
    return '[https://catalogue.bnf.fr/ark:/12148/'..id..' '..(label or 'França')..'] [https://data.bnf.fr/ark:/12148/'..id..' (dados)]'
end

function p.botanistLink( id, label )
    --P428's format regex: ('t )?(d')?(de )?(la )?(van (der )?)?(Ma?c)?(De)?(Di)?\p{Lu}?C?['\p{Ll}]*([-'. ]*(van )?(y )?(d[ae][nr]?[- ])?(Ma?c)?[\p{Lu}bht]?C?['\p{Ll}]*)*\.? ?f?\.? (e.g. L.)
    --not easily/meaningfully implementable in Lua's regex since "(this)?" is not allowed...
    if not mw.ustring.match( id, "^[%u%l%d%. '-]+$" ) then --better than nothing
        return false
    end
    id = id:gsub(' +', '%%20')
    return '[https://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation='..id..' '..(label or 'Índice Internacional de Nomes de Plantas')..']'
end

function p.bndLink( id, label )
    --P5691's format regex: \d{5} (e.g. 22954)
    if not id:match( '^%d%d%d%d%d$' ) then
        return false
    end
    return '[http://purl.pt/'..id..(label or 'BND (Portugal)')..']'
end

function p.bpnLink( id, label )
    --P651's format regex: \d{6,8} (e.g. 00123456)
    if not id:match( '^%d%d%d%d%d%d%d%d$' ) and --original format regex, changed 8/2019 to
       not id:match( '^0?%d%d%d%d%d%d%d$' ) and --allow 1-2 leading 0s, allowed by the website
       not id:match( '^0?0?%d%d%d%d%d%d$' ) then
        return false
    end
    return '[http://www.biografischportaal.nl/en/persoon/'..id..' '..(label or 'Países Baixos')..']' --no https as of 9/2019
end

function p.canticLink( id, label )
    --P1273's format regex: a\d{7}[0-9x] (e.g. a10640745)
    if not id:match( '^a%d%d%d%d%d%d%d[%dx]$' ) then
        return false
    end
    return '[http://cantic.bnc.cat/registres/CUCId/'..id..' '..(label or 'Catalunha')..']' --no https as of 10/2019
end

function p.ccgLink( id, label )
    --P3920's format regex: ([NAIP])?[1-9]\d*(\.\d+)? (e.g. A1761)
    if not id:match( '^[NAIP]?[1-9]%d*$' ) and
       not id:match( '^[NAIP]?[1-9]%d*%.%d+$' ) then
        return false
    end
    return id
end

function p.ciniiLink( id, label )
    --P271's format regex: DA\d{7}[\dX] (e.g. DA12345678)
    if not id:match( '^DA%d%d%d%d%d%d%d[%dX]$' ) then
        return false
    end
    return '[https://ci.nii.ac.jp/author/'..id..'?l=en '..(label or 'CiNii (Japão)')..']'
end

function p.cwgcLink( id, label )
    --P1908's format regex: [1-9]\d* (e.g. 75228351)
    if not id:match( '^[1-9]%d*$' ) then
        return false
    end
    return '[https://www.cwgc.org/find-war-dead/casualty/'..id..'/ '..(label or 'Commonwealth War Graves Commission')..']'
end

function p.emuLink( id, label )
    --P4613's format regex: \d{1,6} (e.g. 15409 (or 015409))
    if not id:match( '^%d%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[http://esu.com.ua/search_articles.php?id='..id..' '..(label or 'Ucrânia')..']'
end

function p.daaoLink( id, label )
    --P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)
    if not id:match( '^[a-z%-]+%d*$' ) then
        return false
    end
    return '[https://www.daao.org.au/bio/'..id..' '..(label or 'Australian Artists')..']'
end

function p.dblpLink( id, label )
    --P2456's format regex: \d{2,3} /\d+(-\d+)?|[a-z] /[a-zA-Z][0-9A-Za-z]*(-\d+)? (e.g. 123/123)
    if not id:match( '^%d%d%d?/%d+$' ) and
       not id:match( '^%d%d%d?/%d+%-%d+$' ) and
       not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and
       not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*%-%d+$' ) then
        return false
    end
    return '[https://dblp.org/pid/'..id..' '..(label or 'DBLP (ciência da computação)')..']'
end

function p.dibLink( id, label )
    --P6829's format regex: a\d{4}\d?(-[A-D])? (e.g. a1953)
    if not id:match( '^a%d%d%d%d%d?%-?[A-D]?$' ) then
        return false
    end
    return '[https://dib.cambridge.org/viewReadPage.do?articleId='..id..' '..(label or 'Irlanda')..']'
end

function p.dsiLink( id, label )
    --P2349's format regex: [1-9]\d* (e.g. 1538)
    if not id:match( '^[1-9]%d*$' ) then
        return false
    end
    return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..(label or 'Ilustração técnica')..']'
end

function p.fastLink( id, label )
    --P2163's format regex: [1-9]\d{0,7} (e.g. 1916996)
    if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[http://id.worldcat.org/fast/'..id..'/ '..(label or 'Faceted Application of Subject Terminology')..']'
end


function p.fnzaLink( id, label )
    --P6792's format regex: [1-9]\d* (e.g. 9785)
    if not id:match( '^[1-9]%d*$' ) then
        return false
    end
    return '[https://findnzartists.org.nz/artist/'..id..'/ '..(label or 'New Zealand Artists')..']'
end

function p.gndLink( id, label )
    --P227's format regex: 1[012]?\d{7}[0-9X]|[47]\d{6}-\d|[1-9]\d{0,7}-[0-9X]|3\d{7}[0-9X] (e.g. 4079154-3)
    if not id:match( '^1[012]?%d%d%d%d%d%d%d[0-9X]$' ) and
       not id:match( '^[47]%d%d%d%d%d%d%-%d$' ) and
       not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and
       not id:match( '^3%d%d%d%d%d%d%d[0-9X]$' ) then
        return false
    end
    return '[https://d-nb.info/gnd/'..id..' '..(label or 'Gemeinsame Normdatei (Alemanha)')..']'
end

function p.googleLink( id, label )
    --P1960's format regex: [-_0-9A-Za-z]{12} (e.g. CUO0vDcAAAAJ)
    if not id:match( '^[%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u][%-_%d%l%u]$' ) then
        return false
    end
    return '[https://scholar.google.com/citations?user='..id..' '..(label or 'Google Scholar')..']'
end

function p.hdsLink( id, label )
    --P902's format regex: \d{6} (e.g. 050123)
    if not id:match( '^%d%d%d%d%d%d$' ) then
        return false
    end
    return '[https://hls-dhs-dss.ch/fr/articles/'..id..' '..(label or 'Dicionário Histórico da Suíça')..']'
end

function p.iaafLink( id, label )
    --P1146's format regex: [0-9][0-9]* (e.g. 012)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[https://www.iaaf.org/athletes/_/'..id..' '..(label or 'World Athletics')..']'
end


function p.iccuLink( id, label )
    --P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
    if not id:match( '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
       not id:match( '^IT\\ICCU\\%u%u[%u%d]%u\\%d%d%d%d%d%d$' ) then --legacy: %u used here instead of %D (but the faulty ID cat is empty, out of ~12k uses)
        return false
    end
    return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..(label or 'Itália')..']'
end
function p.iciaLink( id, label )
    --P1736's format regex: \d+ (e.g. 1)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[https://www.imj.org.il/artcenter/newsite/en/?artist='..id..' '..(label or 'ICIA (Israel)')..']'
end

function p.ieuLink( id, label )
    --P9070's format regex: [A-Z]\\[A-Z]\\[A-Za-z0-9]+ (e.g. K\Y\Kyiv)
    if not id:match( '^[A-Z]\\[A-Z]\\%w+$' ) then
        return false
    end
    return '[http://www.encyclopediaofukraine.com/display.asp?linkpath=pages\\'..id..' '..(label or 'Internet Encyclopedia of Ukraine')..']'
end

function p.itauLink( id, label )
    --P4399's format regex: (pessoa|grupo|obra|evento|instituicao|termo)\d+\/[a-z][\-a-z]* (e.g. 1743787)
    if not id:match( '^(pessoa|grupo|obra|evento|instituicao|termo)\d+\/[a-z][\-a-z]*$' ) then
        return false
    end
    return '[https://enciclopedia.itaucultural.org.br/'..id..' '..(label or 'Itáu cultural')..']'
end

function p.isniLink( id, label )
    id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
    if not id then
        return false
    end
    return '[https://isni.org/isni/'..id..' '..(label or 'ISNI')..']'
end

function p.jocondeLink( id, label )
    --P347's format regex: [\-0-9A-Za-z]{11} (e.g. 12345678901)
    local regex = '^'..string.rep('[%-0-9A-Za-z]', 11)..'$'
    if not id:match( regex ) then
        return false
    end
    return '[https://www.pop.culture.gouv.fr/notice/joconde/'..id..' '..(label or 'Joconde (França)')..']'
end

function p.kulturnavLink( id, label )
    --P1248's format regex: [0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    return '[http://kulturnav.org/'..id..' '..(label or 'KulturNav (Noruega)')..']' --no https as of 9/2019
end

function p.lccnLink( id, label )
    local parts = p.splitLccn( id ) --e.g. n78039510
    if not parts then
        return false
    end
    local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
    id = parts[1] .. parts[2] .. p.append( parts[3], '0', 6 )
    return '[https://id.loc.gov/authorities/'..lccnType..'/'..id..' '..(label or 'Estados Unidos')..']'
end

function p.lirLink( id, label )
    --P886's format regex: \d+ (e.g. 1)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[http://www.e-lir.ch/e-LIR___Lexicon.'..id..'.450.0.html '..(label or 'Lexicon Istoric Retic (Suíça)')..']' --no https as of 9/2019
end

function p.lnbLink( id, label )
    --P1368's format regex: \d{9} (e.g. 123456789)
    if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[https://kopkatalogs.lv/F?func=direct&local_base=lnc10&doc_number='..id..'&P_CON_LNG=ENG '..(label or 'Letónia')..']'
end

function p.leonoreLink( id, label )
    --P640's format regex: LH//\d{1,4}/\d{1,3}|19800035/\d{1,4}/\d{1,5}(Bis|Ter)?|C/0/\d{1,2} (e.g. LH//2064/18)
    if not id:match( '^LH//%d%d?%d?%d?/%d%d?%d?$' ) and               --IDs from      LH//1/1 to        LH//2794/54 (legionaries)
       not id:match( '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?$' ) and    --IDs from 19800035/1/1 to 19800035/385/51670 (legionnaires who died 1954-1977 & some who died < 1954)
       not id:match( '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?Bis$' ) and --IDs from ?
       not id:match( '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?Ter$' ) and --IDs from ?
       not id:match( '^C/0/%d%d?$' ) then                             --IDs from        C/0/1 to             C/0/84 (84 famous legionaries)
        return false
    end
    return '[http://www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1='..id..' '..(label or 'Léonore (França)')..']' --no https as of 9/2019
end

function p.mbaLink( id, label )
    --P434's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    local url = 'https://musicbrainz.org/artist/'..id
    if label then
        return '['..url..' '..label..']'--..cat   
    else
        return '[[MBA (identificador)|MusicBrainz]] ['..url..' artist]'
    end
end

function p.mbareaLink( id, label )
    --P982's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    local url = 'https://musicbrainz.org/area/'..id
    if label then
        return '['..url..' '..label..']'   
    else
        return '[[MBAREA (identificador)|MusicBrainz]] ['..url..' area]'-- ..cat
    end
end

function p.mbiLink( id, label )
    --P1330's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    local url = 'https://musicbrainz.org/instrument/'..id
    if label then
        return '['..url..' '..label..']'--..cat   
    else
        return '[[MBI (identificador)|MusicBrainz]] ['..url..' instrument]'
    end
end

function p.mblLink( id, label )
    --P966's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    local url = 'https://musicbrainz.org/label/'..id
    if label then
        return '['..url..' '..label..']'
    else
        return '[[MBL (identificador)|MusicBrainz]] ['..url..' label]'--..cat
    end
end

function p.mbpLink( id, label )
    --P1004's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    local url = 'https://musicbrainz.org/place/'..id
    if label then
        return '['..url..' '..label..']'
    else
        return '[[MBP (identificador)|MusicBrainz]] ['..url..' place]'--..cat
    end
end

function p.mbrgLink( id, label )
    --P436's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    local url = 'https://musicbrainz.org/release-group/'..id
    if label then
        return '['..url..' '..label..']'   
    else
        return '[[MBRG (identificador)|MusicBrainz]] ['..url..' release group]'--..cat
    end
end

function p.mbsLink( id, label )
    --P1407's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    local url = 'https://musicbrainz.org/series/'..id
    if label then
        return '['..url..' '..label..']'   
    else
        return '[[MBS (identificador)|MusicBrainz]] ['..url..' series]'--..cat
    end
end

function p.mbwLink( id, label )
    --P435's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
    if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
        return false
    end
    
    local url = 'https://musicbrainz.org/work/'..id
    if label then
        return '['..url..' '..label..']'
    else
        return '[[MBW (identificador)|MusicBrainz]] ['..url..' work]'--..cat
    end
end

function p.mgpLink( id, label )
    --P549's format regex: \d{1,6} (e.g. 123456)
    if not id:match( '^%d%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[https://genealogy.math.ndsu.nodak.edu/id.php?id='..id..' '..(label or 'Mathematics Genealogy Project')..']'
end

function p.naraLink( id, label )
    --P1225's format regex: ^([1-9]\d{0,8})$ (e.g. 123456789)
    if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[https://catalog.archives.gov/id/'..id..' '..(label or 'National Archives (EUA)')..']'
end

function p.nclLink( id, label )
    --P1048's format regex: \d+ (e.g. 1081436)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence='..id..'&CON_LNG=ENG '..(label or 'Taiwan')..']' --no https as of 9/2019
end

function p.ndlLink( id, label )
    --P349's format regex: 0?\d{8} (e.g. 012345678)
    if not id:match( '^0?%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[https://id.ndl.go.jp/auth/ndlna/'..id..' '..(label or 'Japão')..']'
end

function p.ngaLink(id,label)
    --P3563's format rgex: 11[0-6]-\d+(\.\d+)? (e.g. 114-7721.2)
    if not id:match('^11[0-6]%-%d+%.?%d*$') then
        return false
    end
    return '[https://wikidata-externalid-url.toolforge.org/?url=https%3A%2F%2Fmsi.nga.mil%2FqueryResults%3Fpublications%2Fngalol%2Flights-buoys%3Fvolume%3D%251%26featureNumber%3D%252%26includeRemovals%3Dfalse%26output%3Dhtml&exp=(%5Cd%7B3%7D)-(.*)&id='..id..' '..(label or 'NGA')..']'
end

function p.ngvLink( id, label )
    --P2041's format regex: \d+ (e.g. 12354)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[https://www.ngv.vic.gov.au/explore/collection/artist/'..id..'/ '..(label or 'Victoria')..']'
end

function p.nkcLink( id, label )
    --P691's format regex: [a-z]{2,4}[0-9]{2,14} (e.g. abcd12345678901234)
    if not id:match( '^[a-z][a-z][a-z]?[a-z]?%d%d%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[https://aleph.nkp.cz/F/?func=find-c&local_base=aut&ccl_term=ica='..id..'&CON_LNG=ENG '..(label or 'Chéquia')..']'
end

function p.nlaLink( id, label )
    --P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)
    if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[https://nla.gov.au/anbd.aut-an'..id..' '..(label or 'Austrália')..']'
end

function p.nlgLink( id, label )
    --P3348's format regex: [1-9]\d* (e.g. 1)
    if not id:match( '^[1-9]%d*$' ) then
        return false
    end
    return '[https://data.nlg.gr/resource/authority/record'..id..' '..(label or 'Grécia')..']'
end

function p.nliLink( id, label )
    --P949's format regex: \d{9} (e.g. 123456789)
    if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[http://uli.nli.org.il/F/?func=direct&doc_number='..id..'&local_base=nlx10'..' '..(label or 'Israel')..']'
end

function p.nlkLink( id, label )
    --P5034's format regex: KA.(19|20).{7} (e.g. KAC201501465)
    if not id:match( '^KA.19.......$' ) and
       not id:match( '^KA.20.......$' ) then
        return false
    end
    return '[https://librarian.nl.go.kr/LI/contents/L20101000000.do?id='..id..' '..(label or 'Coreia')..']'
end

function p.nlpLink( id, label )
    --P1695's format regex: 9810[0-9]\d* or A[0-9]{7}[0-9X] (e.g. 9810123456789012345 or A10414836)
    if not id:match( '^9810%d+$' ) and
       not id:match( '^A%d%d%d%d%d%d%d[%dX]$' ) then
        return false
    end
    return '[https://tools.wmflabs.org/wikidata-externalid-url?p=1695&id='..id..' '..(label or 'Polónia')..']'
end

function p.nlrLink( id, label )
    --P1003's format regex: \d{9} (e.g. 123456789)
    if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[http://aleph.bibnat.ro:8991/F/?func=direct&local_base=NLR10&doc_number='..id..' '..(label or 'Romênia')..']'
end

function p.nskLink( id, label )
    --P1375's format regex: \d{9} (e.g. 123456789)
    if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
        return false
    end
    return '[http://katalog.nsk.hr/F/?func=direct&doc_number='..id..'&local_base=nsk10 '..(label or 'Croácia')..']' --no https as of 9/2019
end

function p.ntaLink( id, label )
    --P1006's format regex: \d{8}[\dX] (e.g. 12345678X)
    if not id:match( '^%d%d%d%d%d%d%d%d[%dX]$' ) then
        return false
    end
    return '[http://data.bibliotheken.nl/id/thes/p'..id..' '..(label or 'Países Baixos')..']'
end

function p.orcidLink( id, label )
    id = p.validateIsni( id ) --e.g. 0000-0002-7398-5483
    if not id then
        return false
    end
    id = id:sub( 1, 4 )..'-'..id:sub( 5, 8 )..'-'..id:sub( 9, 12 )..'-'..id:sub( 13, 16 )
    return '[https://orcid.org/'..id..' '..(label or 'ORCID')..']'
end

function p.picLink( id, label )
    --P2750's format regex: [1-9]\d* (e.g. 1)
    if not id:match( '^[1-9]%d*$' ) then
        return false
    end
    return '[https://pic.nypl.org/constituents/'..id..' '..(label or 'Photographers\' Identities')..']'
end

function p.plwabnLink( id, label )
    --P7293's format regex: 981[0-9]{8}05606 (e.g. 9810696457305606)
    if not id:match( '^981%d%d%d%d%d%d%d%d05606*$' ) then
        return false
    end
    return '[http://mak.bn.org.pl/cgi-bin/KHW/makwww.exe?BM=1&NU=1&IM=4&WI='..id..' '..(label or 'Polónia')..']'
end

function p.publonsLink( id, label )
    --P3829's format regex: \d+ (e.g. 654601)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[https://publons.com/author/'..id..'/ '..(label or 'Publons (investigadores)')..']'
end

function p.PWNLink( id, label )
    --P7305's format regex: [1-9]\d+ (e.g. 3886065)
    if not id:match( '^[1-9]%d+$' ) then
        return false
    end
    return '[https://encyklopedia.pwn.pl/haslo/;'..id..'.html '..(label or 'Online PWN')..']'
end

function p.ridLink( id, label )
    --P1053's format regex: [A-Z]{1,3}-\d{4}-(19|20)\d\d (e.g. AAS-5150-2020)
    if not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-19%d%d$' ) and
       not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-20%d%d$' ) then
        return false
    end
    return '[https://www.researcherid.com/rid/'..id..' '..(label or 'ResearcherID')..']'
end

function p.rismLink( id, label )
    --P5504's format regex: (pe|ks)?\[1-9]d* (e.g. pe30006410)
    if not id:match( '^pe[1-9]%d*$' ) and --99% start with 'pe'
       not id:match( '^ks[1-9]%d*$' ) and
       not id:match( '^[1-9]%d*$' ) then
        return false
    end
    return '[https://opac.rism.info/search?id='..id..' '..(label or 'RISM (França)')..']'
end

function p.reroLink( id, label )
    --P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)
    if not id:match( '^0[1-2]%-[A-Z%d][A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?$' ) then
        return false
    end
    return '[http://data.rero.ch/'..id..' '..(label or 'RERO (Suíça')..']'
end

function p.rkdartistsLink( id, label )
    --P650's format regex: [1-9]\d{0,5} (e.g. 123456)
    if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[https://rkd.nl/en/explore/artists/'..id..' '..(label or 'RKD Artists (Países Baixos)')..']'
end

function p.rkdidLink( id, label )
    --P350's format regex: [1-9]\d{0,5} (e.g. 123456)
    if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[https://rkd.nl/nl/explore/images/'..id..' '..(label or 'RKD ID (Países Baixos)')..']'
end

function p.rslLink( id, label )
    --P947's format regex: \d{1,9} (e.g. 123456789)
    if not id:match( '^%d%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
        return false
    end
    return '[http://aleph.rsl.ru/F?func=find-b&find_code=SYS&adjacent=Y&local_base=RSL11&request='..id..'&CON_LNG=ENG '..(label or 'Rússia')..']' --no https as of 9/2019
end

function p.scopusLink( id, label )
    --P1153's format regex: [1-9]\d{9,10} (e.g. 35247902700)
    if not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d?$' ) then
        return false
    end
    return '[https://www.scopus.com/authid/detail.uri?authorId='..id..' '..(label or 'autor Scopus')..']'
end

function p.selibrLink( id, label )
    --P906's format regex: [1-9]\d{4,5} (e.g. 123456)
    if not id:match( '^[1-9]%d%d%d%d%d?$' ) then
        return false
    end
    return '[https://libris.kb.se/auth/'..id..' '..(label or 'Suécia')..']'
end

function p.sikartLink( id, label )
    --P781's format regex: \d{7,9} (e.g. 123456789)
    if not id:match( '^%d%d%d%d%d%d%d%d?%d?$' ) then
        return false
    end
    return '[http://www.sikart.ch/KuenstlerInnen.aspx?id='..id..'&lng=en '..(label or 'SIKART (Suíça)')..']' --no https as of 9/2019
end

function p.snacLink( id, label )
    --P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
    if not id:match( '^%d*[A-Za-z][0-9A-Za-z]*$' ) then
        return false
    end
    return '[https://snaccooperative.org/ark:/99166/'..id..' '..(label or 'Social Networks and Archival Context')..']'
end

function p.sudocLink( id, label )
    --P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)
    if not id:match( '^%d%d%d%d%d%d%d%d[%dxX]$' ) then --legacy: allow lowercase 'x'
        return false
    end
    return '[https://www.idref.fr/'..id..' '..(label or 'SUDOC (França)')..']'
end

function p.ta98Link( id, label )
    --P1323's format regex: A\d{2}\.\d\.\d{2}\.\d{3}[FM]? (e.g. A12.3.45.678)
    if not id:match( '^A%d%d%.%d%.%d%d%.%d%d%d[FM]?$' ) then
        return false
    end
    local longurl = '[http://tools.wmflabs.org/wikidata-externalid-url/?p=1323&url_prefix=https:%2F%2Fwww.unifr.ch%2Fifaa%2FPublic%2FEntryPage%2FTA98%20Tree%2FEntity%20TA98%20EN%2F&url_suffix=%20Entity%20TA98%20EN.htm&id='
    return longurl..id..' '..(label or 'Terminologia Anatomica')..']'
end

function p.tdviaLink( id, label )
    --P7314's format regex: [a-z/-]+] (e.g. barkan-omer-lutfi)
    if not id:match( '^[a-z/-]+$' ) then
        return false
    end
    return '[https://islamansiklopedisi.org.tr/'..id..' '..(label or 'Enciclopédia do Islão')..']'
end


function p.tepapaLink( id, label )
    --P3544's format regex: \d+ (e.g. 1)
    if not id:match( '^%d+$' ) then
        return false
    end
    return '[https://collections.tepapa.govt.nz/agent/'..id..' '..(label or 'Te Papa (Nova Zelândia)')..']'
end

function p.tlsLink( id, label )
    id = id:gsub(' +', '_')
    --P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,69} (e.g. Abcd)
    local class = "[%a%d_',%.%-%(%)%*/–]"
    local idlen = #id
    if idlen < 4 or idlen > 70 then
        return false
    end
    local regex = '^%u'..string.rep(class, idlen - 1)..'$'
    if not mw.ustring.match( id, regex ) then
        return false
    end
    return '[http://tls.theaterwissenschaft.ch/wiki/'..id..' '..(label or 'Theaterlexikon (Suíça)')..']' --no https as of 9/2019
end

function p.troveLink( id, label )
    --P1315's format regex: [1-9]\d{5,7} (e.g. 12345678)
    if not id:match( '^[1-9]%d%d%d%d%d%d?%d?$' ) then
        return false
    end
    return '[https://trove.nla.gov.au/people/'..id..' '..(label or 'Trove (Austrália)')..']'
end

function p.ukparlLink( id, label )
    --P6213's format regex: [a-zA-Z\d]{8} (e.g. AQUupyiR)
    if not id:match( '^[a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d]$' ) then
        return false
    end
    return '[https://id.parliament.uk/'..id..' '..(label or 'Parlamento do RU')..']'
end

function p.ulanLink( id, label )
    --P245's format regex: 500\d{6} (e.g. 500123456)
    if not id:match( '^500%d%d%d%d%d%d$' ) then
        return false
    end
    return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid='..id..' '..(label or 'Nomes de Artistas (Getty)')..']'
end

function p.uscgLink( id, label )
    --P3723's format regex: [1-7]-\d{1,5}(.[1-9])? (e.g. 6-0695)
    if not id:match( '^[1-7]%-%d%d?%d?%d?%d?$' ) and
       not id:match( '^[1-7]%-%d%d?%d?%d?%d?%.%d*[1-9]$' ) then
        return false
    end
    return id
end

function p.uscongressLink( id, label )
    --P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
    if not id:match( '^[A-Z]00[01]%d%d%d$' ) then
        return false
    end
    return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..(label or 'Congresso dos EUA')..']' --no https as of 9/2019
end

function p.vcbaLink( id, label )
    --P8034's format regex: \d{3}\/[1-9]\d{0,5} (e.g. 494/9793)
    if not id:match( '^%d%d%d\/[1-9]%d?%d?%d?%d?%d?$' ) then
        return false
    end
    id = id:gsub('\/', '_')
    return '[https://opac.vatlib.it/auth/detail/'..id..' '..(label or 'Vaticano')..']'
end

function p.viafLink( id, label )
    --P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)
    if not id:match( '^[1-9]%d%d?%d?%d?%d?%d?%d?%d?$' ) and
       not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d?%d?%d?$' ) then
        return false
    end
    -- If the "VIAF" entry at [[:m:Interwiki map]] would resolve to "https://viaf.org/viaf/$1" (rather than "http://viaf.org/viaf/$1", as it currently still does),
    -- then the code below could change from '[https://viaf.org/viaf/'..id..' '..id..']' to '[[:VIAF:'..id..'|'..id..']]'.
    return '[https://viaf.org/viaf/'..id..' '..(label or 'VIAF')..']'
end

function p.worldcatidLink( id, label )
    --P7859's format regex: viaf-\d+|lccn-n[a-z]?[0-9\-]+|n[pc]-.+ (e.g. viaf-100181709, lccn-n78-95332, np-verbeck,%20william$1861)
    if not id:match( '^viaf%-%d+$' ) and
       not id:match( '^lccn%-n[a-z]?[0-9%-]+$' ) and
       not id:match( '^n[pc]%-.+$' ) then
        return false
    end
    return '[https://www.worldcat.org/identities/'..mw.uri.encode(id, 'PATH')..'/ '..(label or 'WorldCat')..']'
end

--[[=========================== Helper functions =============================]]

function p.append(str, c, length)
    while str:len() < length do
        str = c..str
    end
    return str
end

--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145
function p.getIsniCheckDigit( isni )
    local total = 0
    for i = 1, 15 do
        local digit = isni:byte( i ) - 48 --Get integer value
        total = (total + digit) * 2
    end
    local remainder = total % 11
    local result = (12 - remainder) % 11
    if result == 10 then
        return 'X'
    end
    return tostring( result )
end

--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid
--See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
function p.validateIsni( id )
    --P213 (ISNI) format regex: [0-9]{4} [0-9]{4} [0-9]{4} [0-9]{3}[0-9X] (e.g. 0000-0000-6653-4145)
    --P496 (ORCID) format regex: 0000-000(1-[5-9]|2-[0-9]|3-[0-4])\d{3}-\d{3}[\dX] (e.g. 0000-0002-7398-5483)
    id = id:gsub( '[ %-]', '' ):upper()
    if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dX]$' ) then
        return false
    end
    if p.getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
        return false
    end
    return id
end

function p.splitLccn( id )
    --P244's format regex: (n|nb|nr|no|ns|sh)([4-9][0-9]|00|20[0-1][0-9])[0-9]{6} (e.g. n78039510)
    if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
        id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
    end
    if id:match( '^%l%l?%l?/%d%d%d?%d?/%d+$' ) then
        return mw.text.split( id, '/' )
    end
    return false
end

--[[==========================================================================]]
--[[                    Wikidata & documentation functions                    ]]
--[[==========================================================================]]

function p.getIdsFromWikidata( itemId, property )
    local ids = {}
    local statements = mw.wikibase.getBestStatements( itemId, property )
    if statements then
        for _, statement in ipairs( statements ) do
            if statement.mainsnak.datavalue then
                table.insert( ids, statement.mainsnak.datavalue.value )
            end
        end
    end
    return ids
end


--[[==========================================================================]]
--[[                              Configuration                               ]]
--[[==========================================================================]]

-- Please use "<parameter> (identificador)" redirects rather than linking directly to the target page.
-- This reduces clutter in "What links here" on both the redirect and the target,
-- and improves reverse lookup of articles where a manifestation of each identifier is used.

-- p.conf table basic format: { 'parameter name', propertyId # in Wikidata, formatting/validation function, section, example ID for documentation }
-- p.conf table optional named parameters:
--  link: to override the link in the documentation (defaults to "<parameter> (identifier)")
--  category: to override the ID in category names (defaults to "... with <parameter> identifiers")
--  prefix: to include a prefix (usually a wikilink explaining what the identifier is) before the external link itself
p.conf = {
    { 'GeoNames', 1566, 'p.getIdsFromWikidata' ,'Lugares' },
    { 'Booking.com', 3607, 'p.getIdsFromWikidata', 'Lugares' },
    { 'GeoNames', 1566, 'p.getIdsFromWikidata' ,'Lugares' },
    { 'MusicBrainzArea', 982, 'p.getIdsFromWikidata','Lugares' },

    { 'AAG', 3372, p.aagLink, 'Galerias de arte e museus', '1' },
    { 'ACM-DL', 864, p.acmLink, 'Bases de dados científicas', '12345678901', link = 'ACM DL (identificador)' },
    { 'ADB', 1907, p.adbLink,'Dicionários bibliográficos', 'barton-sir-edmund-toby-71' },
    { 'admiralty', 3562, p.admiraltyLink, 'Identificadores de faróis', 'D1204.1', prefix='[[Instituto Hidrográfico do Reino Unido|Admiralty]]'},
    { 'AGSA', 6804, p.agsaLink, 'Galerias de arte e museus', '3625' },
    { 'ARLHS', 2980, p.ARLHSLink, 'Identificadores de faróis', 'LAT023'},
    { 'autores.uy', 2558, p.autoresuyLink, 'Dicionários bibliográficos', '12345' },
    { 'AWR', 4186, p.awrLink, 'Dicionários bibliográficos', 'PR00768b' },
    { 'BIBSYS', 1015, p.bibsysLink, 'Bibliotecas nacionais', '1234567890123' },
    { 'Bildindex', 2092, p.bildLink, 'Institutos de pesquisa de arte', '1' },
    { 'BLBNB', 4619, p.blbnbLink, 'Bibliotecas nacionais', '001027092'},
    { 'BNC', 1890, p.bncLink, 'Bibliotecas nacionais', '123456789' },
    { 'BND', 5691, p.bndLink, 'Bibliotecas nacionais', '22954' },
    { 'BNE', 950, p.bneLink, 'Bibliotecas nacionais', 'XX1234567' },
    { 'BNF', 268, p.bnfLink, 'Bibliotecas nacionais', '123456789' },
    { 'Botanist', 428, p.botanistLink , 'Bases de dados científicas', 'L.' },
    { 'BPN', 651, p.bpnLink , 'Dicionários bibliográficos', '12345678' },
    { 'CANTIC', 1273, p.canticLink, 'Bibliotecas nacionais', 'a12345678' },
    { 'CCG', 3920, p.ccgLink, 'Identificadores de faróis', 'A1761', prefix='[[CCG (identificador)|CCG]]' },
    { 'CINII', 271, p.ciniiLink, 'Bases de dados científicas', 'DA12345678', link = 'CiNii (identificador)' },
    { 'CWGC', 1908, p.cwgcLink, 'Outro', '1234567' },
    { 'DAAO', 1707, p.daaoLink, 'Institutos de pesquisa de arte', 'rolf-harris' },
    { 'DBLP', 2456, p.dblpLink, 'Bases de dados científicas', '123/123' },
    { 'DIB',  6829, p.dibLink, 'Dicionários bibliográficos', 'a1234' },
    { 'DSI', 2349, p.dsiLink, 'Institutos de pesquisa de arte', '1538' },
    { 'EMU', 4613, p.emuLink, 'Bibliotecas nacionais', '15409' },
    { 'FAST', 2163, p.fastLink, 'Outro', '1' },
    { 'FNZA', 6792, p.fnzaLink, 'Institutos de pesquisa de arte', '12' },
    { 'GND', 227, p.gndLink, 'Geral', '4079154-3' },
    { 'Google Scholar', 1960, p.googleLink, 'Bases de dados científicas', 'QPdLuj8AAAAJ' },
    { 'HDS', 902, p.hdsLink, 'Outro', '050123' },
    { 'IAAF', 1146, p.iaafLink, 'Outro', '123' },
    { 'ICCU', 396, p.iccuLink, 'Bibliotecas nacionais', 'IT\\ICCU\\CFIV\\000163' }, --formerly SBN
    { 'ICIA', 1736, p.iciaLink, 'Institutos de pesquisa de arte', '1' },
    { 'IEU', 9070, p.ieuLink, 'Outro', 'N\\A\\NationalAcademyofArtandArchitecture' },
    { 'ISNI', 213, p.isniLink, 'Geral', '0000-0000-6653-4145', prefix = '[[ISNI (identificador)|ISNI]]' },
    { 'ITAU', 4399, p.itauLink, 'Outro', 'pessoa1461/burle-marx'},
    { 'Joconde', 347, p.jocondeLink, 'Institutos de pesquisa de arte', '12345678901' },
    { 'KULTURNAV', 1248, p.kulturnavLink, 'Institutos de pesquisa de arte', '12345678-1234-1234-1234-1234567890AB', link = 'KulturNav (identificador)' },
    { 'LCCN', 244, p.lccnLink, 'Bibliotecas nacionais', 'n78039510' },
    { 'LIR', 886, p.lirLink, 'Outro', '1' },
    { 'LNB', 1368, p.lnbLink, 'Bibliotecas nacionais', '123456789' },
    { 'Léonore', 640, p.leonoreLink, 'Outro', 'LH//1/1', prefix = '[[Léonore (identificador)|Léonore (França)]]' },
    { 'MBA', 434, p.mbaLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MBAREA', 982, p.mbareaLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MBI', 1330, p.mbiLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MBL', 966, p.mblLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MBP', 1004, p.mbpLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MBRG', 436, p.mbrgLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MBS', 1407, p.mbsLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MBW',  435, p.mbwLink, 'Outro', '12345678-1234-1234-1234-1234567890AB' }, --special cat name
    { 'MGP', 549, p.mgpLink, 'Bases de dados científicas', '123456' },
    { 'NARA', 1225, p.naraLink, 'Outro', '12345678' },
    { 'NCL', 1048, p.nclLink, 'Bibliotecas nacionais', '1081436' },
    { 'NDL', 349, p.ndlLink, 'Bibliotecas nacionais', '012345678' },
    { 'NGA', 3563, p.ngaLink, 'Identificadores de faróis', '114-5592'},
    { 'NGV', 2041, p.ngvLink, 'Galerias de arte e museus', '12354' },
    { 'NKC', 691, p.nkcLink, 'Bibliotecas nacionais', 'abcd12345678901234' },
    { 'NLA', 409, p.nlaLink, 'Bibliotecas nacionais', '123456789012' },
    { 'NLG', 3348, p.nlgLink, 'Bibliotecas nacionais', '12345678' },
    { 'NLI', 949, p.nliLink, 'Bibliotecas nacionais', '123456789' },
    { 'NLK', 5034, p.nlkLink, 'Bibliotecas nacionais', 'KAB197000000' },
    { 'NLP', 1695, p.nlpLink, 'Bibliotecas nacionais', '9810123456789012345' },
    { 'NLR', 1003, p.nlrLink, 'Bibliotecas nacionais', '123456789' },
    { 'NSK', 1375, p.nskLink, 'Bibliotecas nacionais', '123456789' },
    { 'NTA', 1006, p.ntaLink, 'Bibliotecas nacionais', '12345678X' },
    { 'ORCID', 496, p.orcidLink, 'Geral', '0000-0002-7398-5483', prefix = '[[ORCID (identificador)|ORCID]]' },
    { 'PIC', 2750, p.picLink, 'Institutos de pesquisa de arte', '1' },
    { 'PLWABN',  7293, p.plwabnLink, 'Bibliotecas nacionais', '9812345678905606' },
    { 'Publons', 3829, p.publonsLink, 'Bases de dados científicas', '2776255' },
    { 'PWN', 7305, p.PWNLink, 'Geral', '3878797' },
    { 'RID', 1053, p.ridLink, 'Bases de dados científicas', 'A-1234-1934' },
    { 'RISM', 5504, p.rismLink, 'Outro', 'pe1',  prefix = '[[RISM (identificador)|RISM (França)]]' },
    { 'RERO', 3065, p.reroLink, 'Outro', '02-A012345678', prefix = '[[RERO (identificador)|RERO (Suíça)]]' },
    { 'RKDartists', 650, p.rkdartistsLink, 'Institutos de pesquisa de arte', '123456' },
    { 'RKDID', 350, p.rkdidLink, 'Institutos de pesquisa de arte', '123456' },
    { 'RSL', 947, p.rslLink, 'Bibliotecas nacionais', '123456789' },
    { 'Scopus', 1153, p.scopusLink, 'Bases de dados científicas', '7005487412' },
    { 'SELIBR', 906, p.selibrLink, 'Bibliotecas nacionais', '123456' },
    { 'SIKART', 781, p.sikartLink, 'Institutos de pesquisa de arte', '123456789' },
    { 'SNAC-ID', 3430, p.snacLink, 'Outro', 'A' },
    { 'SUDOC', 269, p.sudocLink, 'Outro', '026927608', prefix = '[[SUDOC (identificador)|SUDOC (França)]]' },
    { 'TA98', 1323, p.ta98Link, 'Bases de dados científicas', 'A12.3.45.678' },
    { 'TDVİA', 7314, p.tdviaLink, 'Outro', 'asim-b-behdele' },
    { 'TePapa', 3544, p.tepapaLink, 'Galerias de arte e museus', '1' },
    { 'TLS',  1362, p.tlsLink, 'Outro', 'Abcd' },
    { 'Trove', 1315, p.troveLink, 'Outro', '12345678', prefix = '[[Trove (identificador)|Trove (Austrália)]]' }, --formerly NLA-person
    { 'UKPARL', 6213, p.ukparlLink, 'Outro', 'AQUupyiR' },
    { 'ULAN', 245, p.ulanLink, 'Institutos de pesquisa de arte', '500123456' },
    { 'USCG', 3723, p.uscgLink, 'Identificadores de faróis', '6-0695', prefix='[[USCG (identificador)|USCG]]'},
    { 'USCongress', 1157, p.uscongressLink, 'Outro', 'A000123', link = 'US Congress (identificador)' },
    { 'VcBA', 8034, p.vcbaLink, 'Bibliotecas nacionais', '494/9793' },
    { 'VIAF', 214, p.viafLink, 'Geral', '123456789', prefix = '[[VIAF (identificador)|VIAF]]' },
    { 'WORLDCATID', 7859, p.worldcatidLink, 'Geral', 'lccn-n78-95332', link = 'WorldCat Identities (identificador)' },
}

-- Legitimate aliases to p.conf, for convenience
-- Format: { 'alias', 'parameter name in p.conf' }
p.aliases = {
    { 'DNB', 'GND' }, --Deutsche Nationalbibliothek -> Gemeinsame Normdatei
    { 'Leonore', 'Léonore' }, --alias name without diacritics
    { 'leonore', 'Léonore' }, --lowercase variant without diacritics
    { 'MusicBrainz', 'MBA' },
    { 'MusicBrainz artist', 'MBA' },
    { 'MusicBrainz label', 'MBL' },
    { 'MusicBrainz release group', 'MBRG' },
    { 'MusicBrainz work', 'MBW' },
    { 'SBN', 'ICCU' }, --SBN alias to be deprecated at a later stage
    { 'TDVIA', 'TDVİA' }, --alias name without diacritics
    { 'tdvia', 'TDVİA' }, --lowercase variant without diacritics
}

-- Deprecated aliases to p.conf; tracked in [[Category:Articles with deprecated authority control identifiers]]
-- Format: { 'deprecated parameter name', 'replacement parameter name in p.conf' }
p.deprecated = {
    { 'GKD', 'GND' },
    { 'PND', 'GND' },
    { 'RLS', 'RSL' },
    { 'SWD', 'GND' },
    { 'NARA-organization', 'NARA' },
    { 'NARA-person', 'NARA' },
}

--[[==========================================================================]]
--[[                                   Main                                   ]]
--[[==========================================================================]]

function p.authorityControl( frame )
    local resolveEntity = require( 'Module:ResolveEntityId' )
    local parentArgs = frame:getParent().args --WD IDs added here later
    local iParentArgs = 0 --count original/manual parent args only later
    local worldcatCat = ''
    local elementsCat = ''
    local multipleIdCat = ''
    local suppressedIdCat = ''
    local suppressedIdCatArts = ''
    local deprecatedIdCat = ''
    local differentOnWDCat = ''
    local sameOnWDCat = ''
    local stateCat = ''
    
    --redirect aliases to proper parameter names
    for _, a in pairs( p.aliases ) do
        local alias, param = a[1], a[2]
        if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[alias] then
            parentArgs[param] = parentArgs[alias]
        end
    end
    
    --redirect deprecated parameters to proper parameter names, and assign tracking cat
    for _, d in pairs( p.deprecated ) do
        local dep, param = d[1], d[2]
        if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[dep] then
            parentArgs[param] = parentArgs[dep]
            if namespace == 0 then
                deprecatedIdCat = '[[Categoria:!Artigos com identificadores de controle de autoridade obsoletos|'..dep..']]'
            end
        end
    end
    
    --use QID= parameter for testing/example purposes only
    local itemId = nil
    if namespace ~= 0 then
        local qid = parentArgs['qid'] or parentArgs['QID']
        if qid then
            itemId = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')
            itemId = resolveEntity._id(itemId) --nil if unresolvable
        end
    else
        itemId = mw.wikibase.getEntityIdForCurrentPage()
    end
    
    --Wikidata fallback if available
    if itemId then
        local suppressedIdCount = 0
        local iMatches = 0
        for _, params in ipairs( p.conf ) do
            if params[2] > 0 then
                local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]
                if val == nil or val == '' then
                    local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[2] )
                    if wikidataIds[1] then
                        if val == '' and (namespace == 0 or testcases) then
                            suppressedIdCount = suppressedIdCount + 1
                            suppressedIdCat = '[[Categoria:!Artigos com identificadores de controle de autoridade suprimidos|'..params[1]..']]'
                        else
                            parentArgs[params[1]] = wikidataIds[1] --add ID from WD
                        end
                    end
                else
                    iParentArgs = iParentArgs + 1
                    local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[2] )
                    if wikidataIds[1] and differentOnWDCat == '' then
                        local bMatch = false
                        for _, wd in pairs( wikidataIds ) do
                            if val == wd then
                                iMatches = iMatches + 1
                                bMatch = true
                            end
                        end
                        if bMatch == false then
--                            differentOnWDCat = '[[Category:Pages using authority control with parameters different on Wikidata|'..params[1]..']]'
        end    end    end    end    end
        if iMatches > 0 and iMatches == iParentArgs then
            sameOnWDCat = '[[Categoria:!Artigos com identificadores de controle de autoridade com todos os parâmetros iguais a Wikidata]]'
        end
        if parentArgs['arts'] == 'arts' and suppressedIdCount > 0 then
            if namespace == 0 or testcases then
                local s = 's'
                if suppressedIdCount == 1 then s = '' end
--                local sCat = 'ACArt with '..suppressedIdCount..' suppressed element'..s
--                suppressedIdCatArts = addCat(sCat)
            end
        end
    end
    
    --configure rows
    local rct = 0
    local sectionOrder = {
        'Geral',
        'Bibliotecas nacionais',
        'Galerias de arte e museus',
        'Institutos de pesquisa de arte',
        'Dicionários bibliográficos',
        'Bases de dados científicas',
        'Identificadores de faróis',
        'Outro',
    'identificadores taxonómicos',
    'Cinema',
    'informática',
    'Bases de datos taxonómicas',
    'Identificadores médicos',
    'Identificadores químicos',
    'Identificadores biológicos',
    'Identificadores astronómicos',
    'Lugares',
    'Arquitetura'
    }
    local sections = {
        ['Geral'] = {},
        ['Bibliotecas nacionais'] = {},
        ['Galerias de arte e museus'] = {},
        ['Institutos de pesquisa de arte'] = {},
        ['Dicionários bibliográficos'] = {},
        ['Bases de dados científicas'] = {},
        ['Identificadores de faróis'] = {},
        ['Outro'] = {},
    ['identificadores taxonómicos'] = {},
    ['Cinema'] = {},
    ['informática'] = {},
    ['Bases de datos taxonómicas'] = {},
    ['Identificadores médicos'] = {},
    ['Identificadores químicos'] = {},
    ['Identificadores biológicos'] = {},
    ['Identificadores astronómicos'] = {},
    ['Lugares'] = {},
    ['Arquitetura'] = {},
    }
    --don't show NLP if PLWABN is present, since they both go to the National Library of Poland
    --and the library has deprecated NLP IDs in favor of PLWABN IDs
    if parentArgs.PLWABN or parentArgs.plwabn then
        parentArgs.NLP = ''
        parentArgs.nlp = ''
    end
    for _, params in ipairs( p.conf ) do
        local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]
        local tval, tlinks = {}, {} --init tables
        if val and val ~= '' and type(params[3]) == 'function' then
            table.insert( tval, val )
            if params.prefix then
                table.insert( tlinks, params[3]( val, '1' ) )
            else
                table.insert( tlinks, params[3]( val ) )
            end
        end
        --collect other unique vals (IDs) from WD, if present
        if itemId and tval[1] then
            local nextIdVal = 2
            local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[2] )
            for _, v in pairs( wikidataIds ) do
                local bnew = true
                for _, w in pairs( tval ) do
                    if v == w then bnew = false end
                end
                if bnew then
                    table.insert( tval, v )
                    table.insert( tlinks, params[3]( v, tostring(nextIdVal) ) )
                    nextIdVal = nextIdVal + 1
                end
            end
        end
        --assemble
        if tval[1] then
            table.insert( sections[params[4]], p.createRow( params[1], tval, nil, tlinks, true, params.category, params.prefix) )
            rct = rct + 1
--            if tval[2] then
--                multipleIdCat = p.getCatForId( 'multiple' )
--            end
        end
    
    end
    
    --WorldCat-VIAF & WorldCat-LCCN
    local worldcatId = parentArgs['worldcatid'] or parentArgs['WORLDCATID']
    if worldcatId == nil then --if WORLDCATID absent but unsuppressed
        local viafId = parentArgs['viaf'] or parentArgs['VIAF']
        local lccnId = parentArgs['lccn'] or parentArgs['LCCN']
        if viafId and viafId ~= '' and p.viafLink( viafId ) then --VIAF must be present, unsuppressed, & validated
            table.insert( sections['Geral'], p.createRow( 'VIAF', viafId, '[https://www.worldcat.org/identities/containsVIAFID/'..viafId..' WorldCat (via VIAF)]', nil, false ) )
            if namespace == 0 then
                worldcatCat = '[[Categoria:!Artigos com identificadores WorldCat-VIAF]]'
            end
            rct = rct + 1
        elseif lccnId and lccnId ~= '' and p.lccnLink( lccnId ) then --LCCN must be present, unsuppressed, & validated
            local lccnParts = p.splitLccn( lccnId )
            if lccnParts and lccnParts[1] ~= 'sh' then
                local lccnIdFmtd = lccnParts[1]..lccnParts[2]..'-'..lccnParts[3]
                table.insert( sections['Geral'], p.createRow( 'LCCN', lccnId, '[https://www.worldcat.org/identities/lccn-'..lccnIdFmtd..' WorldCat (via Library of Congress)]', nil, false ) )
                if namespace == 0 then
                    worldcatCat = '[[Categoria:!Artigos com identificadores WorldCat-LCCN]]'
                end
            end
            rct = rct + 1
        end
    end
    
    --configure Navbox
    local outString = ''
    local extrap = true
    local extra = parentArgs.extralist
    if extra == nil or extra == '' then
        extrap = false
    end
    if rct > 0 or extrap then
        local Navbox = require('Module:Navbox')
        local sectionID = 1
        local args = { pid = 'identifiers' } -- #target the list of identifiers
        if testcases and itemId then args = { pid = 'identifiers', qid = itemId } end --expensive
        local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}
        local navboxArgs = {
            name  = 'Controle de autoridade',
            navboxclass = 'authority-control',
            bodyclass = 'hlist',
            state = parentArgs.state or parentArgs.estado or 'autocollapse',
            navbar = 'off'
        }
        for _, sectName in ipairs(sectionOrder) do
            if #sections[sectName] ~= 0 then
                navboxArgs['group'..sectionID] = sectName
                navboxArgs['list'..sectionID] = table.concat(sections[sectName])
                sectionID = sectionID + 1
            end
        end
        if extrap then
            if parentArgs.extragroup then
                navboxArgs['group'..sectionID] = parentArgs.extragroup
            else
                navboxArgs['group'..sectionID] = 'Adicional'
            end
            navboxArgs['list'..sectionID] = extra
            sectionID = sectionID + 1
        end
        if navboxArgs.list2 then
            navboxArgs.title = '[[Ajuda:Controle de autoridade|Controle de autoridade]]'..pencil
        else
            local sect = navboxArgs.group1
            if sect == 'Geral' or sect == 'Outro' or sect == 'Adicional' then
                -- Just say "Authority control" with no label if only general or only other IDs are present
                -- since "general" is redundant and "other" is silly when there's nothing to contrast it with
                navboxArgs.group1 = '[[Ajuda:Controle de autoridade|Controle de autoridade]]'..pencil
            else
                navboxArgs.group1 = '[[Ajuda:Controle de autoridade|Controle de autoridade: '..sect..']] '..pencil
            end
        end
        outString = Navbox._navbox(navboxArgs)
    end
    
    --auxCats
    if rct == 0 or rct >= 25 then
        if namespace == 0 or testcases then
            local eCat = '!CA com '..rct..' elementos'
            elementsCat = addCat(eCat)
        end
    end
    if parentArgs.state or parentArgs.estado then
        if namespace == 0 or testcases then
            local sCat
            if parentArgs.state or parentArgs.estado== 'collapsed' or 'colapsar' then sCat = '!Artigos que utilizam colapsar em "Controle de autoridade"'
            elseif parentArgs.state == 'expanded' or 'expandir' then sCat = '!Artigos que utilizam expandir em "Controle de autoridade"'
            elseif parentArgs.state == 'autocollapse' or 'autocolapsar' then sCat = '!Artigos que utilizam autocolapsar em "Controle de autoridade"'
            else sCat = '' --'AC using state parameter: other'
            end
            stateCat = addCat(sCat)
        end
    end
    local auxCats = stateCat --worldcatCat..elementsCat..multipleIdCat..suppressedIdCat..suppressedIdCatArts..
--                    deprecatedIdCat..differentOnWDCat..sameOnWDCat..stateCat
    if testcases then
        auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Categoria)', '%1:%2') --for easier checking
    end
    
    --out
    outString = outString --..auxCats
    if namespace ~= 0 then
        outString = mw.ustring.gsub(outString, '(%[%[)(Categoria:!Artigos)', '%1:%2') --by definition
    end
    
    return outString
end

return p