#include "log.h"

#include <library/cpp/logger/stream.h>

#include <util/datetime/base.h>
#include <util/datetime/base.h>
#include <util/generic/vector.h>
#include <util/stream/buffer.h>
#include <util/stream/output.h>
#include <util/system/thread.h>


using namespace NSrvKernel;

namespace {
    class TLimitedAutoResetableBufferOutput: public IOutputStream {
    public:
        explicit TLimitedAutoResetableBufferOutput(size_t limit = 2 * 1024 * 1024)
            : Data_(limit, '\0')
            , Limit_(limit)
        {
            Y_VERIFY(Limit_ > 0, "limit must be a positive number");
        }

        const char* Data() const noexcept {
            return Data_.data();
        }

        size_t Size() const noexcept {
            return Current_;
        }

    private:
        void DoWrite(const void* buf, size_t len) override {
            if (Y_UNLIKELY(len > Limit_)) {
                return;
            }

            if (Limit_ - Current_ < len) {
                Current_ = 0;
            }
            memcpy(Data_.data() + Current_, buf, len);
            Current_ += len;
        }

    private:
        TVector<char> Data_;
        const size_t Limit_;
        size_t Current_ = 0;
    };

    struct TBufEnd: public TLimitedAutoResetableBufferOutput, public TStreamLogBackend {
        TBufEnd()
            : TStreamLogBackend(this)
        {}
    };

    //MINOTAUR-632
    class TNoExceptionLogBackend: public TLogBackend {
    public:
        explicit TNoExceptionLogBackend(THolder<TLogBackend>&& slave)
            : Slave_(std::move(slave))
        {
        }

        void WriteData(const TLogRecord& rec) override {
            try {
                Slave_->WriteData(rec);
            } catch (...) {
            }
        }

        void ReopenLog() override {
            Slave_->ReopenLog();
        }

        void ReopenLogNoFlush() override {
            Slave_->ReopenLogNoFlush();
        }

        ELogPriority FiltrationLevel() const override {
            return Slave_->FiltrationLevel();
        }
    private:
        THolder<TLogBackend> Slave_;
    };
}

TSystemLog::TSystemLog()
    : TLog(MakeHolder<TBufEnd>())
{}

void TSystemLog::Open(const TString& path) {
    const THolder<TLogBackend> backend(this->ReleaseBackend());

    *static_cast<TLog*>(this) = TLog(path);

    THolder<TLogBackend> wrapper = MakeHolder<TNoExceptionLogBackend>(ReleaseBackend());
    ResetBackend(std::move(wrapper));

    TBufEnd* const bufEnd = dynamic_cast<TBufEnd*>(backend.Get());
    if (bufEnd) {
        this->Write(bufEnd->Data(), bufEnd->Size());
    }
}

void TSystemLog::LogPrefix(TLogElement& s) {
    s << Tid_ << '\t' << Now().ToStringLocal() << '\t';
}
