######################################################################
# Slightly advanced logging utility
# -----
# Licensed under Apache License 2.0
module Logging
import ExtraFun: curry

struct LogMessage
    message::String
    time::Float64
end
LogMessage(msg::AbstractString) = LogMessage(string(msg), time())

export LogCategory
struct LogCategory
    name::String
    default_duplex::Union{IO, Nothing}
    messages::Vector{LogMessage}
end
LogCategory(name::AbstractString = "uncategorized", default_duplex::Union{IO, Nothing} = stdout) = LogCategory(string(name), default_duplex, LogMessage[])
(cat::LogCategory)(args...; kwargs...) = log(cat, args...; kwargs...)

const Uncategorized    = LogCategory()
const DebugCategory    = LogCategory("Debug")
const InfoCategory     = LogCategory("Info")
const WarnCategory     = LogCategory("Warning")
const ErrorCategory    = LogCategory("Error", stderr)
const CriticalCategory = LogCategory("Critical", stderr)

export logdebug, loginfo, logwarn, logerror, logcritical
function log(cat::LogCategory, xs...; duplex::Union{IO, Nothing} = cat.default_duplex, joiner = ' ')
    msg = join(string.(xs), joiner)
    push!(cat.messages, LogMessage(msg))
    
    if duplex !== nothing
        write(duplex, msg * '\n')
    end
    nothing
end
log(args...; kwargs...) = log(Uncategorized, args...; kwargs...)

const logdebug    = curry(log, DebugCategory)
const loginfo     = curry(log, InfoCategory)
const logwarn     = curry(log, WarnCategory)
const logerror    = curry(log, ErrorCategory)
const logcritical = curry(log, CriticalCategory)

function Base.empty!(log::LogCategory; older_than::Real = Inf)
    filter!(msg->msg.time ≥ older_than, log.messages)
    return log
end

end # module Logging
