// Copyright(c) 2019 ZVYAGIN.Alexander@gmail.com
// Distributed under the MIT License (http://opensource.org/licenses/MIT)

#pragma once

#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/synchronous_factory.h"

#include <array>
#include <string>
#include <systemd/sd-journal.h>

namespace spdlog {
namespace sinks {

inline int syslog_level(level::level_enum l)
{
    switch (l)
    {
    case level::off:
    case level::trace:
    case level::debug:
        return LOG_DEBUG;
    case level::info:
        return LOG_INFO;
    case level::warn:
        return LOG_WARNING;
    case level::err:
        return LOG_ERR;
    case level::critical:
        return LOG_CRIT;
    default:
        throw std::invalid_argument("systemd_sink.h syslog_level()");
    }
}

/**
 * Sink that write to systemd using the `sd_journal_print()` library call.
 *
 * Locking is not needed, as `sd_journal_print()` itself is thread-safe.
 */
template<typename Mutex>
class systemd_sink : public base_sink<Mutex>
{
public:
    //
    explicit systemd_sink(void) {}

    ~systemd_sink() override {}

    systemd_sink(const systemd_sink &) = delete;
    systemd_sink &operator=(const systemd_sink &) = delete;

protected:
    void sink_it_(const details::log_msg &msg) override
    {
        if (sd_journal_print(syslog_level(msg.level), "%.*s", static_cast<int>(msg.payload.size()), msg.payload.data()))
            throw spdlog_ex("Failed writing to systemd");
    }

    void flush_() override {}
};

using systemd_sink_mt = systemd_sink<std::mutex>;
using systemd_sink_st = systemd_sink<details::null_mutex>;
} // namespace sinks

// Create and register a syslog logger
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> systemd_logger_mt(const std::string &logger_name)
{
    return Factory::template create<sinks::systemd_sink_mt>(logger_name);
}

template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> systemd_logger_st(const std::string &logger_name)
{
    return Factory::template create<sinks::systemd_sink_st>(logger_name);
}
} // namespace spdlog
