#pragma once

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

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

    int32_t Read(char *output_buffer, int32_t buffer_len) {

        int32_t buffer_size = static_cast<int>(strlen(buffer.get()));
        int32_t real_size = std::min<int32_t>(buffer_size, buffer_len);
        memcpy(output_buffer, buffer.get(), real_size);
        memcpy(buffer.get(), buffer.get() + real_size,
               buffer_size - real_size);
        AddZero(buffer.get(), buffer_size - real_size);
        unique_ptr<char[]> tmp_buf(new char[stream->PackageLen()]);

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

            real_size += add_size;
        }

        return real_size;
    }

 private:
    void AddZero(char* c, int32_t index) {
        c[index] = 0;
    }

    PackageStream *stream;
    unique_ptr<char[]> buffer;
};










//    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;
//    }