local math_mod = require "Módulo:Math"
local params = require "Módulo:Conversão/Dados"
local linguistic = require "Módulo:Linguística"
local defaultlang = 'pt'

local p = {}

local i18n = {
    invalidunitcat = '!Página com unidade de medida não suportada',
    invalidsourceunit = '$1 (unidade não suportada)',
    invalidtargetunit = 'unidade de destino não suportada $1',
    typemismatch = 'impossível converter $1 em $2'
}

local function numString(val, rounding, displayformat) -- transformar um número em uma string
    if rounding then
        val = math_mod._round( val, rounding )
    end
    val = mw.getContentLanguage():formatNum(val)
    if displayformat and displayformat.suffix then
        val = val .. suffix
    end
    return val
end

local function convert(value, sourceunitdata, targetunitdata) -- converter um valor numérico para seu equivalente em outra unidade de medida
    if not value then
        return nil
    end
    if type(value) ~= 'number' then
        return error("bad datatype: " .. type(value))
    end
    if (not sourceunitdata) or (not targetunitdata) then
        return value
    end
    return value * sourceunitdata[2] / targetunitdata[2]
end

function p.displayvalue(val, sourceunit, displayformat, errhandling) -- exibe um valor formatado)
    
    -- preparação de parâmetros
    local numval = tonumber(val)
    if not numval then -- se os dados são incomuns, a função de chamada é deixada para se defender
        return val
    end
    
    if not displayformat or type(displayformat) ~= 'table' then
        displayformat = {}
    end
    local showunit, showlink, targetunit = displayformat.showunit, displayformat.showlink, displayformat.targetunit
    local rounding = displayformat.rounding or 2
    -- recuperação de dados para unidades
    if sourceunit and not targetunit then
        targetunit = sourceunit
    end
    local sourceunitdata, targetunitdata = sourceunit, targetunit
    if type(sourceunitdata) ~= 'table' then
        sourceunitdata = params.units[sourceunit] or params.units[params.redirects[sourceunit]]
    end
    if type(targetunitdata) ~= 'table' then    
        targetunitdata = params.units[targetunit] or params.units[params.redirects[targetunit]]
    end

    local maintenancestr = ""    

    
    -- conversion

    local function invalidsourcefallback(val, sourceunit) -- text to be shown when source unit is not supported
        local str = tostring(val)
        if sourceunit:match('Q%d+') or sourceunit:match('q%d+') then -- wikidata item
            sourceunit = mw.wikibase.label(sourceunit)
        end
        return val .. ' ' .. i18n['invalidsourceunit']:gsub('$1', sourceunit) .. '[[Category:' .. i18n.invalidunitcat .. '|' .. sourceunit .. ']]'
    end


    -- escape if source unit is invalid
    if (sourceunit and targetunit) and (targetunit~= sourceunit) and (not sourceunitdata or not sourceunitdata[1]) then
        return invalidsourcefallback(val, sourceunit)
    end

    if sourceunit and (not sourceunitdata) then
        local label = sourceunit
        local item = sourceunit:match('q%d+') or sourceunit:match('Q%d+')
        local link
        local symbol
        if item then
            label = mw.wikibase.label(item)
            link = mw.wikibase.sitelink(item)
            if (displayformat.showunit) and (displayformat.showunit ~= 'long') then -- symbole retrieved only if needed (somewhat expensive)
                for _,statement in pairs(mw.wikibase.getBestStatements(item, 'P5061')) do
                    if statement['mainsnak']['snaktype'] == 'value' and statement['mainsnak']['datavalue']['value']['language'] == 'fr' then
                        symbol = statement['mainsnak']['datavalue']['value']['text']
                    end
                end
            end
        end
        sourceunitdata = {nil, 1, symbol or label, item, link, label, label}
        targetunit, targetunitdata  = sourceunit, sourceunitdata
    end
    
    -- warn if targetunit is unknown
    if targetunit and (not targetunitdata) then
        targetunit, targetunitdata = sourceunit, sourceunitdata
        maintenancestr = maintenancestr .. " "  .. i18n['invalidtargetunit']:gsub('$1', targetunit) .. '[[Category:' .. i18n.invalidunitcat .. '|' .. targetunit .. ']]'
    end

    -- check for type mismatch
    if (sourceunitdata and targetunitdata) and (targetunitdata[1] ~= sourceunitdata[1]) then
        local errmsg = i18n.typemismatch
        errmsg = errmsg:gsub('$1', sourceunit)
        errmsg = errmsg:gsub('$2', targetunit)
        maintenancestr = maintenancestr .. '(' .. errmsg .. ')'
        targetunit, targetunitdata = sourceunit, sourceunitdata
    end

    -- convert if needed
    if (sourceunit and targetunit) and (sourceunit ~= targetunit) then
        numval = convert(numval, sourceunitdata, targetunitdata)
    end
    
    if displayformat.raw == true then -- número convertível num número não formatado, salvo categoria de manutenção
        return (tostring(numval) or "") .. maintenancestr
    end

    local numstr = numString(numval, rounding)

    -- visor da unidade
    local unitstr, link
    if not targetunitdata then -- para evitar os bugs
        targetunitdata = {}
    end
    if showunit == 'long' then -- format long = mostre a unidade inteira
        if (numval or 0) >= 2 then
            unitstr = targetunitdata[7]
        else
            unitstr = targetunitdata[6]
        end
        if ((numval or 0) > 999999) and ( (numval or 0)% 1000000 == 0) then
            unitstr = linguistic.of(unitstr) -- 10 000 000 "de" tonnes
        end
             
    elseif showunit and (showunit ~= '-') then
        unitstr = targetunitdata[3]
    end

    -- showlink

    local link
    if (type(showlink) == 'string') and (showlink == '-') then
        link = nil
    elseif type(showlink) == 'string' then
        link = showlink
    elseif showlink then
        link = targetunitdata[5]
    end
    
    if (unitstr and link) then
        unitstr = '[[' .. link .. '|' .. unitstr .. ']]'
    end
    
    local str = numstr
    if (unitstr and unitstr ~= '') then
        str = str .. ' ' .. unitstr
    end
    str = str .. maintenancestr
    if complement then
        if sourceunit then
            str = str .. " " .. linguistic.of(complement) -- "10 kg de bambous"
        else
            str = str .. " " .. complement -- "3000 véhicules"
        end
    end
    return str

end

function p.display(frame)
    local args = frame.args
    local value, origunit, targetunit = args[1], args[2], args[3]
    local rounding = args.rounding
    local showlink, showunit =     args.showlink, args.showunit
    if showunit == 'true' then
        showunit = true
    end
    if showlink == 'true' then
        showlink = true
    end
    displayformat = {showunit = showunit, showlink = showlink, rounding = rounding, targetunit = targetunit}
    return p.displayvalue(value, origunit, displayformat)
end
return p