#pragma once

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

class BufferedReader {
public:
    explicit BufferedReader(PackageStream *stream) : stream(stream) {
        buffer = new char[stream->PackageLen() + 1];
        memset(buffer, 0, stream->PackageLen() + 1);
    }
    int32_t Read(char *output_buffer, int32_t buffer_len) {
        int32_t buffer_size = BufferSize();
        int32_t real_size = std::min<int32_t>(buffer_size, buffer_len);
        memcpy(output_buffer, buffer, real_size);
        memcpy(buffer, buffer + real_size,
               buffer_size - real_size);
        AddZero(buffer + buffer_size - real_size);
        char* tmp_buf = new char[stream->PackageLen() + 1];

        for (int32_t last_result = INT32_MAX;
             real_size < buffer_len && last_result != 0;) {
            last_result = this->stream->ReadPackage(tmp_buf);
            int32_t add_size;
            if (real_size + last_result > buffer_len) {
                add_size = buffer_len - real_size;
                memcpy(output_buffer + real_size, tmp_buf, add_size);
                memcpy(buffer, tmp_buf + add_size,
                       real_size + last_result - buffer_len);
                AddZero(buffer + real_size + last_result - buffer_len);
            } else {
                add_size = last_result;
                memcpy(output_buffer + real_size, tmp_buf, add_size);
            }

            real_size += add_size;
        }
        delete[] tmp_buf;

        return real_size;
    }
    ~BufferedReader() {
        delete[] buffer;
    }

private:
    void AddZero(char* c) const {
        *c = 0;
    }
    int32_t BufferSize() const {
        int i = 0;
        while (buffer[i] != 0) {
            i++;
        }
        return i;
    }

    PackageStream *stream;
    char* buffer;
};