/*
    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
*/
#pragma once
#include "interfaces/shard.h"
#include "vm/db/StaticBagOfCellsDb.h"
#include "block/mc-config.h"
#include "config.hpp"
namespace ton {
namespace validator {
using td::Ref;
class ShardStateQ : virtual public ShardState {
 protected:
  BlockIdExt blkid;
 private:
  RootHash rhash;
  td::BufferSlice data;
  std::vector> bocs_;
  Ref root;
  LogicalTime lt{0};
  UnixTime utime{0};
  bool before_split_{false};
  bool fake_split_{false};
  bool fake_merge_{false};
 protected:
  friend class Ref;
  ShardStateQ(const ShardStateQ& other);
  ShardStateQ(ShardStateQ&& other) = default;
 public:
  td::Status init();
  ShardStateQ(const BlockIdExt& _id, td::BufferSlice _data);
  ShardStateQ(const BlockIdExt& _id, Ref _root, td::BufferSlice _data = {});
  virtual ~ShardStateQ() = default;
  static td::Result[> fetch(const BlockIdExt& _id, td::BufferSlice _data, Ref _root = {});
  bool disable_boc() const override {
    return false;
  }
  ShardIdFull get_shard() const override {
    return ShardIdFull(blkid);
  }
  BlockSeqno get_seqno() const override {
    return blkid.id.seqno;
  }
  BlockIdExt get_block_id() const override {
    return blkid;
  }
  RootHash root_hash() const override {
    return rhash;
  }
  Ref root_cell() const override {
    return root;
  }
  bool before_split() const override {
    return before_split_;
  }
  UnixTime get_unix_time() const override {
    return utime;
  }
  LogicalTime get_logical_time() const override {
    return lt;
  }
  td::Status validate_deep() const override;
  ShardStateQ* make_copy() const override;
  td::Result][> message_queue() const override;
  td::Status apply_block(BlockIdExt id, Ref block) override;
  td::Result][> merge_with(const ShardState& with) const override;
  td::Result, Ref>> split() const override;
  td::Result serialize() const override;
  td::Status serialize_to_file(td::FileFd& fd) const override;
};
#if TD_MSVC
#pragma warning(push)
#pragma warning(disable : 4250)  // MasterchainState is an interface, so there is no problem here
#endif
class MasterchainStateQ : public MasterchainState, public ShardStateQ {
 public:
  MasterchainStateQ(const BlockIdExt& _id, td::BufferSlice _data);
  MasterchainStateQ(const BlockIdExt& _id, Ref _root, td::BufferSlice _data = {});
  virtual ~MasterchainStateQ() = default;
  td::Status apply_block(BlockIdExt id, Ref block) override;
  Ref get_validator_set(ShardIdFull shard) const override;
  Ref get_next_validator_set(ShardIdFull shard) const override;
  Ref get_total_validator_set(int next) const override;  // next = -1 -> prev, next = 0 -> cur
  Ref get_validator_set(ShardIdFull shard, UnixTime ts, CatchainSeqno cc_seqno) const;
  bool rotated_all_shards() const override;
  std::vector][> get_shards() const override;
  td::Ref get_shard_from_config(ShardIdFull shard) const override;
  bool ancestor_is_valid(BlockIdExt id) const override {
    return check_old_mc_block_id(id);
  }
  bool workchain_is_active(WorkchainId workchain_id) const override {
    return has_workchain(workchain_id);
  }
  bool has_workchain(WorkchainId workchain) const {
    return config_ && config_->has_workchain(workchain);
  }
  td::uint32 min_split_depth(WorkchainId workchain_id) const override;
  td::uint32 soft_min_split_depth(WorkchainId workchain_id) const override;
  BlockSeqno min_ref_masterchain_seqno() const override;
  td::Status prepare() override;
  ZeroStateIdExt get_zerostate_id() const {
    return zerostate_id_;
  }
  ValidatorSessionConfig get_consensus_config() const override {
    return config_->get_consensus_config();
  }
  BlockIdExt last_key_block_id() const override;
  BlockIdExt next_key_block_id(BlockSeqno seqno) const override;
  BlockIdExt prev_key_block_id(BlockSeqno seqno) const override;
  MasterchainStateQ* make_copy() const override;
  static td::Result][> fetch(const BlockIdExt& _id, td::BufferSlice _data,
                                                  Ref _root = {});
  bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
                           ton::LogicalTime* end_lt = nullptr) const override;
  bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const override;
  std::shared_ptr get_config() const {
    return config_;
  }
  td::Result> get_config_holder() const override {
    if (!config_) {
      return td::Status::Error(ErrorCode::notready, "config not found");
    } else {
      return td::make_ref(config_);
    }
  }
 private:
  ZeroStateIdExt zerostate_id_;
  std::shared_ptr config_;
  std::shared_ptr cur_validators_, next_validators_;
  MasterchainStateQ(const MasterchainStateQ& other) = default;
  td::Status mc_init();
  td::Status mc_reinit();
  Ref compute_validator_set(ShardIdFull shard, const block::ValidatorSet& vset, UnixTime time,
                                          CatchainSeqno cc_seqno) const;
};
#if TD_MSVC
#pragma warning(pop)
#endif
}  // namespace validator
}  // namespace ton
]