#include "initializer_io.h"

using namespace NSrvKernel;

namespace NSrvKernel {
    std::pair<TMasterStreamPtr, TTwoNonblockingPipesPtr> SetupMasterPipes() {
        TTwoNonblockingPipesPtr initializerStream = MakeHolder<TTwoNonblockingPipes>();
        TMasterStreamPtr masterStream = MakeHolder<TMasterStream>();

        TPipe::Pipe(initializerStream->Input(), masterStream->Output_);
        TPipe::Pipe(masterStream->Input_, initializerStream->Output());

        for (const auto& handle: {masterStream->GetInputHandle(), masterStream->GetOutputHandle()}) {
            SetNonBlock(handle);
        }
        initializerStream->Init();
        return std::make_pair(std::move(masterStream), std::move(initializerStream));
    }

    EIoResult TMasterStream::Write() {
        const size_t remaining = Filled_ - OutputBuffer_.size();
        const void* data = OutputBuffer_.Pos();
        size_t piece = 0;
        try {
            piece = Output_.Write(data, remaining);
        } catch (const TSystemError& e) {
            if (e.Status() == EPIPE) {
                return EIoResult::EOF_OCCURED;
            }
        }
        if (piece == 0) {
            return EIoResult::EOF_OCCURED;
        }
        OutputBuffer_.Advance(piece);

        if (Filled_ != OutputBuffer_.size()) {
            return EIoResult::PARTIALLY_COMPLETED;
        }
        Filled_ = 0;
        OutputBuffer_.Clear();
        return EIoResult::COMPLETED;
    }

    void TMasterStream::EnqueueWrite(const TInitializerMessage& message) {
        const size_t messageSize = message.ByteSizeLong();
        const size_t maxLen = Filled_ + messageSize + sizeof(size_t);
        if (OutputBuffer_.Capacity() < maxLen) {
            OutputBuffer_.Reserve(Max(2 * OutputBuffer_.Capacity(), maxLen));
        }
        memcpy(OutputBuffer_.Data() + Filled_, &messageSize, sizeof(size_t));
        Filled_ += sizeof(size_t);
        Y_VERIFY(message.SerializeToArray(OutputBuffer_.Data() + Filled_, messageSize), "Failed to serialize initializer message");
        Filled_ += messageSize;
    }

    EIoResult TMasterStream::Read(TInitializerMessage* message) {
        const size_t remaining = Remaining_ == -1 ? (sizeof(size_t) - InputBuffer_.size()) : Remaining_;
        if (InputBuffer_.Capacity() < remaining) {
            InputBuffer_.Reserve(Max(2 * InputBuffer_.Capacity(), remaining));
        }
        const size_t piece = Input_.Read(InputBuffer_.Pos(), remaining);
        if (piece == 0) {
            return EIoResult::EOF_OCCURED;
        }
        InputBuffer_.Advance(piece);

        if (Remaining_ == -1) {
            if (InputBuffer_.size() == sizeof(size_t)) {
                size_t dataLength;
                memcpy(&dataLength, InputBuffer_.Data(), sizeof(size_t));
                Remaining_ = dataLength;
                InputBuffer_.Clear();
            }
            return EIoResult::PARTIALLY_COMPLETED;
        } else {
            if (remaining != piece) {
                Remaining_ -= piece;
                return EIoResult::PARTIALLY_COMPLETED;
            }
            Y_VERIFY(message->ParseFromArray(InputBuffer_.data(), InputBuffer_.size()), "Cannot parse message from master");
            Remaining_ = -1;
            InputBuffer_.Clear();
            return EIoResult::COMPLETED;
        }
    }
}
