#pragma once

#include <cstddef>
#include <string>
#include <vector>

#define PREFER_MY_CONFIG_H
#include <mariadb.h>
#include <mysql.h>
#include <my_sys.h>
// #include <json_lib.h>

#include "collation.h"
#include "functor_json.h"
#include "functor_str.h"
#include "collation.h"
#include "rowgroup.h"
#include "treenode.h"
#include "functioncolumn.h"
#include "constantcolumn.h"

#include "json_lib.h"

namespace funcexp::helpers
{

static const int NO_WILDCARD_ALLOWED = 1;

/*
  Checks if the path has '.*' '[*]' or '**' constructions
  and sets the NO_WILDCARD_ALLOWED error if the case.
*/
int setupJSPath(json_path_t* path, CHARSET_INFO* cs, const std::string_view& str, bool wildcards);

// Return true if err occur, let the outer function handle the exception
bool appendEscapedJS(std::string& ret, const CHARSET_INFO* retCS, const utils::NullString& js,
                     const CHARSET_INFO* jsCS);
bool appendJSKeyName(std::string& ret, const CHARSET_INFO* retCS, rowgroup::Row& row, execplan::SPTP& parm);
bool appendJSValue(std::string& ret, const CHARSET_INFO* retCS, rowgroup::Row& row, execplan::SPTP& parm);

static const int TAB_SIZE_LIMIT = 8;
static const char tab_arr[TAB_SIZE_LIMIT + 1] = "        ";

// Format the json using format mode
int doFormat(json_engine_t* je, std::string& niceJS, Func_json_format::FORMATS mode, int tabSize = 4);

static const int SHOULD_END_WITH_ARRAY = 2;
static const int TRIVIAL_PATH_NOT_ALLOWED = 3;

bool findKeyInObject(json_engine_t* jsEg, json_string_t* key);

#if MYSQL_VERSION_ID >= 100900
using IntType = int;
#else
using IntType = uint;
#endif

/*
  Compatible with json_find_path function in json_lib
  before 10.9: uint* array_counters
  after 10.9: int* array_counters
 */
inline static int locateJSPath(json_engine_t& jsEg, JSONPath& path, int* jsErr = nullptr)
{
  IntType arrayCounters[JSON_DEPTH_LIMIT];

#if MYSQL_VERSION_ID >= 120200
  MEM_ROOT_DYNAMIC_ARRAY array;

  initJsonArray(NULL, &array, sizeof(int), &arrayCounters, MY_INIT_BUFFER_USED | MY_BUFFER_NO_RESIZE);

  path.currStep = reinterpret_cast<json_path_step_t*>(path.p.steps.buffer);
  if (json_find_path(&jsEg, &path.p, &path.currStep, &array))
#else
  path.currStep = path.p.steps;
  if (json_find_path(&jsEg, &path.p, &path.currStep, arrayCounters))
#endif
  {
    if (jsErr && jsEg.s.error)
      *jsErr = 1;
    return 1;
  }
  return 0;
}

// Check and set the constant flag from function parameters
inline static void markConstFlag(JSONPath& path, const execplan::SPTP& parm)
{
  path.constant = (dynamic_cast<execplan::ConstantColumn*>(parm->data()) != nullptr);
}

int cmpJSPath(const json_path_t* a, const json_path_t* b, enum json_value_types vt,
              const int* arraySize = nullptr);

inline const CHARSET_INFO* getCharset(execplan::SPTP& parm)
{
  return parm->data()->resultType().getCharset();
}

inline void initJSEngine(json_engine_t& jsEg, const CHARSET_INFO* jsCS, const utils::NullString& js)
{
  json_scan_start(&jsEg, jsCS, (const uchar*)js.str(), (const uchar*)js.end());
}

int parseJSPath(JSONPath& path, rowgroup::Row& row, execplan::SPTP& parm, bool wildcards = true);

bool matchJSPath(const std::vector<funcexp::JSONPath>& paths, const json_path_t* p, json_value_types valType,
                 const int* arrayCounter = nullptr, bool exact = true);
}  // namespace funcexp::helpers
