// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).

#pragma once

#include <cstdint>

#include "db/db_impl/db_impl.h"
#include "logging/logging.h"
#include "util/set_comparator.h"

namespace ROCKSDB_NAMESPACE {
// During recovery if the memtable is flushed we cannot rely on its help on
// duplicate key detection and as key insert will not be attempted. This class
// will be used as a emulator of memtable to tell if insertion of a key/seq
// would have resulted in duplication.
class DuplicateDetector {
 public:
  explicit DuplicateDetector(DBImpl* db) : db_(db) {}
  bool IsDuplicateKeySeq(uint32_t cf, const Slice& key, SequenceNumber seq) {
    assert(seq >= batch_seq_);
    if (batch_seq_ != seq) {  // it is a new batch
      keys_.clear();
    }
    batch_seq_ = seq;
    CFKeys& cf_keys = keys_[cf];
    if (cf_keys.size() == 0) {  // just inserted
      InitWithComp(cf);
    }
    auto it = cf_keys.insert(key);
    if (it.second == false) {  // second is false if a element already existed.
      keys_.clear();
      InitWithComp(cf);
      keys_[cf].insert(key);
      return true;
    }
    return false;
  }

 private:
  SequenceNumber batch_seq_ = 0;
  DBImpl* db_;
  using CFKeys = std::set<Slice, SetComparator>;
  std::map<uint32_t, CFKeys> keys_;
  void InitWithComp(const uint32_t cf) {
    auto h = db_->GetColumnFamilyHandle(cf);
    if (!h) {
      // TODO(myabandeh): This is not a concern in MyRocks as drop cf is not
      // implemented yet. When it does, we should return proper error instead
      // of throwing exception.
      ROCKS_LOG_FATAL(
          db_->immutable_db_options().info_log,
          "Recovering an entry from the dropped column family %" PRIu32
          ". WAL must must have been emptied before dropping the column "
          "family",
          cf);
      throw std::runtime_error(
          "Recovering an entry from a dropped column family. "
          "WAL must must have been flushed before dropping the column "
          "family");
      return;
    }
    auto cmp = h->GetComparator();
    keys_[cf] = CFKeys(SetComparator(cmp));
  }
};
}  // namespace ROCKSDB_NAMESPACE
