/*
    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 "ton/ton-types.h"
#include "td/actor/actor.h"
#include "td/utils/Time.h"
#include "interfaces/block-handle.h"
#include "interfaces/validator-manager.h"
#include "interfaces/shard.h"
#include "block.hpp"
#include "shard.hpp"
#include "proof.hpp"
#include "block/block-auto.h"
namespace ton {
namespace validator {
using td::Ref;
class LiteQuery : public td::actor::Actor {
  td::BufferSlice query_;
  td::actor::ActorId manager_;
  td::Timestamp timeout_;
  td::Promise promise_;
  td::Promise,UnixTime,LogicalTime,std::unique_ptr>> acc_state_promise_;
  int pending_{0};
  int mode_{0};
  WorkchainId acc_workchain_;
  StdSmcAddress acc_addr_;
  LogicalTime trans_lt_;
  Bits256 trans_hash_;
  BlockIdExt base_blk_id_, base_blk_id_alt_, blk_id_;
  Ref mc_state_, mc_state0_;
  Ref state_;
  Ref mc_block_, block_;
  Ref mc_proof_, mc_proof_alt_;
  Ref proof_link_;
  td::BufferSlice buffer_;
  std::function continuation_;
  bool cont_set_{false};
  td::BufferSlice shard_proof_;
  std::vector[> roots_;
  std::vector][> aux_objs_;
  std::vector blk_ids_;
  std::unique_ptr chain_;
  Ref stack_;
 public:
  enum {
    default_timeout_msec = 4500,      // 4.5 seconds
    max_transaction_count = 16,       // fetch at most 16 transactions in one query
    client_method_gas_limit = 300000  // gas limit for liteServer.runSmcMethod
  };
  enum {
    ls_version = 0x101,
    ls_capabilities = 7
  };  // version 1.1; +1 = build block proof chains, +2 = masterchainInfoExt, +4 = runSmcMethod
  LiteQuery(td::BufferSlice data, td::actor::ActorId manager,
            td::Promise promise);
  LiteQuery(WorkchainId wc, StdSmcAddress  acc_addr, td::actor::ActorId manager,
            td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise);
  static void run_query(td::BufferSlice data, td::actor::ActorId manager,
                        td::Promise promise);
  static void fetch_account_state(WorkchainId wc, StdSmcAddress  acc_addr, td::actor::ActorId manager,
                                  td::Promise,UnixTime,LogicalTime,std::unique_ptr>> promise);
 private:
  bool fatal_error(td::Status error);
  bool fatal_error(std::string err_msg, int err_code = -400);
  bool fatal_error(int err_code, std::string err_msg = "");
  void abort_query(td::Status reason);
  void abort_query_ext(td::Status reason, std::string err_msg);
  bool finish_query(td::BufferSlice result);
  void alarm() override;
  void start_up() override;
  void perform_getTime();
  void perform_getVersion();
  void perform_getMasterchainInfo(int mode);
  void continue_getMasterchainInfo(Ref mc_state, BlockIdExt blkid, int mode);
  void gotMasterchainInfoForAccountState(Ref mc_state, BlockIdExt blkid, int mode);
  void perform_getBlock(BlockIdExt blkid);
  void continue_getBlock(BlockIdExt blkid, Ref block);
  void perform_getBlockHeader(BlockIdExt blkid, int mode);
  void continue_getBlockHeader(BlockIdExt blkid, int mode, Ref block);
  void perform_getState(BlockIdExt blkid);
  void continue_getState(BlockIdExt blkid, Ref state);
  void continue_getZeroState(BlockIdExt blkid, td::BufferSlice state);
  void perform_sendMessage(td::BufferSlice ext_msg);
  void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode);
  void continue_getAccountState_0(Ref mc_state, BlockIdExt blkid);
  void continue_getAccountState();
  void finish_getAccountState(td::BufferSlice shard_proof);
  void perform_fetchAccountState();
  void perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode, td::int64 method_id,
                            td::BufferSlice params);
  void finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref acc_root,
                           UnixTime gen_utime, LogicalTime gen_lt);
  void perform_getLibraries(std::vector library_list);
  void continue_getLibraries(Ref mc_state, BlockIdExt blkid, std::vector library_list);
  void perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt);
  void continue_getOneTransaction();
  void perform_getTransactions(WorkchainId workchain, StdSmcAddress addr, LogicalTime lt, Bits256 hash, unsigned count);
  void continue_getTransactions(unsigned remaining, bool exact);
  void continue_getTransactions_2(BlockIdExt blkid, Ref block, unsigned remaining);
  void abort_getTransactions(td::Status error, ton::BlockIdExt blkid);
  void finish_getTransactions();
  void perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool exact);
  void perform_getAllShardsInfo(BlockIdExt blkid);
  void continue_getShardInfo(ShardIdFull shard, bool exact);
  void continue_getAllShardsInfo();
  void perform_getConfigParams(BlockIdExt blkid, int mode, std::vector param_list = {});
  void continue_getConfigParams(int mode, std::vector param_list);
  void perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, UnixTime utime);
  void perform_listBlockTransactions(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt);
  void finish_listBlockTransactions(int mode, int count);
  void perform_listBlockTransactionsExt(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt);
  void finish_listBlockTransactionsExt(int mode, int count);
  void perform_getBlockProof(BlockIdExt from, BlockIdExt to, int mode);
  void continue_getBlockProof(BlockIdExt from, BlockIdExt to, int mode, BlockIdExt baseblk,
                              Ref state);
  void perform_getValidatorStats(BlockIdExt blkid, int mode, int count, Bits256 start_after, UnixTime min_utime);
  void continue_getValidatorStats(int mode, int limit, Bits256 start_after, UnixTime min_utime);
  bool construct_proof_chain(BlockIdExt id);
  bool construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdExt next);
  bool construct_proof_link_forward_cont(ton::BlockIdExt cur, ton::BlockIdExt next);
  bool construct_proof_link_back(ton::BlockIdExt cur, ton::BlockIdExt next);
  bool construct_proof_link_back_cont(ton::BlockIdExt cur, ton::BlockIdExt next);
  bool adjust_last_proof_link(ton::BlockIdExt cur, Ref block_root);
  bool finish_proof_chain(ton::BlockIdExt id);
  void perform_getShardBlockProof(BlockIdExt blkid);
  void continue_getShardBlockProof(Ref cur_block,
                                   std::vector> result);
  void load_prevKeyBlock(ton::BlockIdExt blkid, td::Promise>>);
  void continue_loadPrevKeyBlock(ton::BlockIdExt blkid, td::Result, BlockIdExt>> res,
                                 td::Promise>>);
  void finish_loadPrevKeyBlock(ton::BlockIdExt blkid, td::Result][> res,
                               td::Promise>> promise);
  void get_block_handle_checked(BlockIdExt blkid, td::Promise promise);
  bool request_block_data(BlockIdExt blkid);
  bool request_block_state(BlockIdExt blkid);
  bool request_block_data_state(BlockIdExt blkid);
  bool request_proof_link(BlockIdExt blkid);
  bool request_mc_block_data(BlockIdExt blkid);
  bool request_mc_block_state(BlockIdExt blkid);
  bool request_mc_block_data_state(BlockIdExt blkid);
  bool request_mc_proof(BlockIdExt blkid, int mode = 0);
  bool request_zero_state(BlockIdExt blkid);
  void got_block_state(BlockIdExt blkid, Ref state);
  void got_mc_block_state(BlockIdExt blkid, Ref state);
  void got_block_data(BlockIdExt blkid, Ref data);
  void got_mc_block_data(BlockIdExt blkid, Ref data);
  void got_mc_block_proof(BlockIdExt blkid, int mode, Ref proof);
  void got_block_proof_link(BlockIdExt blkid, Ref proof_link);
  void got_zero_state(BlockIdExt blkid, td::BufferSlice zerostate);
  void dec_pending() {
    if (!--pending_) {
      check_pending();
    }
  }
  void check_pending();
  bool set_continuation(std::function&& cont);
  bool make_mc_state_root_proof(Ref& proof);
  bool make_state_root_proof(Ref& proof);
  bool make_state_root_proof(Ref& proof, Ref state, Ref block,
                             const BlockIdExt& blkid);
  bool make_state_root_proof(Ref& proof, Ref state_root, Ref block_root,
                             const BlockIdExt& blkid);
  bool make_shard_info_proof(Ref& proof, Ref& info, ShardIdFull shard,
                             ShardIdFull& true_shard, Ref& leaf, bool& found, bool exact = true);
  bool make_shard_info_proof(Ref& proof, Ref& info, ShardIdFull shard, bool exact = true);
  bool make_shard_info_proof(Ref& proof, Ref& info, AccountIdPrefixFull prefix);
  bool make_shard_info_proof(Ref& proof, BlockIdExt& blkid, AccountIdPrefixFull prefix);
  bool make_ancestor_block_proof(Ref& proof, Ref state_root, const BlockIdExt& old_blkid);
};
}  // namespace validator
}  // namespace ton
]