/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#ifndef GEODE_CACHETRANSACTIONMANAGER_H_
#define GEODE_CACHETRANSACTIONMANAGER_H_

//#### Warning: DO NOT directly include Region.hpp, include Cache.hpp instead.

#include <chrono>
#include <memory>

#include "TransactionId.hpp"
#include "internal/geode_globals.hpp"

namespace apache {
namespace geode {
namespace client {

/**
 * An interface for objects who perform transaction management
 */

class APACHE_GEODE_EXPORT CacheTransactionManager {
 public:
  /** Creates a new transaction and associates it with the current thread.
   *
   * @throws IllegalStateException if the thread is already associated with a
   * transaction
   *
   * @since 3.6
   */
  virtual void begin() = 0;

  /**
   * Performs prepare during 2 phase commit completion, for the transaction
   * associated with the current thread.
   * Locks of the entries modified in the current transaction on the server
   * side. If the prepare operation fails due to a conflict it will destroy
   * the transaction state and throw a {@link CommitConflictException}.
   * If the prepare operation succeeds, transaction state is set to
   * prepared state.  When this method completes, the thread is still
   * associated with a transaction, and is waiting on commit or rollback
   * operation.
   *
   * @throws IllegalStateException if the thread is not associated with a
   * transaction
   *
   * @throws CommitConflictException if the commit operation fails due to
   * a write conflict.
   *
   * @throws TransactionDataNodeHasDepartedException if the node hosting the
   * transaction data has departed. This is only relevant for transaction that
   * involve PartitionedRegions.
   *
   * @throws TransactionDataNotColocatedException if at commit time, the data
   * involved in the transaction has moved away from the transaction hosting
   * node. This can only happen if rebalancing/recovery happens during a
   * transaction that involves a PartitionedRegion.
   *
   * @throws TransactionInDoubtException when Geode cannot tell which nodes
   * have applied the transaction and which have not. This only occurs if nodes
   * fail mid-commit, and only then in very rare circumstances.
   */
  virtual void prepare() = 0;

  /** Commit the transaction associated with the current thread. If
   *  the commit operation fails due to a conflict it will destroy
   *  the transaction state and throw a {@link
   *  CommitConflictException}.  If the commit operation succeeds,
   *  it returns after the transaction state has been merged with
   *  committed state.  When this method completes, the thread is no
   *  longer associated with a transaction.
   *
   * @throws IllegalStateException if the thread is not associated with a
   * transaction
   *
   * @throws CommitConflictException if the commit operation fails due to
   *   a write conflict.
   *
   * @throws TransactionDataNodeHasDepartedException if the node hosting the
   * transaction data has departed. This is only relevant for transaction that
   * involve PartitionedRegions.
   *
   * @throws TransactionDataNotColocatedException if at commit time, the data
   * involved in the transaction has moved away from the transaction hosting
   * node. This can only happen if rebalancing/recovery happens during a
   * transaction that involves a PartitionedRegion.
   *
   * @throws TransactionInDoubtException when Geode cannot tell which nodes
   * have applied the transaction and which have not. This only occurs if nodes
   * fail mid-commit, and only then in very rare circumstances.
   */
  virtual void commit() = 0;

  /** Roll back the transaction associated with the current thread. When
   *  this method completes, the thread is no longer associated with a
   *  transaction and the transaction context is destroyed.
   *
   * @since 3.6
   *
   * @throws IllegalStateException if the thread is not associated with a
   * transaction
   */
  virtual void rollback() = 0;

  /**
   * Suspends the transaction on the current thread. All subsequent operations
   * performed by this thread will be non-transactional. The suspended
   * transaction can be resumed by calling {@link #resume(TransactionId)}
   *
   * @return the transaction identifier of the suspended transaction or null if
   *         the thread was not associated with a transaction
   * @since 3.6.2
   */
  virtual TransactionId& suspend() = 0;

  /**
   * On the current thread, resumes a transaction that was previously suspended
   * using {@link #suspend()}
   *
   * @param transactionId
   *          the transaction to resume
   * @throws IllegalStateException
   *           if the thread is associated with a transaction or if
   *           {@link #isSuspended(TransactionId)} would return false for the
   *           given transactionId
   * @since 3.6.2
   */
  virtual void resume(TransactionId& transactionId) = 0;

  /**
   * This method can be used to determine if a transaction with the given
   * transaction identifier is currently suspended locally. This method does not
   * check other members for transaction status.
   *
   * @param transactionId transaction to check if is suspended
   * @return true if the transaction is in suspended state, false otherwise
   * @since 3.6.2
   * @see #exists(TransactionId)
   */
  virtual bool isSuspended(TransactionId& transactionId) = 0;

  /**
   * On the current thread, resumes a transaction that was previously suspended
   * using {@link #suspend()}.
   *
   * This method is equivalent to
   * <pre>
   * if (isSuspended(txId)) {
   *   resume(txId);
   * }
   * </pre>
   * except that this action is performed atomically
   *
   * @param transactionId
   *          the transaction to resume
   * @return true if the transaction was resumed, false otherwise
   * @since 3.6.2
   */
  virtual bool tryResume(TransactionId& transactionId) = 0;

  /**
   * On the current thread, resumes a transaction that was previously suspended
   * using {@link #suspend()}, or waits for the specified timeout interval if
   * the transaction has not been suspended. This method will return if:
   * <ul>
   * <li>Another thread suspends the transaction</li>
   * <li>Another thread calls commit/rollback on the transaction</li>
   * <li>This thread has waited for the specified timeout</li>
   * </ul>
   *
   * This method returns immediately if {@link #exists(TransactionId)} returns
   * false.
   *
   * @param transactionId
   *          the transaction to resume
   * @param waitTime
   *          the maximum time to wait
   * @return true if the transaction was resumed, false otherwise
   * @since 3.6.2
   * @see #tryResume(TransactionId)
   */
  virtual bool tryResume(TransactionId& transactionId,
                         std::chrono::milliseconds waitTime) = 0;

  /**
   * Reports the existence of a transaction for the given transactionId. This
   * method can be used to determine if a transaction with the given transaction
   * identifier is currently in progress locally.
   *
   * @param transactionId
   *          the given transaction identifier
   * @return true if the transaction is in progress, false otherwise.
   * @since 3.6.2
   * @see #isSuspended(TransactionId)
   */
  virtual bool exists(TransactionId& transactionId) = 0;

  /** Returns the transaction identifier for the current thread
   *
   * @return the transaction identifier or null if no transaction exists
   *
   * @since 3.6.2
   */
  virtual TransactionId& getTransactionId() = 0;

  /** Reports the existence of a Transaction for this thread
   *
   * @return true if a transaction exists, false otherwise
   *
   * @since 3.6
   */
  virtual bool exists() = 0;

 protected:
  CacheTransactionManager();
  virtual ~CacheTransactionManager();
};
}  // namespace client
}  // namespace geode
}  // namespace apache

#endif  // GEODE_CACHETRANSACTIONMANAGER_H_
