#pragma once

#include "deimos/core/base.h"
#include "deimos/core/id_name.h"
#include "deimos/core/format.h"

namespace deimos
{

enum class LogSeverity : uint8_t
{
    kInfo,
    kDebug,
    kError,
};

class ILogger
{
public:
    ILogger() = default;
    deimos_NO_COPY_MOVE(ILogger);
    virtual ~ILogger() = default;

    virtual void Log(LogSeverity, const SourceLocation&, StringView) = 0;
};

// Just a helper to pass a SourceLocation without having to write {} to logging functions.
struct LogSourceLocation
{
    gsl::czstring       fmt;
    SourceLocation      source_location;

    // NOLINTNEXTLINE(*-explicit-conversions)
    LogSourceLocation(gsl::czstring fmt_, const SourceLocation& source_location_ = {}) :
        fmt{fmt_}, source_location{source_location_}
    {}
};

class LogApi
{
public:
    static constexpr IdName kApiName{"deimos::LogApi"};

    LogApi() = default;
    deimos_NO_COPY_MOVE(LogApi);
    virtual ~LogApi() = default;

    virtual void LogVa(LogSeverity, const SourceLocation&, gsl::czstring msg, Span<const FormatArg>) = 0;

    template<Formattable... Args>
    void LogInfo(const LogSourceLocation& fmt_source_location, Args&&... args)
    {
        LogVa(LogSeverity::kInfo, fmt_source_location.source_location, fmt_source_location.fmt, { FormatArg(std::forward<Args>(args))... });
    }

    template<Formattable... Args>
    void LogDebug(const LogSourceLocation& fmt_source_location, Args&&... args)
    {
        LogVa(LogSeverity::kDebug, fmt_source_location.source_location, fmt_source_location.fmt, { FormatArg(std::forward<Args>(args))... });
    }

    template<Formattable... Args>
    void LogError(const LogSourceLocation& fmt_source_location, Args&&... args)
    {
        LogVa(LogSeverity::kError, fmt_source_location.source_location, fmt_source_location.fmt, { FormatArg(std::forward<Args>(args))... });
    }
};

} // namespace deimos