--[=[ 2016-05-26
Expr
* max
* min
* TemplateMax
* TemplateMin
* booland
]=]



local messagePrefix = "lua-module-Expr-"
local l10nDef = {}
l10nDef[ "en" ] = {
    ErrorExpr  = "Error in mathematical expression, function#parameter"
}
l10nDef[ "de" ]  = {
    ErrorExpr  = "Fehler in mathematischem Ausdruck, Funktion#Parameter"
}


local function factory( say )
    -- Retrieve localized message string in content language
    -- Precondition:
    --     say  -- string; message ID
    -- Postcondition:
    --     Return some message string
    -- Uses:
    --     >  messagePrefix
    --     >  l10nDef
    --     mw.language.getContentLanguage()
    --     mw.message.new()
    local c = mw.language.getContentLanguage():getCode()
    local m = mw.message.new( messagePrefix .. say )
    local r = false
    if m:isBlank() then
        local l10n = l10nDef[ c ]
        if not l10n then
            l10n = l10nDef[ "en" ]
        end
        r = l10n[ say ]
    else
        m:inLanguage( c )
        r = m:plain()
    end
    if not r then
        r = "(((" .. say .. ")))"
    end
    return r
end -- factory()

local function eval( source, frame )
    -- Evaluate expression
    -- Precondition:
    --     source  -- string; mathematical expression
    --     frame   -- object
    return frame:callParserFunction( "#expr", source )
end -- eval()


local function expr( source, frame, show )
    -- Safe evaluation of presumable expression
    -- Precondition:
    --     source  -- string; mathematical expression
    --     frame   -- object
    --     show    -- string; details about source
    -- Postcondition:
    --     throws error, if expression failed
    --     returns number with resulting figure
    -- Uses:
    --     factory()
    local lucky, r = pcall( eval, source, frame )
    local n = tonumber( r, 10 )
    if not lucky or n == nil then
        r = r .. " " .. factory( "ErrorExpr" )
            .. " ''" .. show .. "'' (" .. source .. ")"
        error( r, 0 )
    else
        r = n
    end
    return r
end -- expr()


local function base62( value )
    -- Convert number from and to base62 encoding
    -- Precondition:
    --     value  -- number or string to be converted
    --               number: to base62
    --               string: base62 to number
    --     Lua limitation at 10^53; larger numbers are less precise
    -- Postcondition:
    --     returns string, or number, or false
    local r = false
    local state = type( value )
    if state == "number" then
        local k = math.floor( value )
        if k == value  and  value > 0 then
            local m
            r = ""
            while k > 0 do
                m = k % 62
                k = ( k - m ) / 62
                if m >= 36 then
                    m = m + 61
                elseif m >= 11 then
                    m = m + 55
                else
                    m = m + 48
                end
                r = string.char( m ) .. r
            end
        elseif value == 0 then
            r = "0"
        end
    elseif state == "string" then
        if value:match( "^%w+$" ) then
            local n = #value
            local k = 1
            local c
            r = 0
            for i = n, 1, -1 do
                c = value:byte( i, i )
                if c >= 48  and  c <= 57 then
                    c = c - 48
                elseif c >= 65  and  c <= 90 then
                    c = c - 55
                elseif c >= 97  and  c <= 122 then
                    c = c - 61
                else    -- How comes?
                    r = nil
                    break    -- for i
                end
                r = r + c * k
                k = k * 62
            end -- for i
        end
    end
    return r
end -- base62()

function logicaland(args)
	local r = true;
	local k, v, s
	local b
	for k, v in pairs(args) do
		s = mw.text.trim(v)
		b = (s or '') ~= ''
		r = r and b
	end
    return r
end

function logicalor(args)
	local r = false;
	local k, v, s
	local b
	for k, v in pairs(args) do
		s = mw.text.trim(v) or '';
		if s == '' then
			b = false;
		elseif s=='0' then
			b = false;
		elseif s=='false' then
			b = false;
		elseif s=='falsch' then
			b = false;
		else
			b = true;
		end
		if b then
			r = true;
		end
	end
    return r
end

local function minmax( params, frame, low, lazy  )
    -- Find extremum of unnamed params values
    -- Precondition:
    --     params  -- table; like args
    --                       .minus
    --                       .zeroBlank
    --     frame   -- object
    --     low     -- true: minimum;  false: maximum
    --     lazy    -- true: try numeric result;  false: return string
    -- Postcondition:
    --     throws error, if expression failed
    --     returns number, or
    --             string if formatting required, or
    --             false if no data provided
    -- Uses:
    --     expr()
    local k, v, n, scope
    local light  = ( params.minus ~= "-" )
    local luxury = ( params.minus and light )
    local c      = mw.ustring.char( 8722 )    -- minus
    local scan   = "^%s*%-?[0-9]*%.?[0-9]*%s*$"
    local r      = false
    for k, v in pairs( params ) do
        if type( k ) == "number" then
            scope  = type( v )
            if scope == "string" then
                if v:match( "^%s*$" ) then
                    n = false
                else
                    if mw.ustring.match( v, c ) then
                        luxury = light
                        v      = mw.ustring.gsub( v, c, "-" )
                    end
                    if not mw.ustring.match( v, scan ) then
                        if low then
                            scope = "min()#"
                        else
                            scope = "max()#"
                        end
                        scope = scope .. tostring( k )
                        v     = expr( v, frame, scope )
                    end
                    n = tonumber( v )
                end
            elseif scope == "number" then
                n = v
            else
                n = false
            end
            if n then
                if r then
                    if low then
                        if n < r then
                            r = n
                        end
                    else
                        if n > r then
                            r = n
                        end
                    end
                else
                    r = n
                end
            end
        end
    end -- for k, v
    if r then
        if luxury and r < 0 then
            r = c .. tostring( -1 * r )
        elseif not lazy then
            if r == 0 then
                if params.zeroBlank then
                    r = ""
                else
                    r = "0"
                end
            else
                r = tostring( r )
            end
        end
    end
    return r
end -- minmax()



-- Export
local p = {}

function p.base62( frame )
    local r
    local s = frame.args[ 1 ]
    if s then
        local s2 = frame.args[ 2 ]
        if s2 then
            s2 = mw.text.trim( s2 )
        end
        if s2 == "D2B" then
            s = tonumber( s )
        else
            s = mw.text.trim( s )
            s2 = false
        end
        r = base62( s )
        if r  and  not s2 then
            r =  string.format( "%17d", r )
        end
    end
    return r or ""
end

function p.max( frame )
    local lucky, r = pcall( minmax, frame.args, frame, false, false )
    return r or ""
end

function p.min( frame )
    local lucky, r = pcall( minmax, frame.args, frame, true, false )
    return r or ""
end

function p.TemplateMax( frame )
    return p.max( frame:getParent() )
end

function p.TemplateMin( frame )
    return p.min( frame:getParent() )
end

function p.booland(frame)
	local fr=frame:getParent()
	return logicaland(fr.args)
end

function p.boolor(frame)
	local fr=frame:getParent()
	return logicalor(fr.args)
end

function p.Expr( f, a )
    local r = false
    if f == "min"  or  f == "max" then
        local frame = mw.getCurrentFrame()
        local low = ( f == "min" )
        local lucky
        lucky, r = pcall( minmax, a, frame, low, true )
    elseif f == "base62" then
        r = base62( a )
    end
    return r
end -- .Expr()

return p -- Expr