/**
 * 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_SQL_OPTIMIZER_OB_OPT_EST_UTILS_
#define OCEANBASE_SQL_OPTIMIZER_OB_OPT_EST_UTILS_
#include "sql/resolver/expr/ob_raw_expr.h"

namespace oceanbase {
namespace share {
namespace schema {
class ObSchemaGetterGuard;
}
}  // namespace share
namespace sql {
class ObLogPlan;
class ObEstSelInfo;
class ObSQLSessionInfo;
class ObOptimizerContext;
struct RangeExprs {
  RangeExprs() : table_id_(common::OB_INVALID_ID), column_id_(common::OB_INVALID_ID), range_exprs_()
  {}
  TO_STRING_KV(K_(table_id), K_(column_id));
  uint64_t table_id_;
  uint64_t column_id_;
  common::ObSEArray<ObRawExpr*, 2, common::ModulePageAllocator, true> range_exprs_;
};

class ObOptEstUtils {
public:
  // check if op is monotonic, some type may not considered.
  static bool is_monotonic_op(const ObItemType type);

  //@brief expr is cmp_op , has is_range_cond flag or is simple op_and,op_or
  // such as, c1 > 1 and c1 < 2, c1 between 1 and 1000, c1 > 100 or c1 < 10000
  //@param in qual
  //@param out is_range
  static int is_range_expr(const ObRawExpr* qual, bool& is_simple_filter, const int64_t level = 0);

  // extract column exprs with simple operator check.
  // level must be initialized with 0(default value)
  static int extract_column_exprs_with_op_check(const ObRawExpr* raw_expr,
      common::ObIArray<const ObRawExpr*>& column_exprs, bool& only_monotonic_op, const int64_t level = 0);

  //@brief according different column, generate one or more range_exprs, push them into column_exprs_array.
  //@brief the item in range_exprs mush be is_range_expr() -> see notes below.
  //@param qual the expr need be dealed with
  //@param out can_be_extracted, if the expr can be extracted range here
  //@param out column_exprs_array, if generate new range_expr, pushed it into this array
  static int extract_simple_cond_filters(
      ObRawExpr& qual, bool& can_be_extracted, common::ObIArray<RangeExprs>& column_exprs_array);

  // is_calculable_expr:is PARAM with param idx[0, param_count) or const value, or calculable
  static bool is_calculable_expr(const ObRawExpr& expr, const int64_t param_count);

  // get the expr's value.
  //@@param get_value[out]:is_calculable_expr:true, otherwise:false
  //@@param value[out]:the value of calculable expr
  static int get_expr_value(const ParamStore* params, const ObRawExpr& expr, ObSQLSessionInfo* session,
      common::ObIAllocator& allocator, bool& get_value, common::ObObj& value);

  // whether the value of calculable expr is null.
  static int if_expr_value_null(const ParamStore* params, const ObRawExpr& expr, ObSQLSessionInfo* session,
      common::ObIAllocator& allocator, bool& is_null);

  // whether the like condition's patten start with '%' or '_' and not start with escape sign
  static int if_expr_start_with_patten_sign(const ParamStore* params, const ObRawExpr* expr, const ObRawExpr* esp_expr,
      ObSQLSessionInfo* session, ObIAllocator& allocator, bool& is_start_with);

  // whether the value of first_expr and second_expr is equal.
  // if someone of the exprs is not calculable, equal return false;
  //@@param null_safe, if think NULL equal NULL, null_safe should be true, otherwise false.
  static int if_expr_value_equal(ObOptimizerContext& context, const ObDMLStmt* stmt, const ObRawExpr& first_expr,
      const ObRawExpr& second_expr, const bool null_safe, bool& equal);

  static int is_column_primary_key(const uint64_t col_id, const uint64_t table_id,
      share::schema::ObSchemaGetterGuard& schema_guard, bool& is_unique);

  static int is_columns_contain_primary_key(const ObIArray<uint64_t>& col_ids, uint64_t table_id,
      share::schema::ObSchemaGetterGuard& schema_guard, bool& is_unique);

  static int columns_has_unique_subset(const ObIArray<uint64_t>& full, const ObRowkeyInfo& sub, bool& is_subset);
};

class ObOptEstObjToScalar {
public:
  // Functionality: convert objs to scalars(double).
  // One Optimization : Limited precision of double type cannot distinguish long strs using
  // a static base of 256 (max distinguishable length is 6). We need to firstly truncate the
  // common headers, then use a dynamic base to convert strings.
  //
  // Choice of dynamic base : find min and max of all chars of all given strs beyond the already
  // found common prefix length, use (max - min) as base
  //
  // When to use this optimization : all given objs (2 to 4, min and max may be NULL) are string
  // or min or max.
  //
  // If this optimization is not used, use old method to convert objs.
  static int convert_objs_to_scalars(const common::ObObj* min, const common::ObObj* max, const common::ObObj* start,
      const common::ObObj* end, common::ObObj* min_out, common::ObObj* max_out, common::ObObj* start_out,
      common::ObObj* end_out);
  /////////////Start convert obj to scalar related function//////
  // @param obj
  // @return
  static double convert_obj_to_scalar(const common::ObObj* obj);

  // double type cannot represent min (max can be represented using Double.INF).
  // wrap conversion result as obj, so it can represent min, max, etc.
  static int convert_obj_to_scalar_obj(const common::ObObj* obj, common::ObObj* out);

  static int convert_obj_to_double(const common::ObObj* obj, double& num);

  static int convert_string_to_scalar_for_number(const common::ObString& str, double& scala);

private:
  static int add_to_string_conversion_array(
      const common::ObObj& strobj, common::ObIArray<common::ObString>& arr, uint64_t& convertable_map, int64_t pos);
  // 1, find common prefix length of strings
  // 2, find dynamix base and offset of strings
  // 3, use dynamic base and offset to convert strings to scalars
  static int convert_strings_to_scalar(
      const common::ObIArray<common::ObString>& origin_strs, common::ObIArray<double>& scalars);

  static double convert_string_to_scalar(
      const common::ObString& str, int64_t prefix_len = 0, uint8_t offset = 0, double base = 256.0);

  static int find_common_prefix_len(const common::ObIArray<common::ObString>& strs, int64_t& length);
  static inline void expand_range(uint8_t& min, uint8_t& max, uint8_t rmin, uint8_t rmax)
  {
    if (rmin < min) {
      min = rmin;
    }
    if (rmax > max) {
      max = rmax;
    }
  }

  static int find_string_scalar_offset_base(
      const common::ObIArray<common::ObString>& strs, int64_t prefix_len, uint8_t& offset, double& base);
};
}  // namespace sql
}  // namespace oceanbase
#endif
