/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2020 Telegram Systems LLP
*/
#include "BinlogReaderHelper.h"
#include "BinlogReaderInterface.h"
#include "td/utils/misc.h"
namespace td {
td::Status BinlogReaderHelper::parse(BinlogReaderInterface& reader, td::Slice data) {
  SCOPE_EXIT {
    reader.flush();
  };
  while (true) {
    if (expected_prefix_size_ > 0 && expected_prefix_size_ == prefix_size_) {
      TRY_RESULT(size, reader.parse(MutableSlice(buf_.data(), prefix_size_)));
      if (size < 0) {
        if (expected_prefix_size_ > td::narrow_cast(-size)) {
          return td::Status::Error("BinlogReader decreased logevent size estimation (1)");
        }
        expected_prefix_size_ = static_cast(-size);
      } else {
        if (expected_prefix_size_ != td::narrow_cast(size)) {
          return td::Status::Error("BinlogReader changed logevent");
        }
        prefix_size_ = 0;
        expected_prefix_size_ = 0;
      }
    }
    if (data.empty()) {
      break;
    }
    if (expected_prefix_size_ > 0) {
      CHECK(expected_prefix_size_ < buf_.size());
      CHECK(prefix_size_ < expected_prefix_size_);
      auto got = data.copy().truncate(expected_prefix_size_ - prefix_size_);
      reader.flush();
      auto dest = td::MutableSlice(buf_.data(), buf_.size()).substr(prefix_size_);
      if (dest.size() < got.size()) {
        return td::Status::Error("Too big logevent");
      }
      dest.copy_from(got);
      prefix_size_ += got.size();
      data = data.substr(got.size());
      continue;
    }
    CHECK(!data.empty());
    TRY_RESULT(size, reader.parse(data));
    if (size < 0) {
      expected_prefix_size_ = td::narrow_cast(-size);
      prefix_size_ = data.size();
      if (expected_prefix_size_ < prefix_size_) {
        return td::Status::Error("BinlogReader waits for less data than it already has");
      }
      if (expected_prefix_size_ > buf_.size()) {
        return td::Status::Error("BinlogReader waits for too big logevent");
      }
      reader.flush();
      td::MutableSlice(buf_.data(), prefix_size_).copy_from(data);
      break;
    }
    if (size == 0) {
      return td::Status::Error("BinlogReader parseed nothing and asked for nothing");
    }
    if (td::narrow_cast(size) > data.size()) {
      return td::Status::Error("BinlogReader parseed more than was given");
    }
    data = data.substr(static_cast(size));
  }
  return td::Status::OK();
}
size_t BinlogReaderHelper::unparsed_size() const {
  return prefix_size_;
}
}  // namespace td