// Check whether need to recovery, if yes, reset fseek to the right place.
bool WriteBehindFrontendLogger::NeedRecovery(void) {
  // Otherwise, read the last transaction record
  fseek(log_file, -TransactionRecord::GetTransactionRecordSize(), SEEK_END);

  // Get its type
  auto log_record_type = GetNextLogRecordType(log_file, log_file_size);

  // Check if the previous transaction run is broken
  if (log_record_type == LOGRECORD_TYPE_TRANSACTION_COMMIT) {
    TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_COMMIT);

    // read the last written out transaction log record
    if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) ==
        false) {
      return false;
    }

    // Peloton log records items have fixed size.
    // Compute log offset based on txn_id
    size_t tuple_log_record_count = txn_record.GetTransactionId();

    size_t rollback_offset =
        tuple_log_record_count * TupleRecord::GetTupleRecordSize() +
        TransactionRecord::GetTransactionRecordSize();

    // Rollback to the computed offset
    fseek(log_file, -rollback_offset, SEEK_END);
    return true;
  } else {
    return false;
  }
}
/**
 * @brief Add new txn to recovery table
 */
void AriesFrontendLogger::AddTransactionToRecoveryTable() {
  // read transaction information from the log file
  TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_BEGIN);

  // Check for torn log write
  if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) ==
      false) {
    return;
  }

  auto txn_id = txn_record.GetTransactionId();

  // create the new txn object and added it into recovery recovery_txn_table
  concurrency::Transaction *txn =
      new concurrency::Transaction(txn_id, INVALID_CID);
  recovery_txn_table.insert(std::make_pair(txn_id, txn));
  LOG_TRACE("Added txd id %d object in table", (int)txn_id);
}
/**
 * @brief abort tuple
 */
void AriesFrontendLogger::AbortTuplesFromRecoveryTable() {
  // read transaction information from the log file
  TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_ABORT);

  // Check for torn log write
  if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) ==
      false) {
    return;
  }

  auto txn_id = txn_record.GetTransactionId();

  // Get info about the transaction from recovery table
  if (recovery_txn_table.find(txn_id) != recovery_txn_table.end()) {
    auto txn = recovery_txn_table.at(txn_id);
    AbortTuples(txn);
    LOG_INFO("Abort txd id %d object in table", (int)txn_id);
  } else {
    LOG_INFO("Abort txd id %d not found in recovery txn table", (int)txn_id);
  }
}
/**
 * @brief move tuples from current txn to recovery txn so that we can commit
 * them later
 * @param recovery txn
 */
void AriesFrontendLogger::MoveCommittedTuplesToRecoveryTxn(
    concurrency::Transaction *recovery_txn) {
  // read transaction information from the log file
  TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_COMMIT);

  // Check for torn log write
  if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) ==
      false) {
    return;
  }

  // Get info about the transaction from recovery table
  auto txn_id = txn_record.GetTransactionId();
  if (recovery_txn_table.find(txn_id) != recovery_txn_table.end()) {
    auto txn = recovery_txn_table.at(txn_id);
    // Copy inserted/deleted tuples to recovery transaction
    MoveTuples(recovery_txn, txn);

    LOG_TRACE("Commit txd id %d object in table", (int)txn_id);
  } else {
    LOG_TRACE("Commit txd id %d not found in recovery txn table", (int)txn_id);
  }
}
/**
 * @brief Remove txn from recovery table
 */
void AriesFrontendLogger::RemoveTransactionFromRecoveryTable() {
  // read transaction information from the log file
  TransactionRecord txn_record(LOGRECORD_TYPE_TRANSACTION_END);

  // Check for torn log write
  if (ReadTransactionRecordHeader(txn_record, log_file, log_file_size) ==
      false) {
    return;
  }

  auto txn_id = txn_record.GetTransactionId();

  // remove txn from recovery txn table
  if (recovery_txn_table.find(txn_id) != recovery_txn_table.end()) {
    auto txn = recovery_txn_table.at(txn_id);
    recovery_txn_table.erase(txn_id);

    // drop the txn as well
    delete txn;
    LOG_TRACE("Erase txd id %d object in table", (int)txn_id);
  } else {
    LOG_TRACE("Erase txd id %d not found in recovery txn table", (int)txn_id);
  }
}