/**
 * Copyright (c) 2021 OceanBase
 * OceanBase Database Proxy(ODP) 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 OB_FUNC_EXPR_PROXY_EXPR_H
#define OB_FUNC_EXPR_PROXY_EXPR_H

#include "opsql/func_expr_resolver/proxy_expr/ob_proxy_expr_type.h"
#include "dbconfig/ob_proxy_db_config_info.h"
#include "lib/allocator/ob_mod_define.h"
#include "lib/ob_define.h"

namespace oceanbase
{
namespace obproxy
{

namespace opsql
{

class ObProxyExprCtx
{
public:
  explicit ObProxyExprCtx(const int64_t physical_size, dbconfig::ObTestLoadType type,
                          bool is_elastic_index, common::ObIAllocator* allocator)
      : sharding_physical_size_(physical_size), test_load_type_(type), is_elastic_index_(is_elastic_index),
        allocator_(allocator), scale_(-1) {}
  ~ObProxyExprCtx() {}

  void set_sharding_physical_size(const int64_t physical_size) { sharding_physical_size_ = physical_size; }
  void set_test_load_type(const dbconfig::ObTestLoadType type) { test_load_type_ = type; }
  void set_is_elastic_index(const bool is_elastic_index) { is_elastic_index_ = is_elastic_index; }
  void set_scale(const int64_t scale) { scale_ = scale; };

public:
  int64_t sharding_physical_size_;
  dbconfig::ObTestLoadType test_load_type_;
  bool is_elastic_index_;
  common::ObIAllocator *allocator_;
  int64_t scale_;
};

struct ObProxyExprCalcItem {
  enum ObProxyExprCalcItemSource {
    FROM_SQL_FIELD,
    FROM_OBJ_ARRAY,
    FROM_INVALID
  };

  ObProxyExprCalcItem() : sql_result_(NULL), obj_array_(NULL), source_(FROM_SQL_FIELD) {}

  ObProxyExprCalcItem(obutils::SqlFieldResult *sql_result) :
      sql_result_(sql_result), obj_array_(NULL), source_(FROM_SQL_FIELD) {}

  ObProxyExprCalcItem(common::ObIArray<common::ObObj*> *obj_array) :
      sql_result_(NULL), obj_array_(obj_array), source_(FROM_OBJ_ARRAY) {}

  obutils::SqlFieldResult *sql_result_;
  common::ObIArray<common::ObObj*> *obj_array_;
  ObProxyExprCalcItemSource source_;
};

// Attention!! all var in expr NOT modify after init
class ObProxyExpr
{
public:
  explicit ObProxyExpr() : type_(OB_PROXY_EXPR_TYPE_NONE), index_(-1), accuracy_(-1), has_agg_(0),
                           is_func_expr_(0), reserved_(0), alias_name_() {}

  ~ObProxyExpr() {}
  void set_expr_type(const ObProxyExprType type) { type_ = type; }
  ObProxyExprType get_expr_type() const { return type_; }
  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj> &result_obj_array);
  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj*> &result_obj_array);

  bool has_agg() const { return has_agg_ == 1; }
  void set_index(int64_t index) { index_ = index; }
  int64_t get_index() const { return index_; }
  void set_accuracy(common::ObAccuracy accuracy) { accuracy_ = accuracy; }
  common::ObAccuracy get_accuracy() const { return accuracy_; }
  bool is_func_expr() const { return is_func_expr_ == 1; }
  bool is_star_expr() { return OB_PROXY_EXPR_TYPE_STAR == type_; }

  int64_t to_string(char *buf, int64_t buf_len) const;

  virtual int to_sql_string(common::ObSqlString& sql_string);
  virtual int to_column_string(common::ObSqlString& sql_string) {
    return sql_string.append(expr_name_);
  }
  static  void print_proxy_expr(ObProxyExpr *root);
  void set_alias_name(const char *buf, const int64_t length) {
    alias_name_.assign_ptr(buf, static_cast<ObString::obstr_size_t>(length));
  }
  void set_alias_name(const ObString &alias_name) { alias_name_ = alias_name; }
  ObString& get_alias_name() { return alias_name_; }
  void set_expr_name(const char *buf, const int64_t length) {
    expr_name_.assign_ptr(buf, static_cast<ObString::obstr_size_t>(length));
  }
  ObString& get_expr_name() { return expr_name_; }

  bool is_agg()
  {
    return OB_PROXY_EXPR_TYPE_FUNC_SUM == type_
    || OB_PROXY_EXPR_TYPE_FUNC_COUNT == type_
    || OB_PROXY_EXPR_TYPE_FUNC_MAX == type_
    || OB_PROXY_EXPR_TYPE_FUNC_MIN == type_
    || OB_PROXY_EXPR_TYPE_FUNC_AVG == type_;
  }

public:
  ObProxyExprType type_;
  int64_t index_;
  common::ObAccuracy accuracy_;
  struct {
    uint16_t has_agg_ : 1;// whethere hava agg func, 1 means true
    uint16_t is_func_expr_ : 1;
    uint16_t reserved_ : 14;
  };
  common::ObString expr_name_;
  common::ObString alias_name_;
};

class ObProxyExprConst : public ObProxyExpr
{
public:
  explicit ObProxyExprConst() : obj_() {}
  virtual ~ObProxyExprConst() {}
  common::ObObj& get_object() { return obj_; }
  void set_object(const common::ObObj& obj) { obj_ = obj; }
  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
  virtual int to_column_string(common::ObSqlString& sql_string);

protected:
  common::ObObj obj_;
};

// add for save sharding const column
class ObProxyExprShardingConst : public ObProxyExpr
{
public:
  explicit ObProxyExprShardingConst() {}
  virtual ~ObProxyExprShardingConst() {}
};

class ObProxyExprTable : public ObProxyExpr
{
public:
  explicit ObProxyExprTable() : database_name_(), table_name_() {}
  ~ObProxyExprTable() {}

  void set_database_name(const char *buf, const int32_t length) { database_name_.assign_ptr(buf, length); }
  ObString& get_database_name() { return database_name_; }
  void set_table_name(const char *buf, const int32_t length) { table_name_.assign_ptr(buf, length); }
  ObString& get_table_name() { return table_name_; }
private:
  common::ObString database_name_;
  common::ObString table_name_;
};

class ObProxyExprColumn : public ObProxyExpr
{
public:
  explicit ObProxyExprColumn() : real_table_name_(), table_name_(), column_name_() {}
  ~ObProxyExprColumn() {}

  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
  virtual int to_column_string(common::ObSqlString& sql_string);
  void set_column_name(const char *buf, const int32_t length) { column_name_.assign_ptr(buf, length); }
  ObString &get_column_name() { return column_name_; }
  void set_table_name(const char *buf, const int32_t length) { table_name_.assign_ptr(buf, length); }
  ObString &get_table_name() { return table_name_; }
  void set_real_table_name(const ObString &real_table_name) { real_table_name_ = real_table_name; }
  ObString &get_real_table_name() { return real_table_name_; }
private:
  common::ObString real_table_name_;
  common::ObString table_name_;
  common::ObString column_name_;
};

class ObProxyExprStar : public ObProxyExpr
{
public:
  explicit ObProxyExprStar() : table_name_() {}
  ~ObProxyExprStar() {}

  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj> &result_obj_array) {
    UNUSED(ctx);
    UNUSED(calc_item);
    UNUSED(result_obj_array);
    return OB_SUCCESS;
  };
  void set_table_name(const char *buf, const int32_t length) { table_name_.assign_ptr(buf, length); }
  ObString &get_table_name() { return table_name_; }
private:
  common::ObString table_name_;
};

enum ObProxyOrderDirection
{
  NULLS_FIRST_ASC = 0,   // Forward, NULLs first
  NULLS_LAST_ASC, // Forward, NULLs last
  NULLS_FIRST_DESC, // Backward, NULLs first
  NULLS_LAST_DESC,   // Backward, NULLs last
  UNORDERED, // not ordered
  MAX_DIR, // invalid
};

class ObProxyGroupItem : public ObProxyExpr
{
public:
  explicit ObProxyGroupItem() : expr_(NULL) {}
  virtual ~ObProxyGroupItem() {
    if (NULL != expr_) {
      expr_->~ObProxyExpr();
    }
  }
  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj> &result_obj_array);
  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj*> &result_obj_array);
  virtual int to_column_string(common::ObSqlString& sql_string);
  void set_expr(ObProxyExpr* expr) { expr_ = expr; }
  ObProxyExpr* get_expr() { return expr_; }
public:
  ObProxyExpr* expr_;
};

class ObProxyOrderItem : public ObProxyGroupItem
{
public:
  explicit ObProxyOrderItem() : order_direction_(NULLS_FIRST_ASC) {}
  virtual ~ObProxyOrderItem() {}
  virtual int to_sql_string(common::ObSqlString& sql_string);
public:
  ObProxyOrderDirection order_direction_;
};

class ObProxyFuncExpr : public ObProxyExpr
{
public:
  explicit ObProxyFuncExpr() : param_array_(common::ObModIds::OB_PROXY_SHARDING_EXPR, common::OB_MALLOC_NORMAL_BLOCK_SIZE) { is_func_expr_ = true; }
  virtual ~ObProxyFuncExpr();

  int add_param_expr(ObProxyExpr* expr) { return param_array_.push_back(expr); }
  int calc_param_expr(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                      common::ObSEArray<common::ObSEArray<common::ObObj, 4>, 4> &param_result,
                      int &cnt);

  common::ObSEArray<ObProxyExpr*, 4>& get_param_array() { return param_array_; }
  void set_param_array(common::ObSEArray<ObProxyExpr*, 4>& param_array) { param_array_ = param_array; }

public:
  static int get_int_obj(const common::ObObj &src, common::ObObj &dst);
  static int get_varchar_obj(const common::ObObj &src, common::ObObj &dst, common::ObIAllocator &allocator);
  int check_varchar_empty(const common::ObObj& result);

protected:
  common::ObSEArray<ObProxyExpr*, 4> param_array_;
};

class ObProxyExprHash : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprHash() {}
  ~ObProxyExprHash() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprSubStr : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprSubStr() {}
  ~ObProxyExprSubStr() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprConcat : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprConcat() {}
  ~ObProxyExprConcat() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprToInt : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprToInt() {}
  ~ObProxyExprToInt() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprDiv : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprDiv() {}
  ~ObProxyExprDiv() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprAdd : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprAdd() {}
  ~ObProxyExprAdd() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprSub : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprSub() {}
  ~ObProxyExprSub() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprMul : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprMul() {}
  ~ObProxyExprMul() {}
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
            common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprTestLoad : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprTestLoad() {}
  ~ObProxyExprTestLoad() {}

  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj> &result_obj_array);
};

class ObProxyExprSum : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprSum() {}
  ~ObProxyExprSum() {}
};

class ObProxyExprCount : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprCount() {}
  ~ObProxyExprCount() {}
};

class ObProxyExprAvg : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprAvg() : sum_expr_(NULL), count_expr_(NULL) {}
  ~ObProxyExprAvg() {
    if (NULL != sum_expr_) {
      sum_expr_->~ObProxyExprSum();
    }

    if (NULL != count_expr_) {
      count_expr_->~ObProxyExprCount();
    }
  }
  int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj> &result_obj_array);
  void set_sum_expr(ObProxyExprSum* sum_expr) { sum_expr_ = sum_expr; }
  ObProxyExprSum *get_sum_expr() { return sum_expr_; }
  void set_count_expr(ObProxyExprCount *count_expr) { count_expr_ = count_expr; }
  ObProxyExprCount *get_count_expr() { return count_expr_; }
private:
  ObProxyExprSum *sum_expr_;
  ObProxyExprCount *count_expr_;
};

class ObProxyExprMax : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprMax() {}
  ~ObProxyExprMax() {}
};

class ObProxyExprMin : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprMin() {}
  ~ObProxyExprMin() {}
};

class ObProxyExprSplit : public ObProxyFuncExpr
{
public:
  explicit ObProxyExprSplit() {}
  ~ObProxyExprSplit() {}

  virtual int calc(const ObProxyExprCtx &ctx, const ObProxyExprCalcItem &calc_item,
                   common::ObIArray<common::ObObj> &result_obj_array);
};

} // end opsql
} // end obproxy
} // end oceanbase

#endif
