#include "xlog.h"
#include <xs/function.h>

using namespace xs;
using namespace panda;
using namespace panda::log;

struct PerlSubLogger : ILogger {
    using fn_t = function<void(const string&, Level)>;
    fn_t fn;

    PerlSubLogger (const Sub& sub) : fn(xs::in<fn_t>(sub)) {}

    void log (const string& msg, const Info& info) override {
        if (!is_perl_thread()) throw std::logic_error("can't call pure-perl logging callback: log() called from perl-foreign thread");
        fn(msg, info.level);
    }
};

struct PerlSubFormatter : IFormatter {
    using fn_t = function<string(const std::string&, Level, const string&, string_view, uint32_t, string_view)>;
    fn_t fn;

    PerlSubFormatter (const Sub& sub) : fn(xs::in<fn_t>(sub)) {}

    string format (std::string& msg, const Info& info) const override {
        if (!is_perl_thread()) throw std::logic_error("can't call pure-perl formatting callback: log() called from perl-foreign thread");
        return fn(msg, info.level, info.module->name(), info.file, info.line, info.func);
    }
};

static bool _init () {
    xs::at_perl_destroy([]{
        // remove all perl loggers and formatters from C++ modules as they are no longer available
        auto modules = get_modules();
        for (auto module : modules) {
            if (dyn_cast<PerlSubLogger*>(module->get_logger().get())) module->set_logger(nullptr);
            if (dyn_cast<PerlSubFormatter*>(module->get_formatter().get())) module->set_formatter(nullptr);
        }
    });
    return true;
}
static bool __init = _init();

namespace xs {

ILoggerSP Typemap<ILoggerSP>::in (Sv sv) {
    if (sv.is_sub_ref()) return new PerlSubLogger(sv);
    return xs::in<ILogger*>(sv);
}

IFormatterSP Typemap<IFormatterSP>::in (Sv sv) {
    if (sv.is_sub_ref()) return new PerlSubFormatter(sv);
    if (sv.is_string()) return new PatternFormatter(xs::in<string_view>(sv));
    return xs::in<IFormatter*>(sv);
}

}