/**
 * 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 OCEANBASE_COMMON_OB_ROWKEY_H_
#define OCEANBASE_COMMON_OB_ROWKEY_H_

#include <stdint.h>
#include "lib/oblog/ob_log.h"
#include "lib/ob_define.h"
#include "lib/utility/utility.h"
#include "lib/regex/ob_regex.h"
#include "lib/checksum/ob_crc64.h"
#include "lib/json/ob_yson.h"
#include "common/ob_common_utility.h"
#include "common/object/ob_object.h"

namespace oceanbase {
namespace common {

class ObRowkeyInfo;
class ObStoreRowkey;

class ObRowkey {
public:
  ObRowkey() : obj_ptr_(NULL), obj_cnt_(0)
  {}
  ObRowkey(ObObj* ptr, const int64_t cnt) : obj_ptr_(ptr), obj_cnt_(cnt)
  {}
  ~ObRowkey()
  {}

public:
  int to_store_rowkey(ObStoreRowkey& store_rowkey) const;
  void reset()
  {
    obj_ptr_ = NULL;
    obj_cnt_ = 0;
  }
  inline int64_t get_obj_cnt() const
  {
    return obj_cnt_;
  }
  inline const ObObj* get_obj_ptr() const
  {
    return obj_ptr_;
  }
  inline ObObj* get_obj_ptr()
  {
    return obj_ptr_;
  }
  // for convenience compactible with ObString
  inline int64_t length() const
  {
    return obj_cnt_;
  }
  inline const ObObj* ptr() const
  {
    return obj_ptr_;
  }
  inline bool is_legal() const
  {
    return !(NULL == obj_ptr_ && obj_cnt_ > 0);
  }
  inline bool is_valid() const
  {
    return NULL != obj_ptr_ && obj_cnt_ > 0;
  }
  // is min rowkey or max rowkey
  inline bool is_min_row(void) const
  {
    bool bret = false;
    int64_t i = 0;
    for (i = 0; i < obj_cnt_; i++) {
      if (!obj_ptr_[i].is_min_value()) {
        break;
      }
    }
    if (obj_cnt_ > 0 && i >= obj_cnt_) {
      bret = true;
    }
    return bret;
  }

  inline bool is_max_row(void) const
  {
    bool bret = false;
    int64_t i = 0;
    for (i = 0; i < obj_cnt_; i++) {
      if (!obj_ptr_[i].is_max_value()) {
        break;
      }
    }
    if (obj_cnt_ > 0 && i >= obj_cnt_) {
      bret = true;
    }
    return bret;
  }

  inline void set_min_row(void)
  {
    *this = ObRowkey::MIN_ROWKEY;
  }
  inline void set_max_row(void)
  {
    *this = ObRowkey::MAX_ROWKEY;
  }
  inline void set_length(const int64_t cnt)
  {
    obj_cnt_ = cnt;
  }
  inline void assign(ObObj* ptr, const int64_t cnt)
  {
    obj_ptr_ = ptr;
    obj_cnt_ = cnt;
  }
  inline int obj_copy(ObRowkey& rowkey, ObObj* obj_buf, const int64_t cnt) const;

  int64_t get_deep_copy_size() const;

  template <typename Allocator>
  int deep_copy(ObRowkey& rhs, Allocator& allocator) const;

  OB_INLINE int deep_copy(char* buffer, int64_t size) const;
  OB_INLINE int deep_copy(ObRowkey& out, char* buffer, int64_t size) const;

  template <typename Allocator>
  int deserialize(Allocator& allocator, const char* buf, const int64_t data_len, int64_t& pos);

  int serialize(char* buf, const int64_t buf_len, int64_t& pos) const;
  int64_t get_serialize_size(void) const;
  int deserialize(const char* buf, const int64_t buf_len, int64_t& pos);

  int serialize_objs(char* buf, const int64_t buf_len, int64_t& pos) const;
  int64_t get_serialize_objs_size(void) const;
  int deserialize_objs(const char* buf, const int64_t buf_len, int64_t& pos);

  TO_YSON_KV(OB_ID(rowkey), to_cstring(*this));
  int64_t to_string(char* buffer, const int64_t length) const;
  // to_smart_string and to_format_string are for log_tool use
  int64_t to_smart_string(char* buffer, const int64_t length) const;
  int64_t to_format_string(char* buffer, const int64_t length) const;

  int64_t to_plain_string(char* buffer, const int64_t length) const;
  int need_transform_to_collation_free(bool& need_transform) const;
  template <typename Allocator>
  int to_collation_free_rowkey(common::ObRowkey& collation_free_rowkey, Allocator& allocator) const;
  template <typename Allocator>
  int to_collation_free_rowkey_on_demand(common::ObRowkey& collation_free_rowkey, Allocator& allocator) const;

  int checksum(ObBatchChecksum& bc) const;
  uint64_t murmurhash(const uint64_t hash) const;
  inline uint64_t hash() const
  {
    return murmurhash(0);
  }
  static int get_common_prefix_length(const ObRowkey& lhs, const ObRowkey& rhs, int64_t& prefix_len);

public:
  int equal(const ObRowkey& rhs, bool& is_equal) const;
  bool simple_equal(const ObRowkey& rhs) const;
  // FIXME temporarily, rowkey compare with column order use seperate func
  OB_INLINE int compare(const ObRowkey& rhs, int& cmp) const
  {
    int ret = OB_SUCCESS;
    cmp = 0;
    if (obj_ptr_ == rhs.obj_ptr_) {
      cmp = static_cast<int32_t>(obj_cnt_ - rhs.obj_cnt_);
    } else {
      ret = compare_prefix(rhs, cmp);
      if (0 == cmp) {
        cmp = static_cast<int32_t>(obj_cnt_ - rhs.obj_cnt_);
      }
    }
    return ret;
  }
  // TODO : remove this function
  OB_INLINE int compare(const ObRowkey& rhs) const
  {
    int cmp = 0;
    if (obj_ptr_ == rhs.obj_ptr_) {
      cmp = static_cast<int32_t>(obj_cnt_ - rhs.obj_cnt_);
    } else {
      // Ignore return code because this version of compare does not have
      // return code. The whole function will be removed later.
      compare_prefix(rhs, cmp);
      if (0 == cmp) {
        cmp = static_cast<int32_t>(obj_cnt_ - rhs.obj_cnt_);
      }
    }
    return cmp;
  }

  OB_INLINE int compare_prefix(const ObRowkey& rhs, int& cmp) const
  {
    int ret = OB_SUCCESS;
    int lv = 0;
    int rv = 0;
    cmp = 0;
    // TODO remove disgusting loop, useless max min judge
    if (is_min_row()) {
      lv = -1;
    } else if (is_max_row()) {
      lv = 1;
    }
    if (rhs.is_min_row()) {
      rv = -1;
    } else if (rhs.is_max_row()) {
      rv = 1;
    }
    if (0 == lv && 0 == rv) {
      int64_t i = 0;
      int64_t cmp_cnt = std::min(obj_cnt_, rhs.obj_cnt_);
      for (; i < cmp_cnt && 0 == cmp && OB_SUCC(ret); ++i) {
        // TODO remove collation free check
        ret = obj_ptr_[i].check_collation_free_and_compare(rhs.obj_ptr_[i], cmp);
      }
    } else {
      cmp = lv - rv;
    }
    return ret;
  }

  inline bool operator<(const ObRowkey& rhs) const
  {
    return compare(rhs) < 0;
  }

  inline bool operator<=(const ObRowkey& rhs) const
  {
    return compare(rhs) <= 0;
  }

  inline bool operator>(const ObRowkey& rhs) const
  {
    return compare(rhs) > 0;
  }

  inline bool operator>=(const ObRowkey& rhs) const
  {
    return compare(rhs) >= 0;
  }

  inline bool operator==(const ObRowkey& rhs) const
  {
    return compare(rhs) == 0;
  }
  inline bool operator!=(const ObRowkey& rhs) const
  {
    return compare(rhs) != 0;
  }

private:
  ObObj* obj_ptr_;
  int64_t obj_cnt_;

public:
  static ObObj MIN_OBJECT;
  static ObObj MAX_OBJECT;
  static ObRowkey MIN_ROWKEY;
  static ObRowkey MAX_ROWKEY;
};

inline int64_t ObRowkey::get_deep_copy_size() const
{
  int tmp_ret = OB_SUCCESS;
  int64_t obj_arr_len = obj_cnt_ * sizeof(common::ObObj);
  int64_t total_len = obj_arr_len;

  if (OB_UNLIKELY(!is_legal())) {
    tmp_ret = OB_INVALID_DATA;
    COMMON_LOG(ERROR, "illegal rowkey.", KP_(obj_ptr), K_(obj_cnt), K(tmp_ret));
  } else {
    for (int64_t i = 0; i < obj_cnt_; ++i) {
      total_len += obj_ptr_[i].get_deep_copy_size();
    }
  }

  return total_len;
}

template <typename Allocator>
int ObRowkey::deserialize(Allocator& allocator, const char* buf, const int64_t data_len, int64_t& pos)
{
  int ret = OB_SUCCESS;
  ObObj array[OB_MAX_ROWKEY_COLUMN_NUMBER];
  ObRowkey copy_rowkey;
  copy_rowkey.assign(array, OB_MAX_ROWKEY_COLUMN_NUMBER);

  if (NULL == buf || data_len <= 0) {
    ret = OB_INVALID_ARGUMENT;
    COMMON_LOG(WARN, "invalid arguments.", KP(buf), K(data_len), K(ret));
  } else if (OB_FAIL(copy_rowkey.deserialize(buf, data_len, pos))) {
    COMMON_LOG(WARN, "deserialize to shallow copy key failed.", KP(buf), K(data_len), K(pos), K(ret));
  } else if (OB_FAIL(copy_rowkey.deep_copy(*this, allocator))) {
    COMMON_LOG(WARN, "deep copy to self failed.", KP(buf), K(data_len), K(pos), K(ret));
  }

  return ret;
}

OB_INLINE int ObRowkey::deep_copy(char* ptr, int64_t size) const
{
  int ret = OB_SUCCESS;

  int64_t obj_arr_len = obj_cnt_ * sizeof(ObObj);
  if (OB_UNLIKELY(NULL == ptr) || OB_UNLIKELY(size < obj_arr_len)) {
    ret = OB_INVALID_ARGUMENT;
    COMMON_LOG(WARN, "invalid arguments.", KP(ptr), K(size), KP_(obj_cnt), K(ret));
  } else if (obj_cnt_ > 0 && NULL != obj_ptr_) {
    ObObj* obj_ptr = NULL;
    int64_t pos = 0;

    obj_ptr = reinterpret_cast<ObObj*>(ptr);
    pos += obj_arr_len;

    for (int64_t i = 0; i < obj_cnt_ && OB_SUCCESS == ret; ++i) {
      if (OB_FAIL(obj_ptr[i].deep_copy(obj_ptr_[i], ptr, size, pos))) {
        COMMON_LOG(WARN, "deep copy object failed.", K(i), K(obj_ptr_[i]), K(size), K(pos), K(ret));
      }
    }
  }

  return ret;
}

OB_INLINE int ObRowkey::deep_copy(ObRowkey& rhs, char* ptr, int64_t total_len) const
{
  int ret = OB_SUCCESS;
  rhs.reset();

  if (OB_UNLIKELY(!is_legal())) {
    ret = OB_INVALID_DATA;
    COMMON_LOG(WARN, "illegal rowkey.", KP_(obj_ptr), K_(obj_cnt), K(ret));
  } else if (obj_cnt_ > 0 && NULL != obj_ptr_) {
    if (OB_FAIL(deep_copy(ptr, total_len))) {
      COMMON_LOG(WARN, "deep copy rowkey failed.", KP_(obj_ptr), KP_(obj_cnt), K(ret));
    } else {
      rhs.assign(reinterpret_cast<ObObj*>(ptr), obj_cnt_);
    }
  } else {
    rhs.assign(NULL, 0);
  }
  return ret;
}

template <typename Allocator>
int ObRowkey::deep_copy(ObRowkey& rhs, Allocator& allocator) const
{
  int ret = OB_SUCCESS;

  int64_t total_len = get_deep_copy_size();
  char* ptr = NULL;
  if (0 == total_len) {
    rhs.obj_ptr_ = NULL;
    rhs.obj_cnt_ = 0;
  } else if (OB_ISNULL(ptr = (char*)allocator.alloc(total_len))) {
    ret = OB_ALLOCATE_MEMORY_FAILED;
    COMMON_LOG(WARN, "allocate mem for obj array failed.", K(total_len), K(ret));
  } else if (OB_FAIL(deep_copy(rhs, ptr, total_len))) {
    COMMON_LOG(WARN, "failed to deep copy", K(ret));
  }

  if (OB_FAIL(ret) && NULL != ptr) {
    allocator.free(ptr);
    ptr = NULL;
  }

  return ret;
}

template <typename Allocator>
int ObRowkey::to_collation_free_rowkey(ObRowkey& collation_free_rowkey, Allocator& allocator) const
{
  int ret = OB_SUCCESS;
  if (obj_cnt_ <= 0 || NULL == obj_ptr_) {
    ret = OB_INVALID_ARGUMENT;
    STORAGE_LOG(WARN, "invalid argument", K(ret), K(obj_cnt_), K(obj_ptr_));
  } else {
    ObObj* endkey = NULL;
    if (NULL == (endkey = reinterpret_cast<ObObj*>(allocator.alloc(sizeof(ObObj) * obj_cnt_)))) {
      ret = OB_ALLOCATE_MEMORY_FAILED;
      STORAGE_LOG(WARN, "failed to alloc endkey", K(ret));
    } else {
      const bool is_copy_all = true;
      bool is_obj_collation_free_valid = false;
      collation_free_rowkey.assign(endkey, obj_cnt_);
      for (int64_t i = 0; OB_SUCC(ret) && i < obj_cnt_; ++i) {
        if (obj_ptr_[i].is_character_type()) {
          if (OB_FAIL(obj_ptr_[i].to_collation_free_obj(
                  collation_free_rowkey.obj_ptr_[i], is_obj_collation_free_valid, allocator))) {
            COMMON_LOG(WARN, "fail to convert obj to collation free obj", K(ret), K(obj_ptr_[i]));
          } else if (!is_obj_collation_free_valid) {
            collation_free_rowkey.assign(NULL, 0);
            break;
          }
        } else {
          obj_ptr_[i].copy_value_or_obj(collation_free_rowkey.obj_ptr_[i], is_copy_all);
        }
      }
    }
  }

  if (OB_FAIL(ret)) {
    collation_free_rowkey.assign(NULL, 0);
  }

  return ret;
}

template <typename Allocator>
int ObRowkey::to_collation_free_rowkey_on_demand(ObRowkey& collation_free_rowkey, Allocator& allocator) const
{
  int ret = OB_SUCCESS;
  bool need_transform = false;
  if (obj_cnt_ <= 0 || NULL == obj_ptr_) {
    ret = OB_INVALID_ARGUMENT;
    STORAGE_LOG(WARN, "invalid argument", K(ret), K(obj_cnt_), K(obj_ptr_));
  } else if (OB_FAIL(need_transform_to_collation_free(need_transform))) {
    STORAGE_LOG(WARN, "fail to get if need to transform to collation free rowkey", K(ret));
  } else if (need_transform && OB_FAIL(to_collation_free_rowkey(collation_free_rowkey, allocator))) {
    STORAGE_LOG(WARN, "fail to get collation free rowkey", K(ret));
  }
  return ret;
}

inline std::ostream& operator<<(std::ostream& os, const ObRowkey& key)  // for google test
{
  os << " len=" << key.get_obj_cnt();
  return os;
}

template <typename AllocatorT>
int ob_write_rowkey(AllocatorT& allocator, const ObRowkey& src, ObRowkey& dst)
{
  return src.deep_copy(dst, allocator);
}

// only shallow copy obj, varchar is not copied
inline int ObRowkey::obj_copy(ObRowkey& dest_rowkey, ObObj* obj_buf, const int64_t cnt) const
{
  int ret = OB_SUCCESS;
  if (OB_UNLIKELY(NULL == obj_buf) || OB_UNLIKELY(cnt < obj_cnt_)) {
    ret = OB_INVALID_ARGUMENT;
    COMMON_LOG(WARN, "invalid argument.", KP(obj_buf), K(cnt), K_(obj_cnt), K(ret));
  } else if (OB_UNLIKELY(!is_legal())) {
    ret = OB_INVALID_DATA;
    COMMON_LOG(WARN, "illegal rowkey.", KP_(obj_ptr), K_(obj_cnt), K(ret));
  } else {
    for (int64_t i = 0; i < obj_cnt_; ++i) {
      obj_buf[i] = obj_ptr_[i];
    }
    dest_rowkey.assign(obj_buf, obj_cnt_);
  }

  return ret;
}

}  // namespace common
}  // namespace oceanbase

#endif  // OCEANBASE_COMMON_OB_ROWKEY_H_
