#include <cstring>
#include <memory>
#include <string>
#include <cstdint>

class BufferedReader {
 public:
  explicit BufferedReader(PackageStream *stream);
  int32_t Read(char *output_buffer, int32_t buffer_len);

 private:
  PackageStream *bufR = nullptr;
  std::string _ostBuf{};
};

BufferedReader::BufferedReader(PackageStream *stream)
    : bufR(stream) {}

int32_t BufferedReader::Read(char *output_buffer, int32_t buffer_len) {
  char *buf = new char[2000];
  int32_t readed{};
  //  ves last buf prochitan
  if (_ostBuf.empty()) {
    auto ostInBuf = buffer_len - readed;
    while (readed < buffer_len) {
      auto len = bufR->ReadPackage(buf);
      if (len == 0) {
        delete[] buf;
        return readed;
      }
      if (
          bufR->PackageLen() > ostInBuf) { //  ne pomestitsa berem chast'
        if (len <= ostInBuf) { //  pomestitsa
          if (!buf)
            return 0;
          std::memcpy(output_buffer + readed, buf, len); //  look suda
          readed += len;
        } else { //  pomest ne vse ostatok v _ostatok
          if (!buf)
            return 0; //  prochitali bolche 4em ost mesta
          std::memcpy(output_buffer + readed, buf, ostInBuf);
          for (size_t i = 0; i < static_cast<size_t> (len - ostInBuf); ++i)
            _ostBuf.push_back(buf[i + readed + ostInBuf]); //  ? podumai 3dec oshibka
          readed += ostInBuf;
        }
      } else {
        std::memcpy(output_buffer + readed, buf, len);
        readed += len;
        ostInBuf -= readed;
      }
    }
    delete[] buf;
    return readed;
  } else { // осталось с прошлого большого чтения
    if (auto len = _ostBuf.size(); len <= static_cast<size_t> (buffer_len)) {
      std::memcpy(output_buffer, _ostBuf.c_str(), len);
      _ostBuf = std::string();
      readed += len;
      if (len == buffer_len) {
        delete[] buf;
        return len;
      } else {
        delete[] buf;
        return readed + Read(output_buffer + len, buffer_len - len);
      }
    } else { //fuck
      std::memcpy(output_buffer, _ostBuf.c_str(), buffer_len);
      std::string new_ost{};
      readed += buffer_len;
      for (size_t i = 0; i < _ostBuf.size() - buffer_len; ++i)
        new_ost.push_back(_ostBuf[buffer_len + i]);
      _ostBuf = new_ost;
      delete[] buf;
      return buffer_len;
    }
  }
}