#pragma once
#include <luna/Attributes.h>
#include <luna/Result.h>
#include <stdarg.h>

enum class LogLevel
{
    Debug,
    Info,
    Warn,
    Error,
};

void vlog(LogLevel level, const char* format, va_list ap);
void log(LogLevel level, const char* format, ...) _format(2, 3);

void setup_log(bool enable_debug, bool enable_serial, bool enable_text_console);
bool log_debug_enabled();
bool log_serial_enabled();
bool log_text_console_enabled();

void set_text_console_initialized();

#define kdbgln(...) log(LogLevel::Debug, __VA_ARGS__)
#define kinfoln(...) log(LogLevel::Info, __VA_ARGS__)
#define kwarnln(...) log(LogLevel::Warn, __VA_ARGS__)
#define kerrorln(...) log(LogLevel::Error, __VA_ARGS__)

[[noreturn]] extern void __critical_error_handler(SourceLocation location, const char* expr, const char* failmsg,
                                                  const char* errmsg);

// Mark an expression that returns a Result<T> as critical. If the operation is successful, the Result will be
// unwrapped. If the operation throws an error, however, the kernel will panic, showing the expression, the file in
// which mark_critical() was called, failmsg and the error code.
#define mark_critical(expr, failmsg)                                                                                   \
    ({                                                                                                                 \
        auto _expr_rc = (expr);                                                                                        \
        if (!_expr_rc.has_value())                                                                                     \
            __critical_error_handler(SourceLocation::current(), #expr, failmsg, _expr_rc.error_string());              \
        _expr_rc.release_value();                                                                                      \
    })