#pragma once

#include <iostream>
#include <utility>
#include <algorithm>
#include <string>
#include <cmath>

class BufferedReader {
 public:
    explicit BufferedReader(PackageStream *stream) : stream(stream),
                             buffer(new char[this->stream->PackageLen() + 1]) {
    }

    int32_t Read(char *output_buffer, int32_t buffer_len) {
        int32_t real_size = std::min<int32_t>
                (buffer_len, static_cast<int32_t>(strlen(this->buffer)));
        int32_t last_return = 1;
        memcpy(output_buffer, this->buffer, real_size);
        char* tmp_buf = new char[this->stream->PackageLen() + 1];
        memcpy(tmp_buf, this->buffer, strlen(this->buffer));
        memcpy(this->buffer, tmp_buf + real_size, strlen(tmp_buf));
        memset(tmp_buf, 0, strlen(tmp_buf));

        while (real_size < buffer_len && last_return != 0) {
            last_return = stream->ReadPackage(tmp_buf);
            int add_size = real_size + last_return > buffer_len ?
                    buffer_len - real_size : last_return;
            memcpy(output_buffer + real_size, tmp_buf, add_size);
            real_size += add_size;
            memset(this->buffer, 0, strlen(this->buffer));
            memcpy(this->buffer, tmp_buf + add_size, strlen(tmp_buf));
            memset(tmp_buf, 0, strlen(tmp_buf));
        }

        delete[] tmp_buf;
        return real_size;
    }

 private:
    PackageStream *stream;
    char* buffer;
};