#include "socket_adapter_simple.h"
#include <util/generic/yexception.h>
#include <util/generic/vector.h>

#ifdef _win_
#include <util/system/error.h>
#else
#include <errno.h>
#endif

size_t BlockingRead(void* buffer, size_t length, IInputStream& input) {
    Y_VERIFY(length, "Blocking read called with zero length");
    size_t totalRead = 0;
    size_t read = 0;
    do {
        try {
            read = input.Read((char*)buffer + totalRead, length - totalRead);
        } catch(TSystemError& e) {
            ythrow e;
        }

        totalRead += read;
    } while (read && totalRead && (length > totalRead));
    return totalRead;
}

bool TSocketAdapterSimple::ReadMessage(::google::protobuf::Message& message) const {
    ui32 size = 0;
    size_t read = BlockingRead(&size, sizeof(size), Input);
    if (read) {
        if (read == sizeof(size)) {
            if (size) {
                TVector<char> buffer(size);
                read = BlockingRead(&buffer[0], size, Input);
                if (read == size) {
                    if (message.ParseFromArray(&buffer[0], size)) {
                        return true;
                    } else {
                        ythrow yexception() << "Failed to parse message";
                    }
                } else {
                    ythrow yexception() << "Failed to read message, expected: "
                        << size << " bytes, got: " << read;
                }
            } else {
                return false;
            }
        } else {
            ythrow yexception() << "Failed to read message size, expected: "
                << sizeof(size) << " bytes, got: " << read;
        }
    } else {
        return false;
    }
}

void TSocketAdapterSimple::WriteMessage(const ::google::protobuf::Message& message, const TString& /*request*/) const {
    ui32 size = message.ByteSize();
    Output.Write(&size, sizeof(size));
    if (!message.SerializeToArcadiaStream(&Output)) {
        ythrow yexception() << "Failed to serialize message" << Endl;
    }
    Output.Flush();
}

