// Copyright (C) 2016, 2017, 2018, 2019  Stefan Vargyas
// 
// This file is part of Json-Type.
// 
// Json-Type 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, either version 3 of the License, or
// (at your option) any later version.
// 
// Json-Type 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 Json-Type.  If not, see <http://www.gnu.org/licenses/>.

#ifndef __JSON_LITEX_H
#define __JSON_LITEX_H

#include <stdint.h>

#include "json.h"

#define JSON_LITEX_VERSION_MAJOR 0
#define JSON_LITEX_VERSION_MINOR 1
#define JSON_LITEX_VERSION_PATCH 1

#define JSON_LITEX_VERSION                 \
    (                                      \
        JSON_LITEX_VERSION_MAJOR * 10000 + \
        JSON_LITEX_VERSION_MINOR * 100 +   \
        JSON_LITEX_VERSION_PATCH           \
    )

enum json_litex_node_type_t
{
    json_litex_string_node_type,
    json_litex_object_node_type,
    json_litex_array_node_type,
};

struct json_litex_string_node_t
{
    const uchar_t* val;
    const uchar_t* delim;
};

struct json_litex_object_node_arg_t
{
    struct json_litex_string_node_t key;
    const struct json_litex_node_t* val;
    struct json_text_pos_t pos;
};

struct json_litex_object_node_t
{
    const struct json_litex_object_node_arg_t* args;
    size_t size;
};

struct json_litex_array_node_t
{
    const struct json_litex_node_t** args;
    size_t size;
};

struct json_litex_expr_def_t;
#define json_litex_string_attr_t json_litex_expr_def_t

struct json_litex_object_attr_t;
struct json_litex_array_attr_t;

struct json_litex_node_t
{
    enum json_litex_node_type_t type;
    union {
        struct json_litex_string_node_t string;
        struct json_litex_object_node_t object;
        struct json_litex_array_node_t  array;
    } node;
    union {
        const struct json_litex_string_attr_t* string;
        const struct json_litex_object_attr_t* object;
        const struct json_litex_array_attr_t*  array;
    } attr;
    const struct json_litex_object_node_arg_t* path;
    const struct json_litex_node_t* parent;
    struct json_text_pos_t pos;
};

// stev: the struct 'json_litex_object_attr_t' below
// has two modes of operation, one for each kind of
// path libraries: 'text' or 'sobj'; in case of text
// libraries, 'lookup' points always to the function
// 'json_litex_trie_lookup_name', and 'root' points
// to the root node of the associated trie tree; on
// the other hand, in the case of 'sobj' libraries,
// 'lookup' points to the trie lookup function that
// is associated to the encompassing node instance;
// this function was generated when the respective
// 'sobj' library was built; for this kind of path
// libraries, 'root' has no meaning, being NULL

struct json_litex_trie_node_t;

typedef const struct json_litex_node_t*
    (*json_litex_object_attr_lookup_func_t)(
        const struct json_litex_trie_node_t*,
        const uchar_t*);

struct json_litex_object_attr_t
{
    json_litex_object_attr_lookup_func_t lookup;
    const struct json_litex_trie_node_t* root;
};

struct json_litex_array_attr_t
{
    const struct json_litex_node_t* string;
    const struct json_litex_node_t* object;
};

typedef uint64_t json_litex_expr_value_t;
typedef uint16_t json_litex_expr_rex_index_t;
typedef const uchar_t* json_litex_expr_string_t;

enum json_litex_expr_cmp_op_t
{
    json_litex_expr_cmp_op_eq,
    json_litex_expr_cmp_op_ne,
    json_litex_expr_cmp_op_lt,
    json_litex_expr_cmp_op_le,
    json_litex_expr_cmp_op_gt,
    json_litex_expr_cmp_op_ge,
};

enum json_litex_expr_rex_opt_t
{
    json_litex_expr_rex_opt_ignore_case,
    json_litex_expr_rex_opt_dot_match_all,
    json_litex_expr_rex_opt_no_utf8_check,
    json_litex_expr_rex_opt_extended_pat,
};

struct json_litex_expr_rex_t
{
    const uchar_t* text;
    size_t         opts;
};

struct json_litex_expr_builtin_t;

struct json_litex_expr_jump_t
{
    uint16_t offs;
    bool     popu;
};

typedef json_litex_expr_value_t
    json_litex_expr_num_node_t;
typedef json_litex_expr_string_t
    json_litex_expr_str_node_t;
typedef json_litex_expr_rex_index_t
    json_litex_expr_rex_node_t;
typedef const struct json_litex_expr_builtin_t*
    json_litex_expr_blt_node_t;
typedef struct json_litex_expr_jump_t
    json_litex_expr_jmp_node_t;
typedef enum json_litex_expr_cmp_op_t
    json_litex_expr_cmp_node_t;

enum json_litex_expr_node_type_t
{
    json_litex_expr_node_const_id,
    json_litex_expr_node_const_num,
    json_litex_expr_node_const_str,
    json_litex_expr_node_count_str,
    json_litex_expr_node_count_rex,
    json_litex_expr_node_match_str,
    json_litex_expr_node_match_rex,
    json_litex_expr_node_call_builtin,
    json_litex_expr_node_jump_false,
    json_litex_expr_node_jump_true,
    json_litex_expr_node_make_bool,
    json_litex_expr_node_cmp_op,
    json_litex_expr_node_not_op,
};

struct json_litex_expr_node_t
{
    enum json_litex_expr_node_type_t type;
    union {
        json_litex_expr_blt_node_t const_id;
        json_litex_expr_num_node_t const_num;
        json_litex_expr_str_node_t const_str;
        json_litex_expr_str_node_t count_str;
        json_litex_expr_rex_node_t count_rex;
        json_litex_expr_str_node_t match_str;
        json_litex_expr_rex_node_t match_rex;
        json_litex_expr_blt_node_t call_builtin;
        json_litex_expr_jmp_node_t jump_false;
        json_litex_expr_jmp_node_t jump_true;
        // ...                     make_bool;
        json_litex_expr_cmp_node_t cmp_op;
        // ...                     not_op;
    } node;
};

enum json_litex_expr_rex_codes_type_t
{
    json_litex_expr_rex_codes_patterns,
    json_litex_expr_rex_codes_streams
};

struct json_litex_expr_rex_patterns_t
{
#ifndef JSON_LITEX_MODULE
    pcre2_code const* const* objs;
#else
    void const* const* objs;
#endif
    size_t size;
};

struct json_litex_expr_rex_streams_t
{
    const uchar_t* bytes;
    size_t size;
};

struct json_litex_expr_rex_codes_t
{
    enum json_litex_expr_rex_codes_type_t type;
    union {
        struct json_litex_expr_rex_patterns_t
                                   patterns;
        struct json_litex_expr_rex_streams_t
                                   streams;
    } code;
};

// stev: note that the member 'elems' of the struct
// below serves no purpose other than letting the
// user see the regexes of a text path library in
// plain text form; 'codes' is the member that is
// used for evaluating the literal expressions of
// the library (being it of 'text' or 'sobj' kind)

struct json_litex_expr_rex_def_t
{
          struct json_litex_expr_rex_codes_t codes;
    const struct json_litex_expr_rex_t*      elems;
    size_t size;
};

struct json_litex_expr_def_t
{
    const struct json_litex_expr_node_t* nodes;
    size_t size;
};

struct json_litex_def_t
{
    const struct json_litex_expr_rex_def_t* rexes;
    const struct json_litex_node_t*         node;
};

// stev: declarations of each builtin implemented:

#ifdef JSON_LITEX_MODULE
#define JSON_LITEX_EXPR_BUILTIN_DECL(n)                     \
JSON_WEAK_API extern const struct json_litex_expr_builtin_t \
    json_litex_expr_ ## n ## _builtin
#else
#define JSON_LITEX_EXPR_BUILTIN_DECL(n)                \
JSON_API extern const struct json_litex_expr_builtin_t \
    json_litex_expr_ ## n ## _builtin
#endif

JSON_LITEX_EXPR_BUILTIN_DECL(null);
JSON_LITEX_EXPR_BUILTIN_DECL(boolean);
JSON_LITEX_EXPR_BUILTIN_DECL(number);
JSON_LITEX_EXPR_BUILTIN_DECL(string);

JSON_LITEX_EXPR_BUILTIN_DECL(len);
JSON_LITEX_EXPR_BUILTIN_DECL(int);
JSON_LITEX_EXPR_BUILTIN_DECL(uint);
JSON_LITEX_EXPR_BUILTIN_DECL(date);
JSON_LITEX_EXPR_BUILTIN_DECL(timestamp);
JSON_LITEX_EXPR_BUILTIN_DECL(long_iso);
JSON_LITEX_EXPR_BUILTIN_DECL(iso_8601);
JSON_LITEX_EXPR_BUILTIN_DECL(full_iso);
JSON_LITEX_EXPR_BUILTIN_DECL(c_locale);

// stev: do not forget to update the constant below
// when adding above a new builtin declaration!!!

enum { json_litex_expr_max_builtin_name_len = 9 };

#ifdef JSON_LITEX_MODULE

#define type(n) \
        type = json_litex_expr_node_ ## n

#define const_id(a)                                \
        { .type(const_id),                         \
          .node.const_id =                         \
              &json_litex_expr_ ## a ## _builtin }
#define const_num(a)                               \
        { .type(const_num),                        \
          .node.const_num = a }
#define const_str(a)                               \
        { .type(const_str),                        \
          .node.const_str = (const uchar_t*) a }
#define count_str(a)                               \
        { .type(count_str),                        \
          .node.count_str = (const uchar_t*) a }
#define count_rex(a)                               \
        { .type(count_rex),                        \
          .node.count_rex = a }
#define match_str(a)                               \
        { .type(match_str),                        \
          .node.match_str = (const uchar_t*) a }
#define match_rex(a)                               \
        { .type(match_rex),                        \
          .node.match_rex = a }
#define call_builtin(a)                            \
        { .type(call_builtin),                     \
          .node.call_builtin =                     \
              &json_litex_expr_ ## a ## _builtin }
#define jump_false(a, b)                           \
        { .type(jump_false),                       \
          .node.jump_false =                       \
             { .offs = a, .popu = b } }
#define jump_true(a, b)                            \
        { .type(jump_true),                        \
          .node.jump_true =                        \
             { .offs = a, .popu = b } }
#define make_bool()                                \
        { .type(make_bool) }
#define cmp_op(a)                                  \
        { .type(cmp_op),                           \
          .node.cmp_op =                           \
              json_litex_expr_cmp_op_ ## a }
#define not_op()                                   \
        { .type(not_op) }

#endif // JSON_LITEX_MODULE

#endif /* __JSON_LITEX_H */


