/**
 * Copyright Soramitsu Co., Ltd. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef CONSENSUS_GATE_OBJECT_HPP
#define CONSENSUS_GATE_OBJECT_HPP

#include <boost/variant.hpp>
#include "ametsuchi/ledger_state.hpp"
#include "consensus/round.hpp"
#include "cryptography/hash.hpp"
#include "interfaces/common_objects/types.hpp"

namespace shared_model {
  namespace interface {
    class Block;
  }  // namespace interface
}  // namespace shared_model

namespace iroha {
  namespace consensus {

    struct BaseGateObject {
      consensus::Round round;
      std::shared_ptr<const LedgerState> ledger_state;

      BaseGateObject(consensus::Round round,
                     std::shared_ptr<const LedgerState> ledger_state)
          : round(std::move(round)), ledger_state(std::move(ledger_state)) {}
    };

    /// Current pair is valid
    struct PairValid : public BaseGateObject {
      std::shared_ptr<shared_model::interface::Block> block;

      PairValid(consensus::Round round,
                std::shared_ptr<const LedgerState> ledger_state,
                std::shared_ptr<shared_model::interface::Block> block)
          : BaseGateObject(std::move(round), std::move(ledger_state)),
            block(std::move(block)) {}
    };

    struct Synchronizable : public BaseGateObject {
      shared_model::interface::types::PublicKeyCollectionType public_keys;

      Synchronizable(
          consensus::Round round,
          std::shared_ptr<const LedgerState> ledger_state,
          shared_model::interface::types::PublicKeyCollectionType public_keys)
          : BaseGateObject(std::move(round), std::move(ledger_state)),
            public_keys(std::move(public_keys)) {}
    };

    /// Network votes for another pair and round
    struct VoteOther : public Synchronizable {
      shared_model::interface::types::HashType hash;

      VoteOther(
          consensus::Round round,
          std::shared_ptr<const LedgerState> ledger_state,
          shared_model::interface::types::PublicKeyCollectionType public_keys,
          shared_model::interface::types::HashType hash)
          : Synchronizable(std::move(round),
                           std::move(ledger_state),
                           std::move(public_keys)),
            hash(std::move(hash)) {}
    };

    /// Reject on proposal
    struct ProposalReject : public Synchronizable {
      using Synchronizable::Synchronizable;
    };

    /// Reject on block
    struct BlockReject : public Synchronizable {
      using Synchronizable::Synchronizable;
    };

    /// Agreement on <None, None>
    struct AgreementOnNone : public Synchronizable {
      using Synchronizable::Synchronizable;
    };

    /// round.block_round > ledger_state->top_block_info.height + 1
    struct Future : public Synchronizable {
      using Synchronizable::Synchronizable;
    };

    using GateObject = boost::variant<PairValid,
                                      VoteOther,
                                      ProposalReject,
                                      BlockReject,
                                      AgreementOnNone,
                                      Future>;

  }  // namespace consensus
}  // namespace iroha

extern template class boost::variant<iroha::consensus::PairValid,
                                     iroha::consensus::VoteOther,
                                     iroha::consensus::ProposalReject,
                                     iroha::consensus::BlockReject,
                                     iroha::consensus::AgreementOnNone,
                                     iroha::consensus::Future>;

#endif  // CONSENSUS_GATE_OBJECT_HPP
