/**
 * Copyright (c) 2021 OceanBase
 * OceanBase CE is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef SRC_STORAGE_OB_MULTI_VERSION_TABLE_STORE_H_
#define SRC_STORAGE_OB_MULTI_VERSION_TABLE_STORE_H_
#include "ob_table_store.h"
#include "ob_i_table.h"
#include "share/config/ob_server_config.h"
namespace oceanbase {
namespace storage {

class ObPGMemtableMgr;

class ObMultiVersionTableStore {
public:
  typedef common::hash::ObHashSet<int64_t, common::hash::NoPthreadDefendMode> TableSet;
  static const int64_t MAX_KETP_VERSION_SHIFT_NUM = 4L;
  static const int64_t MAX_KETP_VERSION_NUM = 1LL << MAX_KETP_VERSION_SHIFT_NUM;
  static const int64_t MAX_DEFERRED_GC_MINOR_SSTABLE_COUNT = MAX_SSTABLE_CNT_IN_STORAGE;

  ObMultiVersionTableStore();
  virtual ~ObMultiVersionTableStore();

  int init(const common::ObPartitionKey &pkey, const uint64_t table_id, ObFreezeInfoSnapshotMgr *freeze_info_mgr,
      ObPGMemtableMgr *pg_memtable_mgr, const int64_t schema_version = 0);
  uint64_t get_table_id() const
  {
    return table_id_;
  }

  int set_reference_tables(
      ObTablesHandle &handle, ObTableStore *&new_table_store, const int64_t memtable_base_version, bool &need_update);
  int prepare_update_split_table_store(
      const bool is_major_split, ObTablesHandle &handle, bool &need_update, ObTableStore *&new_table_store);
  int check_need_split(common::ObVersion &split_version, bool &need_split, bool &need_minor_split);
  int check_can_split(bool &can_split);
  int check_can_migrate(bool &can_migrate);
  int check_complete(bool &is_complete);
  int is_physical_split_finished(bool &is_physical_split_finish);
  int get_physical_split_info(ObVirtualPartitionSplitInfo &split_info);
  int prepare_add_sstable(AddTableParam &param, ObTableStore *&new_table_store, bool &need_update);
  int prepare_remove_sstable(
      AddTableParam &param, ObTablesHandle &handle, ObTableStore *&new_table_store, bool &is_equal);
  int alloc_table_store(ObTableStore *&new_table_store);
  void free_table_store(ObTableStore *&new_table_store);
  int enable_table_store(const bool need_prewarm, ObTableStore &new_table_store);

  int get_read_tables(const int64_t snapshot_version, ObTablesHandle &handle, const bool allow_not_ready,
      const bool print_dropped_alert);
  int get_reference_tables(ObTablesHandle &handle);
  int get_sample_read_tables(const common::SampleInfo &sample_info, ObTablesHandle &handle);
  int get_major_sstable(const common::ObVersion &version, ObTablesHandle &handle);

  int get_all_tables(TableSet &table_set, ObTablesHandle &handle);
  int check_memtable_merged(const memtable::ObMemtable &memtable, bool &all_merged, bool &can_release);
  int get_latest_major_sstable(ObTableHandle &handle);
  int get_sstable_schema_version(int64_t &schema_version);
  int get_merge_tables(
      const ObGetMergeTablesParam &param, const int64_t multi_version_start, ObGetMergeTablesResult &result);

  int get_split_tables(const bool is_major_split, ObTablesHandle &handle);
  int retire_prewarm_store(const int64_t duration, const int64_t minor_deferred_gc_time);
  int get_latest_table_count(int64_t &latest_table_count);
  int check_latest_table_count_safe(bool &is_safe);
  int latest_has_major_sstable(bool &has_major);
  int get_effective_tables(const bool include_active_memtable, ObTablesHandle &handle);

  int finish_replay(const int64_t multi_version_start);
  int check_ready_for_read(bool &is_ready);
  int need_remove_old_table(const common::ObVersion &kept_min_version, const int64_t multi_version_start,
      int64_t &real_kept_major_num, bool &need_remove);
  int set_table_store(const ObTableStore &table_store);
  OB_INLINE ObTableStore *get_latest_store();
  int get_replay_tables(ObIArray<ObITable::TableKey> &replay_tables);
  int set_replay_sstables(const bool is_replay_old, const common::ObIArray<ObSSTable *> &sstables);
  int check_need_minor_merge(const bool is_follower_data_rep, const ObMergeType merge_type, bool &need_merge);
  int get_oldest_read_tables(const int64_t snapshot_version, ObTablesHandle &handle);
  int get_gc_sstables(ObTablesHandle &handle);
  int get_multi_version_start(int64_t &multi_version_start);
  int get_max_major_sstable_snapshot(int64_t &max_snapshot_version);
  int get_min_max_major_version(int64_t &min_version, int64_t &max_version);
  int get_latest_minor_sstables(ObTablesHandle &handle);
  int get_latest_continue_tables(ObTablesHandle &handle);
  int get_flashback_major_tables(const int64_t flashback_scn, ObTablesHandle &handle);
  int get_mark_deletion_tables(const int64_t end_log_ts, const int64_t snapshot_version, ObTablesHandle &handle);
  int clear_complement_minor_sstable();
  bool is_data_table()
  {
    return table_id_ == pkey_.get_table_id();
  }
  int get_min_schema_version(int64_t &min_schema_version);
  int get_schema_version(int64_t &schema_version);
  void set_is_dropped_schema()
  {
    is_dropped_schema_ = true;
  }
  bool is_dropped_schema() const
  {
    return is_dropped_schema_;
  }
  int get_migrate_tables(ObTablesHandle &handle, bool &is_ready_for_read);
  int get_needed_local_tables_for_migrate(const ObMigrateRemoteTableInfo &remote_table_info, ObTablesHandle &handle);
  int set_drop_schema_info(const int64_t drop_schema_version);
  int get_drop_schema_info(int64_t &drop_schema_version, int64_t &drop_schema_refreshed_ts);
  int get_recovery_point_tables(const int64_t snapshot_version, ObTablesHandle &handle);
  void set_create_schema_version(const int64_t schema_version)
  {
    create_schema_version_ = schema_version;
  }

  DECLARE_VIRTUAL_TO_STRING;

private:
  struct ObGCSSTableInfo final {
    ObSSTable *sstable_;
    int64_t retired_ts_;

    ObGCSSTableInfo();
    ~ObGCSSTableInfo();
    void reset();
    DECLARE_TO_STRING;
  };
  OB_INLINE int64_t get_idx(const int64_t pos) const;
  OB_INLINE ObTableStore *get_store(const int64_t pos);
  OB_INLINE ObTableStore *get_read_store();
  int retire_head_store();
  OB_INLINE ObTableStore *get_oldest_store();
  int record_gc_minor_sstable(ObTableStore &new_table_store);
  int add_gc_minor_sstable(const int64_t gc_ts, ObSSTable *table, const int64_t max_deferred_gc_count);
  int add_complement_minor_sstable_if_needed_(AddTableParam &param, ObTableStore &store);

private:
  bool is_inited_;
  common::ObPartitionKey pkey_;
  uint64_t table_id_;
  ObTableStore *table_stores_[MAX_KETP_VERSION_NUM];
  int64_t head_;
  int64_t tail_;
  common::ObRandom rand_;
  ObFreezeInfoSnapshotMgr *freeze_info_mgr_;
  ObPGMemtableMgr *pg_memtable_mgr_;
  // Currently, only mini sstables are recorded, sorted by release time, and those released earlier are later
  ObGCSSTableInfo gc_sstable_infos_[MAX_DEFERRED_GC_MINOR_SSTABLE_COUNT];
  int64_t gc_sstable_count_;
  int64_t create_schema_version_;
  bool is_dropped_schema_;
  // Record the version number of the drop in the index table,
  // which is expected to be greater than the version number when it is actually deleted
  int64_t drop_schema_version_;
  // Record the timestamp corresponding to the version number of the index table drop,
  // which is a local timestamp, which may be greater than the actual drop time of the index table
  int64_t drop_schema_refreshed_ts_;

  DISALLOW_COPY_AND_ASSIGN(ObMultiVersionTableStore);
};

OB_INLINE int64_t ObMultiVersionTableStore::get_idx(const int64_t pos) const
{
  return pos & ((1L << MAX_KETP_VERSION_SHIFT_NUM) - 1L);
}

OB_INLINE ObTableStore *ObMultiVersionTableStore::get_store(const int64_t pos)
{
  return table_stores_[get_idx(pos)];
}

OB_INLINE ObTableStore *ObMultiVersionTableStore::get_latest_store()
{
  ObTableStore *table_store = NULL;
  if (tail_ > head_) {
    table_store = table_stores_[get_idx(tail_ - 1L)];
  }
  return table_store;
}
OB_INLINE ObTableStore *ObMultiVersionTableStore::get_read_store()
{
  int64_t index = get_idx(head_);
  const int64_t duration = GCONF.minor_warm_up_duration_time;
  if (duration > 0 && tail_ - head_ > 1L) {
    const int64_t now = ObTimeUtility::current_time();
    const int64_t rand = rand_.get(0L, duration);

    for (int64_t i = tail_ - 1L; i > head_; --i) {
      const int64_t start_time = table_stores_[get_idx(i)]->get_uptime();
      // The function is transformed into a quadratic function,
      // and the flow rate increases slowly during warm-up and then increases rapidly
      const double x = static_cast<int>(now - start_time) / static_cast<double>(duration);
      const double y = x * x;

      if (rand < y * static_cast<int>(duration)) {
        index = get_idx(i);
        break;  // stop searching
      }
    }
  }

  return table_stores_[index];
}

OB_INLINE ObTableStore *ObMultiVersionTableStore::get_oldest_store()
{
  ObTableStore *table_store = NULL;
  if (head_ < tail_) {
    table_store = table_stores_[get_idx(head_)];
  }
  return table_store;
}

}  // namespace storage
}  // namespace oceanbase

#endif /* SRC_STORAGE_OB_MULTI_VERSION_TABLE_STORE_H_ */
