/*
   Copyright (c) 2000, 2018, Oracle and/or its affiliates.
   Copyright (c) 2009, 2019, MariaDB Corporation.
   Copyright (c) 2021 OceanBase.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */

#ifndef _ob_full_link_trace_h
#define _ob_full_link_trace_h

#include <stdint.h>
#include "mysql.h"
#include "ma_list.h"
#include "ob_thread_key.h"

// #define DEBUG_OB20

#define UUID4_LEN 37
#define UUID4_SERIALIZE_LEN 16

#define FLT_DECLARE                                       \
  Ob20Protocol *ob20protocol = mysql->net.ob20protocol;   \
  FLTInfo *flt = NULL;                                    \
  ObSpanCtx *span;                                        \
  ObTrace *trace;                                         \
  char ip_buffer[50] = {0};                               \
  char host_buffer[50] = {0};                             \
  int port = 0

#define FLT_BEFORE_COMMAND(span_level, tag_level, tag_str)              \
  do                                                                    \
  {                                                                     \
    if (get_use_full_link_trace(mysql) && OB_NOT_NULL(ob20protocol)) {  \
      flt = ob20protocol->flt;                                          \
      if (OB_NOT_NULL(flt)) {                                           \
        if ((TRUE == flt->control_info_.flt_show_trace_enable_)         \
          || !(mysql->server_status & SERVER_STATUS_IN_TRANS)) {        \
          BEGIN_TRACE(flt);                                             \
        } else {                                                        \
          /* A reset is required before each begin in the current transaction to prevent insufficient span*/           \
          reset_span(flt->trace_);                                      \
        }                                                               \
        span = BEGIN_SPAN(flt, span_level);                             \
        SET_TAG(flt, tag_level, tag_str);                               \
        if (NULL != mysql && get_local_ip_port(mysql->net.fd, ip_buffer, 50, &port)) {     \
          snprintf(host_buffer, 50, "\"%s:%d\"", ip_buffer, port);      \
          SET_TAG(flt, FLT_TAG_CLIENT_HOST, host_buffer);               \
        }                                                               \
        if (flt_build_request(mysql, flt)) {                            \
          /* error , but do nothing */                                  \
        }                                                               \
      }                                                                 \
    }                                                                   \
  } while (0)

#define FLT_AFTER_COMMAND                                                       \
  do                                                                            \
  {                                                                             \
    ob20protocol = mysql->net.ob20protocol;                                     \
    if (get_use_full_link_trace(mysql) && OB_NOT_NULL(ob20protocol)) {          \
      flt = ob20protocol->flt;                                                  \
      trace = OBTRACE(flt);                                                     \
      if (NULL != trace) {                                                      \
        END_SPAN(flt, span);                                                    \
        if (TRUE == trace->force_print_) {                                      \
          FLUSH_TRACE(flt);                                                     \
        } else if (TRUE == trace->slow_query_print_) {                          \
          trace->slow_query_print_ = FALSE;   /* Slow query is set to FALSE for each request */ \
          flush_first_span(trace);                                              \
        }                                                                       \
        if ((TRUE == flt->control_info_.flt_show_trace_enable_)                 \
          || !(mysql->server_status & SERVER_STATUS_IN_TRANS)) {                \
          END_TRACE(flt);                                                       \
        }                                                                       \
      }                                                                         \
    }                                                                           \
  } while (0)

#define DEFINE_FLT_SERIALIZE_FUNC(type)                                                                       \
  int flt_serialize_##type(char *buf, const int64_t len, int64_t *pos, void *flt_info);                       \
  int flt_get_serialize_size_##type(int32_t *size, void *flt_info);                                           \
  int flt_deserialize_field_##type(FullLinkTraceExtraInfoId extra_id, const int64_t v_len,                    \
                                          const char *buf, const int64_t len, int64_t *pos, void *flt_info)

#define FLT_SERIALIZE_FUNC(type)       \
  {                                    \
    flt_deserialize_field_##type,      \
    flt_serialize_##type,              \
    flt_get_serialize_size_##type      \
  }

#define DEFINE_TO_STRING_FUNC_FOR(type) \
  int tostring_##type(char *buf, const int64_t buf_len, int64_t *pos, type *src)

// Currently maintains a size of 5k for each trace, which is the size of all space allocated during the entire trace
#define OBTRACE_DEFAULT_BUFFER_SIZE (5 * (1L << 10))
// The current driver free span is 4
#define SPAN_CACHE_COUNT (1L << 2)
// The current driver free span is 4
#define TAG_CACHE_COUNT (1L << 4)
// Since there is currently at most one SPAN, the largest LOG buffer is 1k
#define MAX_TRACE_LOG_SIZE (1L << 10)
// The current maximum size of the full link after serialization is 2k
#define MAX_FLT_SERIALIZE_SIZE (1L << 11)
// span cache begin buffer
#define TAG_BUFFER_BEGIN ((2 * MAX_TRACE_LOG_SIZE) + MAX_FLT_SERIALIZE_SIZE)
#define SPAN_BUFFER_BEGIN ((2 * MAX_TRACE_LOG_SIZE) + MAX_FLT_SERIALIZE_SIZE + TAG_CACHE_COUNT * (sizeof(ObTagCtx)))
#define INIT_OFFSET (SPAN_BUFFER_BEGIN + SPAN_CACHE_COUNT * (sizeof(LIST) + sizeof(ObSpanCtx)))

#define OBTRACE(flt) get_trace_instance(flt)
#define BEGIN_TRACE(flt) (begin_trace(OBTRACE(flt)))
#define END_TRACE(flt)         \
  do                           \
  {                            \
    end_trace(OBTRACE(flt));   \
  } while (0)
#define BEGIN_SPAN(flt, span_type) BEGIN_CHILD_SPAN(flt, span_type)
#define BEGIN_CHILD_SPAN(flt, span_type) (begin_span(OBTRACE(flt), span_type, 1, FALSE))
#define BEGIN_FOLLOW_SPAN(flt, span_type) (begin_span(OBTRACE(flt), span_type, 1, TRUE))
#define END_SPAN(flt, span_id)             \
  do                                  \
  {                                   \
    ObTrace *trace = OBTRACE(flt);    \
    (end_span(trace, span_id));       \
  } while (0)
#define SET_TAG(flt, tag_type, value)                   \
  do                                                    \
  {                                                     \
    ObSpanCtx *span = OBTRACE(flt)->last_active_span_;  \
    if (OB_NOT_NULL(span)) {                            \
      append_tag(OBTRACE(flt), span, tag_type, value);  \
    }                                                   \
  } while (0)
#define SET_TRACE_LEVEL(trace, level) (trace->level_ = level)
#define SET_AUTO_FLUSH(trace, value) (trace->auto_flush_ = value)
#define FLUSH_TRACE(flt)       \
  do                           \
  {                            \
    flush_trace(OBTRACE(flt)); \
  } while (0)


typedef struct st_obtrace ObTrace;
/*
0 ～ 999 for obclient
1000～1999 for obproxy
2000～65535 for all
*/
typedef enum enum_fulllinktraceextrainfoid
{
  FLT_DRIVER_SPAN = 1,
  FLT_DRIVER_END = 1000,
  // APP_INFO
  FLT_CLIENT_IDENTIFIER = 2001,
  FLT_MODULE,
  FLT_ACTION,
  FLT_CLIENT_INFO,
  FLT_APPINFO_TYPE,
  // QUERY_INFO
  FLT_QUERY_START_TIMESTAMP = 2010,
  FLT_QUERY_END_TIMESTAMP,
  // CONTROL_INFO
  FLT_LEVEL = 2020,
  FLT_SAMPLE_PERCENTAGE,
  FLT_RECORD_POLICY,
  FLT_PRINT_SAMPLE_PCT,
  FLT_SLOW_QUERY_THRES,
  FLT_SHOW_TRACE_ENABLE,
  // SPAN_INFO
  FLT_TRACE_ENABLE = 2030,
  FLT_FORCE_PRINT,
  FLT_TRACE_ID,
  FLT_REF_TYPE,
  FLT_SPAN_ID,
  // FLT_SHOW_TRACE
  FLT_DRV_SHOW_TRACE_SPAN = 2050,
  FLT_PROXY_SHOW_TRACE_SPAN,
  FLT_EXTRA_INFO_ID_END
} FullLinkTraceExtraInfoId;

/*
0 ～ 999 for obclient
1000～1999 for obproxy
2000～65535 for all
*/
typedef enum enum_fulllinktraceextrainfotype
{
  FLT_DRIVER_SPAN_INFO = 1,
  FLT_EXTRA_INFO_DRIVE_END = 1000,

  FLT_APP_INFO = 2001,
  FLT_QUERY_INFO,
  FLT_CONTROL_INFO,
  FLT_SPAN_INFO,
  FLT_TYPE_SHOW_TRACE_SPAN,

  FLT_EXTRA_INFO_TYPE_END
} FullLinkTraceExtraInfoType;

typedef enum enum_recordpolicy {
  RP_ALL = 1,
  RP_ONLY_SLOW_QUERY,
  RP_SAMPLE_AND_SLOW_QUERY,
  MAX_RECORD_POLICY
} RecordPolicy;

typedef struct st_fltinfobase
{
  FullLinkTraceExtraInfoType type_;
} FLTInfoBase;

typedef struct st_fltcontrolinfo
{
  FullLinkTraceExtraInfoType type_;
  int8_t level_;
  double sample_pct_;
  RecordPolicy rp_;
  double print_sample_pct_;
  int64_t slow_query_threshold_;

  int8_t  flt_show_trace_enable_;									// enable show trace or not
} FLTControlInfo;

typedef struct st_fltappinfo
{
  FullLinkTraceExtraInfoType type_;
  const char *identifier_;
  const char *module_;
  const char *action_;
  const char *client_info_;
} FLTAppInfo;

typedef struct st_fltqueryinfo
{
  FullLinkTraceExtraInfoType type_;
  int64_t query_start_timestamp_;
  int64_t query_end_timestamp_;
} FLTQueryInfo;

typedef struct st_fltspaninfo
{
  FullLinkTraceExtraInfoType type_;
  int8_t trace_enable_;
  int8_t force_print_;
  const char *trace_id_;
  int8_t ref_type_;
  const char *span_id_;
} FLTSpanInfo;

typedef struct st_fltdriverspaninfo
{
  FullLinkTraceExtraInfoType type_;
  const char *client_span_;
} FLTDriverSpanInfo;

typedef struct st_showtracespan
{
  FullLinkTraceExtraInfoType type_;
  const char *client_span_json_;
} FLTShowTraceSpan;

typedef struct st_fltvaluedata
{
  void *value_data_;
  size_t length;
} FLTValueData;

typedef struct st_fltinfo
{
  FLTShowTraceSpan show_trace_span_;
  FLTDriverSpanInfo client_span_;
  FLTControlInfo control_info_;
  FLTAppInfo app_info_;
  FLTQueryInfo query_info_;
  FLTSpanInfo span_info_;

  FLTValueData flt_value_data_;   // for set extra info

  my_bool in_trans_;
  char trace_id_[UUID4_SERIALIZE_LEN];
  char span_id_[UUID4_SERIALIZE_LEN];
  ObTrace *trace_;
} FLTInfo;

typedef int (*flt_deserialize_field_func)(FullLinkTraceExtraInfoId extra_id, const int64_t v_len,
                                        const char *buf, const int64_t len, int64_t *pos, void *flt_info);
typedef int (*flt_serialize_func)(char *buf, const int64_t len, int64_t *pos, void *flt_info);
typedef int (*flt_get_serialize_size_func)(int32_t *size, void *flt_info);

typedef struct st_fltfunc
{
  flt_deserialize_field_func deserialize_field_func;
  flt_serialize_func serialize_func;
  flt_get_serialize_size_func get_serialize_size_func;
} FLTFunc;

typedef struct st_obclient_uuid
{
  union {
    struct {
      uint64_t low_;
      uint64_t high_;
    };
    struct {
      uint32_t time_low;
      uint16_t time_mid;
      uint16_t time_hi_and_version;
      uint8_t clock_seq_hi_and_reserved;
      uint8_t clock_seq_low;
      uint8_t node[6];
    };
  };
} OBCLIENT_UUID;
DEFINE_TO_STRING_FUNC_FOR(OBCLIENT_UUID);

enum enum_flt_tagtype{
  FLT_TAG_COMMAND_NAME = 0,
  FLT_TAG_CLIENT_HOST = 1,
  FLT_TAG_MAX_TYPE
};

typedef struct st_obtagctx ObTagCtx;
struct st_obtagctx
{
  ObTagCtx *next_;
  uint16_t tag_type_;
  const char *data_;
};
DEFINE_TO_STRING_FUNC_FOR(ObTagCtx);

typedef struct st_obspanctx
{
  uint16_t span_type_;
  OBCLIENT_UUID span_id_;
  struct st_obspanctx *source_span_;
  my_bool is_follow_;
  int64_t start_ts_;
  int64_t end_ts_;
  ObTagCtx *tags_;
} ObSpanCtx;
DEFINE_TO_STRING_FUNC_FOR(ObSpanCtx);

struct st_obtrace
{
  uint64_t buffer_size_;
  uint64_t offset_;
  uint64_t seq_;
  my_bool in_trans_;
  my_bool trace_enable_;
  my_bool show_trace_enable_;
  my_bool force_print_;
  my_bool slow_query_print_;
  FLTInfo *flt;                 // point to flt struct
  uint64_t uuid_random_seed[2];
  OBCLIENT_UUID trace_id_;
  OBCLIENT_UUID root_span_id_;
  LIST *current_span_list_;
  LIST *free_span_list_;
  ObSpanCtx *last_active_span_;
  ObTagCtx free_tag_list_;
  union {
    uint8_t policy_;
    struct {
      uint8_t level_ : 7;
      uint8_t auto_flush_ : 1;
    };
  };
  uint64_t log_buf_offset_;
  char *log_buf_;   // size is MAX_TRACE_LOG_SIZE
  uint64_t show_trace_buf_offset_;
  char *show_trace_buf_;   // size is MAX_TRACE_LOG_SIZE
  char *flt_serialize_buf_;  // size is MAX_FLT_SERIALIZE_SIZE
  char data_[0];   // Multi-allocated space to store free span and facilitate subsequent tagging
};

int trace_init();
void trace_end();
void begin_trace(ObTrace *trace);
void end_trace(ObTrace *trace);
ObSpanCtx* begin_span(ObTrace *trace, uint32_t span_type, uint8_t level, my_bool is_follow);
void end_span(ObTrace *trace, ObSpanCtx *span);
void reset_span(ObTrace *trace);
void append_tag(ObTrace *trace, ObSpanCtx *span, uint16_t tag_type, const char *str);
void reset_tag(ObTrace *trace, ObSpanCtx *span, ObTagCtx *tag);
ObTrace *get_trace_instance(FLTInfo *flt);
void flush_first_span(ObTrace *trace);
void flush_trace(ObTrace *trace);

DEFINE_FLT_SERIALIZE_FUNC(appinfo);         // FLT_APP_INFO
DEFINE_FLT_SERIALIZE_FUNC(queryinfo);       // FLT_QUERY_INFO
DEFINE_FLT_SERIALIZE_FUNC(controlinfo);     // FLT_CONTROL_INFO
DEFINE_FLT_SERIALIZE_FUNC(spaninfo);        // FLT_SPAN_INFO
DEFINE_FLT_SERIALIZE_FUNC(driverspaninfo);  // FLT_DRIVER_SPAN_INFO
DEFINE_FLT_SERIALIZE_FUNC(showtracespan);   // FLT_TYPE_SHOW_TRACE_SPAN
DEFINE_FLT_SERIALIZE_FUNC(nosupport);       // FLT_EXTRA_INFO_TYPE_END

my_bool flt_is_vaild(FLTInfo *flt);

int serialize_UUID(char *buf, const int64_t buf_len, int64_t* pos, OBCLIENT_UUID *uuid);
int deserialize_UUID(const char *buf, const int64_t buf_len, int64_t *pos, OBCLIENT_UUID *uuid);

int uuid4_init(uint64_t *seed, size_t seed_size);
OBCLIENT_UUID uuid4_generate(uint64_t *seed);

int flt_init(FLTInfo *flt);
void flt_end(FLTInfo *flt);
int flt_build_request(MYSQL *mysql, FLTInfo *flt);
void flt_set_send_trans_flag(FLTInfo *flt, my_bool send_flag);

int flt_deserialize_extra_info(const char *buf, const int64_t len, int64_t *pos, FullLinkTraceExtraInfoType type, void *flt_info);
int flt_serialize_extra_info(char *buf, const int64_t len, int64_t *pos, void *flt_info);
int flt_get_serialize_size_extra_info(int32_t *size, void *flt_info);

// for encode
int flt_store_type_and_len(char *buf, int64_t len, int64_t *pos, int16_t type, int32_t v_len);
int flt_store_str(char *buf, int64_t len, int64_t *pos, const char *str, const uint64_t str_len, int16_t type);
      //@{Serialize integer data, save the data in v to the position of buf+pos, and update pos
int flt_store_int1(char *buf, int64_t len, int64_t *pos, int8_t v, int16_t type);
int flt_store_int2(char *buf, int64_t len, int64_t *pos, int16_t v, int16_t type);
int flt_store_int3(char *buf, int64_t len, int64_t *pos, int32_t v, int16_t type);
int flt_store_int4(char *buf, int64_t len, int64_t *pos, int32_t v, int16_t type);
int flt_store_int5(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type);
int flt_store_int6(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type);
int flt_store_int8(char *buf, int64_t len, int64_t *pos, int64_t v, int16_t type);
int flt_store_double(char *buf, const int64_t len, int64_t *pos, double val, int16_t type);
int flt_store_float(char *buf, const int64_t len, int64_t *pos, float val, int16_t type);

int flt_get_store_str_size(const uint64_t str_len);
int flt_get_store_int1_size();
int flt_get_store_int2_size();
int flt_get_store_int3_size();
int flt_get_store_int4_size();
int flt_get_store_int5_size();
int flt_get_store_int6_size();
int flt_get_store_int8_size();
int flt_get_store_double_size();
int flt_get_store_float_size();

// for decode
int flt_resolve_type_and_len(const char *buf, int64_t len, int64_t *pos, int16_t *type, int32_t *v_len);
int flt_get_str(const char *buf, int64_t len, int64_t *pos, int64_t str_len, char **str);
//@{ Signed integer in reverse sequence, write the result to v, and update pos
int flt_get_int1(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int8_t *val);
int flt_get_int2(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int16_t *val);
int flt_get_int3(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int32_t *val);
int flt_get_int4(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int32_t *val);
int flt_get_int8(const char *buf, int64_t len, int64_t *pos, int64_t v_len, int64_t *val);
int flt_get_double(const char *buf, int64_t len, int64_t *pos, int64_t v_len, double *val);
int flt_get_float(const char *buf, int64_t len, int64_t *pos, int64_t v_len, float *val);
#endif