#pragma once

#include <string>
#include <algorithm>
#include <cstring>

class BufferedReader {
 public:
    explicit BufferedReader(PackageStream* stream) : cur_stream(stream) {}
    int32_t Read(char* output_buffer, int32_t buffer_len) {
        char* cur_buffer = new char[cur_stream->PackageLen()];
        int32_t cur_size = 0, buf_count = 0, cur_len = buffer_len;
        if (read_flag) {
            while (buffer_len > cur_stream->PackageLen()) {
                buf_count = cur_stream->ReadPackage(cur_buffer);
                cur_size += buf_count;
                buffer = app(buffer, cur_buffer, buf_count);
                buffer_len -= cur_stream->PackageLen();
            }
            buf_count = cur_stream->ReadPackage(cur_buffer);
            if (buf_count == 0) {
                read_flag = false;
            }
            cur_size += buf_count;
            buffer = app(buffer, cur_buffer, buf_count);
        }
        if (cur_len < cur_size || cur_len < static_cast<int32_t>
            (buffer.size())) {
            cpy(output_buffer, buffer.c_str(), cur_len);
            buffer.erase(0, cur_len);
            delete[] cur_buffer;
            return cur_len;
        } else {
            cur_size = buffer.size();
            cpy(output_buffer, buffer.c_str(), buffer.size());
            buffer.clear();
            delete[] cur_buffer;
            return cur_size;
        }
    }

 private:
     PackageStream* cur_stream;
     std::string buffer = "";
     bool read_flag = true;
     void cpy(char* old_buf, const char* new_buf, int32_t size) {
         for (int32_t i = 0; i < size; ++i) {
             old_buf[i] = new_buf[i];
         }
     }
     void app(std::string buf, char* input_str, int32_t size) const {
         for (int32_t i = 0; i < size; ++i) {
             buf.push_back(input_str[i]);
         }
     }
};