/*
    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/validator-manager.h"
#include "validator-session/validator-session.h"
#include "rldp/rldp.h"
#include 
namespace ton {
namespace validator {
class ValidatorManager;
class ValidatorGroup : public td::actor::Actor {
 public:
  void generate_block_candidate(td::uint32 round_id, td::Promise promise);
  void validate_block_candidate(td::uint32 round_id, BlockCandidate block, td::Promise promise);
  void accept_block_candidate(td::uint32 round_id, PublicKeyHash src, td::BufferSlice block, RootHash root_hash,
                              FileHash file_hash, std::vector signatures,
                              std::vector approve_signatures,
                              validatorsession::ValidatorSessionStats stats, td::Promise promise);
  void skip_round(td::uint32 round);
  void accept_block_query(BlockIdExt block_id, td::Ref block, std::vector prev,
                          td::Ref sigs, td::Ref approve_sigs,
                          bool send_broadcast, td::Promise promise, bool is_retry = false);
  void get_approved_candidate(PublicKey source, RootHash root_hash, FileHash file_hash,
                              FileHash collated_data_file_hash, td::Promise promise);
  BlockId create_next_block_id_simple() const;
  BlockIdExt create_next_block_id(RootHash root_hash, FileHash file_hash) const;
  void start(std::vector prev, BlockIdExt min_masterchain_block_id);
  void create_session();
  void destroy();
  void start_up() override {
    if (init_) {
      init_ = false;
      create_session();
    }
  }
  void get_session_info(td::Promise> promise);
  ValidatorGroup(ShardIdFull shard, PublicKeyHash local_id, ValidatorSessionId session_id,
                 td::Ref validator_set, block::CollatorConfig collator_config,
                 validatorsession::ValidatorSessionOptions config,
                 td::actor::ActorId keyring, td::actor::ActorId adnl,
                 td::actor::ActorId rldp, td::actor::ActorId overlays,
                 std::string db_root, td::actor::ActorId validator_manager, bool create_session,
                 bool allow_unsafe_self_blocks_resync,
                 ValidatorManagerOptions::ValidatorMode mode = ValidatorManagerOptions::validator_normal)
      : shard_(shard)
      , local_id_(std::move(local_id))
      , session_id_(session_id)
      , validator_set_(std::move(validator_set))
      , collator_config_(std::move(collator_config))
      , config_(std::move(config))
      , keyring_(keyring)
      , adnl_(adnl)
      , rldp_(rldp)
      , overlays_(overlays)
      , db_root_(std::move(db_root))
      , manager_(validator_manager)
      , init_(create_session)
      , allow_unsafe_self_blocks_resync_(allow_unsafe_self_blocks_resync)
      , mode_(mode) {
  }
 private:
  std::unique_ptr make_validator_session_callback();
  void send_collate_query(td::uint32 round_id, td::Timestamp timeout, td::Promise promise,
                          unsigned max_retries = 4);
  void receive_collate_query_response(td::uint32 round_id, td::BufferSlice data, td::Promise promise);
  struct PostponedAccept {
    RootHash root_hash;
    FileHash file_hash;
    td::BufferSlice block;
    td::Ref sigs;
    td::Ref approve_sigs;
    validatorsession::ValidatorSessionStats stats;
    td::Promise promise;
  };
  std::list postponed_accept_;
  ShardIdFull shard_;
  PublicKeyHash local_id_;
  PublicKey local_id_full_;
  ValidatorSessionId session_id_;
  std::vector prev_block_ids_;
  BlockIdExt min_masterchain_block_id_;
  td::Ref validator_set_;
  block::CollatorConfig collator_config_;
  validatorsession::ValidatorSessionOptions config_;
  td::actor::ActorId keyring_;
  td::actor::ActorId adnl_;
  td::actor::ActorId rldp_;
  td::actor::ActorId overlays_;
  std::string db_root_;
  td::actor::ActorId manager_;
  td::actor::ActorOwn session_;
  adnl::AdnlNodeIdShort local_adnl_id_;
  bool init_ = false;
  bool started_ = false;
  bool allow_unsafe_self_blocks_resync_;
  ValidatorManagerOptions::ValidatorMode mode_;
  td::uint32 last_known_round_id_ = 0;
  struct CachedCollatedBlock {
    td::optional result;
    std::vector> promises;
  };
  std::shared_ptr cached_collated_block_;
  void generated_block_candidate(std::shared_ptr cache, td::Result R);
  typedef std::tuple CacheKey;
  std::map approved_candidates_cache_;
  td::uint32 approved_candidates_cache_round_ = 0;
  void update_approve_cache(td::uint32 round_id, CacheKey key, UnixTime value);
  static CacheKey block_to_cache_key(const BlockCandidate& block) {
    return std::make_tuple(block.pubkey.as_bits256(), block.id, sha256_bits256(block.data), block.collated_file_hash);
  }
};
}  // namespace validator
}  // namespace ton