-- Extensão do Módulo:Info para adicionar funções relacionadas a localidades

ext = {}
ext.especial = {}

-- Converte coordenada do formato G M S para fração
local gms_f = function(coor)
    local quadrante = string.match(coor, '[NSLO]$')
    local div = 1
    local resp = 0
    for n in string.gmatch(coor, '(%-?%d+)') do
        n = tonumber(n)
        resp = resp + n / div
        div = div * 60
    end
    return resp * ((quadrante == 'S' or quadrante == 'O') and -1 or 1)
end

-- Converte coordenada do formato fração para G M S
local f_gms = function(coor, latorlong)
    local abs = math.abs(tonumber(coor))
    local grau = math.floor(abs)
    local min = math.floor((abs - grau) * 60)
    local seg = math.floor(((abs - grau) * 60 - min) * 60)
    local hemi = tonumber(coor) >= 0 and (latorlong == 'lat' and 'N' or 'L') or (latorlong == 'lat' and 'S' or 'O')
    return string.format('%02d° %02d\' %02d" %s', grau, min, seg, hemi)
end

-- Retorna coordenadas no formato G° M' S" [NS] G° M' S" [LO] aceitando vários formatos de entrada
local coordenadas = function()
    local coor = params['coordenadas']
    if not coor or  coor == 'wikidata' or coor == 'Wikidata' then
        if not wd then
            require('Módulo:Info/wd')
        end
        if wd.props and wd.props['P625'] then
            local valor = wd.props['P625'][1]['mainsnak']['datavalue']['value']
            ext.coorf = {valor['latitude'], valor['longitude']}
            ext.coorgms = {f_gms(valor['latitude'], 'lat'), f_gms(valor['longitude'], 'long')}
            return
        end
    end
    if not coor then
        return
    end
    local formato, hemi, lat, long
    local latn, longn
    for match in string.gmatch(coor, '[%-0-9%.NSLOWE]+') do
        if not formato then
            if string.match(match, '^%-?%d+%.%d+$') then
                formato = 'g.f'
                lat = match
                if string.match(match, '^%-') then
                    hemi = '+-'
                end
            elseif string.match(match, '^%d+$') then
                formato = 'gms'
                lat = match .. '°'
                latn = 1
            else
                return nil
            end
        elseif string.match(match, '^[NSLOEW]$') then
            if hemi == '+-' then
                return 'erro nas coordenadas, sinal de menos junto com ' .. match
            elseif formato == 'g.f' then
                if not long and match == 'S' then
                    lat = '-' .. lat
                elseif long and (match == 'O' or match == 'W') then
                    long = '-' .. long
                end
            else
                if long then
                    long = long .. ' ' .. (match == 'W' and 'O' or match == 'E' and 'L' or match)
                else
                    lat = lat .. ' ' .. match
                end
            end
            hemi = 'NSLO'
        elseif string.match(match, '^%d+$') then
            if not long and formato == 'gms' and latn < 3 and not string.match(lat, '[NS]$') then
                lat = lat .. ' ' .. match .. (latn == 1 and "'" or latn == 2 and '"')
                latn = latn + 1
            elseif not long and formato == 'gms' then
                long = match .. '°'
                longn = 1
            elseif long and formato == 'gms' and longn < 3 and not string.match(long, '[LO]$') then
                long = long .. ' ' .. match .. (longn == 1 and "'" or longn == 2 and '"')
                longn = longn + 1
            else
                return 'erro nas coordenadas'
            end
        elseif not long and formato == 'g.f' and string.match(match, '^%-?%d+%.%d+$') then
            long = match
        else
            return 'erro nas coordenadas: ' .. coor
        end
    end
    if not lat or not long then
        return string.format('erro nas coordenadas: coor=%s; lat=%s; latn=%s; long=%s; longn=%s; hemi=%s; formato=%s',
          coor, lat or 'nil', latn or 'nil', long or 'nil', longn or 'nil', hemi or 'nil', formato or 'nil')
    end
    if formato == 'gms' then
        if not string.match(lat, '%d%d?° %d%d?\' %d%d?" [NS]') and not string.match(lat, '%d%d?° %d%d?\' [NS]')
          and not string.match(lat, '%d%d?° [NS]') then
            return 'erro nas coordenadas, lat = ' .. lat
        elseif not string.match(long, '%d%d?° %d%d?\' %d%d?" [LO]') and not string.match(long, '%d%d?° %d%d?\' [LO]')
          and not string.match(long, '%d%d?° [LO]') then
            return 'erro nas coordenadas, long = ' .. long
        end
        ext.coorgms = {lat, long}
        ext.coorf = {gms_f(lat), gms_f(long)}
    else
        ext.coorf = {lat, long}
        ext.coorgms = {f_gms(lat, 'lat'), f_gms(long, 'long')}
    end
end

local mapa_imagem = function(mapa, lat, long)
    local imagem, N, O, S, L = string.match(mapa, '^([^.]+%.%a%a%a) (%-?[%d.]+) (%-?[%d.]+) (%-?[%d.]+) (%-?[%d.]+)')
    if not imagem then
        local paises = mw.loadData('Módulo:Países')
        mapa = paises and paises[mapa] and (type(paises[mapa]) == 'string' and paises[paises[mapa]]['mapa'] or
          paises[mapa]['mapa'])
        if mapa then
            imagem, N, O, S, L = string.match(mapa, '([^.]+%.%a%a%a) (%-?[%d.]+) (%-?[%d.]+) (%-?[%d.]+) (%-?[%d.]+)')
            if not imagem then
                return nil
            end
        else
            return nil
        end
    end
    N, O, S, L = tonumber(N), tonumber(O), tonumber(S), tonumber(L)
    local top = math.floor(100 * (N - lat) / (N - S))
    local left = math.floor(100 * (long - O) / (L - O))
    local ponto = '<div style="position: absolute; z-index: 2; top: ' .. top .. '%; left: ' .. left ..
      '%; display: table"><div style="position: relative; left: -6px; top: -11px; line-height: 15px">[[Ficheiro:Red pog.svg|7px]]</div></div>'
    return '<div style="position: relative; width: 200px; margin: auto">[[Imagem:' .. imagem .. '|200px]]' .. ponto .. '</div>'
end

-- A função ext.extra é chamada pelo Módulo:Info antes de terminar de montar a infobox,
-- o que é retornado é colocado após a infobox
ext.extra = function()
    -- Coordenadas no topo do artigo --
    if not ext.coorgms and params['coordenadas'] then
        local erro = coordenadas()
        if erro then
            table.insert(debug.erros, erro)
        end
    end
    if ext.coorgms and params['coordenadas'] then
        local coor = ext.coorgms[1] .. ' ' .. ext.coorgms[2]
        local pagename = '&pagename=' .. mw.uri.encode(mw.title.getCurrentTitle().fullText)
        local paramcoor = string.gsub(string.gsub(string.gsub(coor, '[^0-9NSLO]+', '_'), 'O', 'W'), 'L', 'E')
        local coordenadas = [=[<div id="coordinates" class="noprint plainlinks" style="padding:0.5em">[[Coordenadas geográficas|Coordenadas]]: '''[//tools.wmflabs.org/geohack/geohack.php?language=pt]=] ..
          pagename .. '&params=' .. paramcoor ..
          ' <span title="Mapas, fotos aéreas e outros dados para este local">' .. coor .. "</span>]'''</div>"
        return coordenadas
    end
end

ext.band = {ordem={}}

ext.especial['bandeira'] = function(campo, i, tipo)
    if not campo[1] or campo[1] == '' then
        return nil
    end
    local dominio, img = string.match(campo[1], '^%[%[(%w+):([^%]%|%.\n]+%.%w%w%w%w?)[%]%|]')
    if dominio then
        for _, d in pairs({'Ficheiro', 'Imagem', 'File', 'Image', 'ficheiro', 'imagem', 'file', 'image'}) do
            if dominio == d then
                dominio = nil
                break
            end
        end
        if dominio then img = nil end
    elseif string.match(campo[1], '^[^%]%|%.\n]+%.%w%w%w%w?$') then
        img = campo[1]
    end
    if not img then
        return nil
    end
    tipo = tipo or 'bandeira'
    tipo = tipo:sub(1, 1):upper() .. tipo:sub(2)
    if ext.band.tipo then
        local band = '[[Ficheiro:' .. ext.band.img .. '|100px]]'
        local band2 = '[[Ficheiro:' .. img .. '|60px]]'
        campo['rótulo'] = nil
        campo[1] = '<table style="text-align:center; width:100%"><tr><td>' .. band .. '</td><td>' ..
          band2 .. '</td></tr><tr><td>' .. ext.band.tipo .. '</td><td>' .. tipo .. '</td></tr></table>'
        campos[ext.band.pos] = campo
        return nil
    else
        ext.band.tipo = tipo
        ext.band.img = img
        ext.band.pos = i
        campo[1] = '[[Ficheiro:' .. img .. '|100px]]<br>' .. tipo
        campo['rótulo'] = nil
        return campo
    end
end

ext.especial['escudo'] = function(campo, i)
    return ext.especial['bandeira'](campo, i, 'brasão de armas')
end

ext.especial['brasão'] = function(campo, i)
    return ext.especial['bandeira'](campo, i, 'brasão de armas')
end

ext.especial['selo'] = function(campo, i)
    return ext.especial['bandeira'](campo, i, 'selo')
end

ext.especial['emblema'] = function(campo, i)
    return ext.especial['bandeira'](campo, i, 'emblema')
end

ext.especial['mapa'] = function(campo)
    local lat, long
    if ext.coorf then
        lat, long = ext.coorf[1], ext.coorf[2]
    else
        local erro = coordenadas()
        if not ext.coorf then
        	return
        elseif erro then
            table.insert(debug.erros, erro)
            return
        end
        lat, long = ext.coorf[1], ext.coorf[2]
    end
    local mapa = campo[1]
    local auto
    if mapa == 'auto' then
        if not wd then
            require('Módulo:Info/wd')
        end
        local p17 = wd.props and wd.props['P17']  -- propriedade 'país' no Wikidata
        if p17 then
            local qid = p17[1]['mainsnak']['datavalue']['value']['id']
            local mapaimg = mapa_imagem(qid, lat, long)
            if mapaimg then
                return {mapaimg}
            end
            table.insert(debug.erros, 'não existe mapa para ' .. (qid or 'nil') .. ', usando o mapa dinâmico')
        else
            table.insert(debug.erros, 'não existe a propriedade P17 no Wikidata, usando o mapa dinâmico')
        end
    elseif string.match(mapa, '^[Dd]inâmico') == nil then
        return {mapa_imagem(mapa, lat, long)}
    end
    lat, long = string.format('%.5f', lat), string.format('%.5f', long)
    local zoom = string.match(mapa, '^[Dd]inâmico zoom:(%d%d?)') or '5'
    local ponto = '{"type": "Feature", "geometry": {"type": "Point", "coordinates": [' .. long .. ', ' .. lat .. ']}}'
    local mapa = '<mapframe frameless width=200 height=200 zoom=' .. zoom .. ' latitude=' .. lat .. ' longitude=' ..
      long .. '>{"type": "FeatureCollection", "features": [' .. ponto .. ']}</mapframe>'
    mapa = '<div style="display:table; margin:0 auto 0 auto; padding: 14px 14px 0 0">' .. mapa .. '</div>'
    return {mw.getCurrentFrame():preprocess(mapa)}
end

ext.especial['área'] = function(campo, i)
    local area, populacao = string.match(campo[1] or '', '^([^;]-) *; *(.+)')
    local nomes = {}
    local total, pop
    local n = i + 1
    while campos[n] do
        if total and campos[n]['rótulo'] and nomes[campos[n]['rótulo']] then
        	pop = true
            -- Densidade populacional
            if campos[n]['rótulo'] and nomes[campos[n]['rótulo']] and campos[n][1] then
                local num = campos[n][1]:match('^[%d ]+')
                if num then
                    num = num:gsub(' ', '')
                    local dens = num / nomes[campos[n]['rótulo']]
                    dens = dens < 10 and math.floor(dens * 10) / 10 or math.floor(dens)
                    dens = string.gsub(dens, '%.', ',')
                    nomes[campos[n]['rótulo']] = nil
                    local campo = {['rótulo']='&nbsp;&nbsp;&nbsp;-&nbsp;densidade', [1]=dens .. ' hab/Km²'}
                    n = n + 1
                    table.insert(campos, n, campo)
                    if next(nomes) == nil then
                        break
                    end
                end
            end
        elseif not pop and (not total or next(nomes)) and campos[n]['rótulo'] and campos[n][1] and
          campos[n][1]:match('^%d') and (area and not total and campos[n]['rótulo'] == area or not area or total) then
            local num, uni = campos[n][1]:match('^([%d %.,]+)(.*)')
            if num then
                num = num:gsub(',', '.'):gsub(' ', '')
                num = tonumber(num)
                if uni:match('^[Kk]m²') or uni:match('^%[%[[^%]|]+|[Kk]m²%]%]') then
                    --pass
                elseif uni:match('^mi²') or uni:match('^sq%. ?mi') or uni:match('^%[%[[Mm]ilha quadrada') then
                    num = num * 2.589988
                else
                    table.insert(debug.erros, 'Não foi encontrada unidade Km² ou mi² em ' .. campos[n]['rótulo'] 
                      .. ' : ' .. campos[n][1])
                    num = nil
                end
                if not total then
                    total = num
                    nomes[populacao or campos[n]['rótulo']] = num
                elseif num then
                    -- Área relativa
                    local porcentagem = math.floor(num * 1000 / total) / 10
                    porcentagem = string.gsub(porcentagem, '%.', ',')
                    campos[n][1] = campos[n][1] .. ' (' .. porcentagem .. ' %)'
                    nomes[campos[n]['rótulo']] = num
                end
            end
        end
        n = n + 1
    end
end

return ext