/*
** Copyright (c) 2011 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD 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.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** Internal definitions for XJD1
*/
#ifndef _XJD1INT_H
#define _XJD1INT_H

#include "xjd1.h"
#include "parse.h"
#include "sqlite3.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

/* Marker for routines not intended for external use */
#define PRIVATE

/* Additional tokens above and beyond those generated by the parser and
** found in parse.h
*/
#define TK_NOT_LIKEOP        (TK_LIKEOP+128)
#define TK_FUNCTION          100
#define TK_SPACE             101
#define TK_ILLEGAL           102
#define TK_CREATECOLLECTION  103
#define TK_DROPCOLLECTION    104
#define TK_ARRAY             105
#define TK_STRUCT            106
#define TK_JVALUE            107

/*
** A convenience macro for returning the size of an fixed-size array.
** For example:
**
**     DataType var[N];
**     assert( ArraySize(var)==N );
*/
#define ArraySize(X)    ((int)(sizeof(X)/sizeof(X[0])))

typedef unsigned char u8;
typedef unsigned short int u16;
typedef struct AggExpr AggExpr;
typedef struct Aggregate Aggregate;
typedef struct Command Command;
typedef struct DataSrc DataSrc;
typedef struct Expr Expr;
typedef struct ExprItem ExprItem;
typedef struct ExprList ExprList;
typedef struct FlattenIter FlattenIter;
typedef struct Function Function;
typedef struct JsonNode JsonNode;
typedef struct JsonStructElem JsonStructElem;
typedef struct Parse Parse;
typedef struct PoolChunk PoolChunk;
typedef struct Pool Pool;
typedef struct Query Query;
typedef struct String String;
typedef struct Token Token;
typedef struct ResultList ResultList;
typedef struct ResultItem ResultItem;

/* A single allocation from the Pool allocator */
struct PoolChunk {
  PoolChunk *pNext;                 /* Next chunk on list of them all */
};

/* A memory allocation pool */
struct Pool {
  PoolChunk *pChunk;                /* List of all memory allocations */
  char *pSpace;                     /* Space available for allocation */
  int nSpace;                       /* Bytes available in pSpace */
};

/* A variable length string */
struct String {
  Pool *pPool;                      /* Memory allocation pool or NULL */
  char *zBuf;                       /* String content */
  int nUsed;                        /* Slots used.  Not counting final 0 */
  int nAlloc;                       /* Space allocated */
};

/* Execution context */
struct xjd1_context {
  int nRef;                         /* Reference count */
  u8 isDying;                       /* True if has been deleted */
  int (*xLog)(const char*,void*);   /* Error logging function */
  void *pLogArg;                    /* 2nd argument to xLog() */
};

/* An open database connection */
struct xjd1 {
  xjd1_context *pContext;           /* Execution context */
  int nRef;                         /* Reference count */
  u8 isDying;                       /* True if has been closed */
  u8 parserTrace;                   /* True to enable parser tracing */
  u8 appendErr;                     /* append errMsg rather than overwrite */
  u8 isSQLite3Borrowed;             /* We'll not close this database as it is borrowed */
  xjd1_stmt *pStmt;                 /* list of all prepared statements */
  sqlite3 *db;                      /* Storage engine */
  int errCode;                      /* Latest non-zero error code */
  String errMsg;                    /* Latest error message */
};

/* A prepared statement */
struct xjd1_stmt {
  xjd1 *pConn;                      /* Database connection */
  xjd1_stmt *pNext, *pPrev;         /* List of all statements */
  Pool sPool;                       /* Memory pool used for parsing */
  int nRef;                         /* Reference count */
  u8 isDying;                       /* True if has been closed */
  char *zCode;                      /* Text of the query */
  Command *pCmd;                    /* Parsed command */
  JsonNode *pDoc;                   /* Current document */
  int okValue;                      /* True if retValue is valid */
  String retValue;                  /* String rendering of return value */

  int errCode;                      /* Error code */
  String errMsg;                    /* Error message */
};

/* A token into to the parser */
struct Token {
  const char *z;                    /* Text of the token */
  int n;                            /* Number of characters */
};

/* A single element of an expression list */
struct ExprItem {
  char *zAs;                /* AS value, or DESCENDING, or ASCENDING */
  Expr *pExpr;              /* The expression */
};

/* A list of expressions */
struct ExprList {
  int nEItem;               /* Number of items on the expression list */
  int nEAlloc;              /* Slots allocated in apEItem[] */
  ExprItem *apEItem;        /* The expression in the list */
};

/*
** A node of an expression.
**
** If (eClass==EXPR_TK), then this node is a reference to a data-source, or
** if the expression is part of a SELECT query, to the result of the query
** itself. If this expression is part of any type of statement other than a
** SELECT, then iDatasrc and pQuery are both set to 0. In that case there is
** only one data-source to which the expression could refer.
**
** If the expression is part of a SELECT and the iDatasrc field is set to
** 0, then the expression refers to the return value of the SELECT statement
** pQuery. pQuery may point to the simple SELECT that the expression is part
** of, or to some other SELECT statement if the expression is part of a
** correlated sub-select. If iDatasrc is set to N, where N is 1 or greater,
** it refers to the Nth data-source joined together in the FROM clause of
** pQuery, counting from left to right.
*/
struct Expr {
  u16 eType;                /* Expression node type */
  u16 eClass;               /* Expression class */
  Query *pQuery;            /* Query this expression belongs to.  May be NULL */
  xjd1_stmt *pStmt;         /* Statement this expression belongs to */
  union {
    struct {                /* Binary or unary operator. eClass==XJD1_EXPR_BI */
      Expr *pLeft;             /* Left operand.  Only operand for unary ops */
      Expr *pRight;            /* Right operand.  NULL for unary ops */
    } bi;
    struct {                /* Substructure nam.  eClass==EXPR_LVALUE */
      Expr *pLeft;             /* Lvalue or id to the left */
      char *zId;               /* ID to the right */
    } lvalue;
    struct {                /* Identifiers */
      char *zId;               /* token value.  eClass=EXPR_TK */
      int iDatasrc;
      Query *pQuery;
    } id;
    struct {                /* Function calls.  eClass=EXPR_FUNC */
      char *zFName;            /* Name of the function */
      ExprList *args;          /* List of arguments */
      Function *pFunction;     /* Function object */
      JsonNode **apArg;        /* Array to martial function arguments in */
      int iAgg;
    } func;
    struct {                /* Subqueries.  eClass=EXPR_Q */
      Query *p;                /* The subquery */
    } subq;
    struct {                /* Literal value.  eClass=EXPR_JSON */
      JsonNode *p;             /* The value */
    } json;
    ExprList *ar;           /* Array literal.  eClass=EXPR_ARRAY */
    ExprList *st;           /* Struct literal.  eClass=EXPR_STRUCT */
    struct {                /* Tertiary operator.  eClass==EXPR_TRI */
      Expr *pTest;          /* A in A?B:C */
      Expr *pIfTrue;        /* B in A?B:C */
      Expr *pIfFalse;       /* C in A?B:C */
    } tri;
  } u;
};
#define XJD1_EXPR_BI      1
#define XJD1_EXPR_TK      2
#define XJD1_EXPR_FUNC    3
#define XJD1_EXPR_Q       4
#define XJD1_EXPR_JSON    5
#define XJD1_EXPR_ARRAY   6
#define XJD1_EXPR_STRUCT  7
#define XJD1_EXPR_LVALUE  8
#define XJD1_EXPR_TRI     9

/* A single element of a JSON structure */
struct JsonStructElem {
  char *zLabel;             /* Label on this element */
  JsonStructElem *pNext;    /* Next element of the structure */
  JsonNode *pValue;         /* Value of this element */
};

/* A single element of a JSON value */
struct JsonNode {
  int eJType;               /* Element type */
  int nRef;                 /* Number of references */
  union {
    int b;                  /* Boolean value */
    double r;               /* Real value */
    char *z;                /* String value */
    struct {                /* Array value */
      int nElem;               /* Number of elements */
      JsonNode **apElem;       /* Value of each element */
    } ar;
    struct {                /* Struct value */
      JsonStructElem *pFirst;  /* List of structure elements */
      JsonStructElem *pLast;   /* Last element of the list */
    } st;
  } u;
};

/* Values for eJType */
#define XJD1_FALSE     0
#define XJD1_TRUE      1
#define XJD1_REAL      2
#define XJD1_NULL      3
#define XJD1_STRING    4
#define XJD1_ARRAY     5
#define XJD1_STRUCT    6

/* Parsing context */
struct Parse {
  xjd1 *pConn;                    /* Connect for recording errors */
  Pool *pPool;                    /* Memory allocation pool */
  Command *pCmd;                  /* Results */
  Token sTok;                     /* Last token seen */
  int errCode;                    /* Error code */
  String errMsg;                  /* Error message string */
};

/* A list of sorted results. */
struct ResultList {
  Pool *pPool;
  int nKey;
  ResultItem *pSaved;
  ResultItem *pItem;
};

struct Aggregate {
  int nExpr;                      /* Number of aggregate functions */
  struct AggExpr {
    Expr *pExpr;                  /* Array of aggregate functions */
    JsonNode *pValue;             /* Value returned by xFinal() */
    void *pAggCtx;                /* Context pointer used by implementation */
  } *aAggExpr;
};

/* A query statement */
struct Query {
  int eQType;                   /* Query type */
  xjd1_stmt *pStmt;             /* Statement this query is part of */
  union {
    struct {                    /* For compound queries */
      Query *pLeft;               /* Left subquery */
      Query *pRight;              /* Right subquery */
      int doneLeft;               /* True if left has run to completion */
      ResultList left;            /* Sorted results of pLeft */
      ResultList right;           /* Sorted results of pRight */
      JsonNode *pOut;
    } compound;
    struct {                    /* For simple queries */
      int isDistinct;             /* True if the DISTINCT keyword is present */
      Expr *pRes;                 /* Result JSON string */
      DataSrc *pFrom;             /* The FROM clause */
      Expr *pWhere;               /* The WHERE clause */
      ExprList *pGroupBy;         /* The GROUP BY clause */
      Expr *pHaving;              /* The HAVING clause */
      Aggregate *pAgg;            /* Aggregation info. 0 for non-aggregates */
      ResultList grouped;         /* Grouped results, for GROUP BY queries */
      ResultList distincted;      /* Distinct results */
    } simple;
  } u;
  const char *zAs;                /* Alias assigned to result object (if any) */
  ExprList *pOrderBy;             /* The ORDER BY clause */
  Expr *pLimit;                   /* The LIMIT clause */
  Expr *pOffset;                  /* The OFFSET clause */
  int eDocFrom;                   /* XJD1_FROM_* - configures xjd1QueryDoc() */
  ResultList ordered;             /* Query results in sorted order */
  int bLimitValid;                /* Set to true after nLimit is set */
  int nLimit;                     /* Stop after returning this many more rows */
};

/* Candidate values for Query.eDocFrom */
#define XJD1_FROM_DATASRC    0
#define XJD1_FROM_GROUPED    1
#define XJD1_FROM_DISTINCTED 2
#define XJD1_FROM_ORDERED    3

/* A Data Source is a representation of a term out of the FROM clause. */
struct DataSrc {
  int eDSType;              /* Source type */
  char *zAs;                /* The identifier after the AS keyword */
  Query *pQuery;            /* Query this data source services */
  JsonNode *pValue;         /* Current value for this data source */
  int isOwner;              /* True if this DataSrc owns the pOut line */
  union {
    struct {                /* For a join.  eDSType==TK_COMMA */
      int bStart;              /* True if has already started */
      DataSrc *pLeft;          /* Data source on the left */
      DataSrc *pRight;         /* Data source on the right */
    } join;
    struct {                /* For a named collection.  eDSType==TK_ID */
      char *zName;             /* The collection name */
      sqlite3_stmt *pStmt;     /* Cursor for reading content */
      int eofSeen;             /* True if at EOF */
    } tab;
    struct {                /* For a named collection.  eDSType==TK_ID */
      Expr *pPath;             /* Path to correlated variable */
      JsonNode *pArray;        /* Value to iterate through */
      int iNext;               /* Index of next value in pValue to return */
    } path;
    struct {                /* EACH() or FLATTEN().  eDSType==TK_FLATTENOP */
      DataSrc *pNext;          /* Data source to the left */
      char cOpName;            /* 'E' or 'F' for "EACH" or "FLATTEN" */
      Expr *pExpr;             /* Expression to flatten on */
      Expr *pAs;               /* AS path, if any */
      FlattenIter *pIter;      /* Iterator */
    } flatten;
    struct {                /* A subquery.  eDSType==TK_SELECT */
      Query *q;                /* The subquery */
    } subq;
    struct {                /* An empty FROM clause.  eDSType==TK_NULL */
      int isDone;              /* True if single row already returned */
    } null;
  } u;
};

/* Any command, including but not limited to a query */
struct Command {
  int eCmdType;             /* Type of command */
  union {
    struct {                /* Transaction control operations */
      char *zTransId;          /* Transaction name */
    } trans;
    struct {                /* Create or drop table */
      int ifExists;            /* IF [NOT] EXISTS clause */
      char *zName;             /* Name of table */
    } crtab;
    struct {                /* Query statement */
      Query *pQuery;           /* The query */
    } q;
    struct {                /* Insert */
      char *zName;             /* Table to insert into */
      Expr *pValue;            /* Value to be inserted */
      Query *pQuery;           /* Query to insert from */
    } ins;
    struct {                /* Delete */
      char *zName;             /* Table to delete */
      Expr *pWhere;            /* WHERE clause */
    } del;
    struct {                /* Update */
      char *zName;             /* Table to modify */
      Expr *pWhere;            /* WHERE clause */
      ExprList *pChng;         /* Alternating lvalve and new value */
      Expr *pUpsert;           /* ELSE INSERT value */
    } update;
    struct {                /* Pragma */
      char *zName;             /* Pragma name */
      Expr *pValue;            /* Argument or empty string */
    } prag;
  } u;
};

/******************************** context.c **********************************/
void xjd1ContextUnref(xjd1_context*);

/******************************** conn.c *************************************/
void xjd1Unref(xjd1*);
void xjd1Error(xjd1*,int,const char*,...);

/******************************** datasrc.c **********************************/
int xjd1DataSrcInit(DataSrc*,Query*,void*);
int xjd1DataSrcRewind(DataSrc*);
int xjd1DataSrcStep(DataSrc*);
int xjd1DataSrcClose(DataSrc*);
int xjd1DataSrcCount(DataSrc*);
JsonNode *xjd1DataSrcDoc(DataSrc*, const char*);
int xjd1DataSrcCount(DataSrc *);
JsonNode *xjd1DataSrcCacheRead(DataSrc *, JsonNode **, const char *zDocname);
void xjd1DataSrcCacheSave(DataSrc *, JsonNode **);
int xjd1DataSrcResolve(DataSrc *, const char *zDocname);
JsonNode *xjd1DataSrcRead(DataSrc *, int);

/******************************** delete.c ***********************************/
int xjd1DeleteStep(xjd1_stmt*);

/******************************** expr.c *************************************/
int xjd1ExprInit(Expr*, xjd1_stmt*, Query*, int, void *);
int xjd1ExprListInit(ExprList*, xjd1_stmt*, Query*, int, void *);
JsonNode *xjd1ExprEval(Expr*);
int xjd1ExprTrue(Expr*);
int xjd1ExprClose(Expr*);
int xjd1ExprListClose(ExprList*);

/* Candidates for the 4th parameter to xjd1ExprInit() */
#define XJD1_EXPR_RESULT  1
#define XJD1_EXPR_WHERE   2
#define XJD1_EXPR_GROUPBY 3
#define XJD1_EXPR_HAVING  4
#define XJD1_EXPR_ORDERBY 5
#define XJD1_EXPR_LIMIT   6
#define XJD1_EXPR_OFFSET  7

/******************************** json.c *************************************/
JsonNode *xjd1JsonParse(const char *zIn, int mxIn);
JsonNode *xjd1JsonRef(JsonNode*);
void xjd1JsonRender(String*, const JsonNode*);
int xjd1JsonToReal(const JsonNode*, double*);
int xjd1JsonToString(const JsonNode*, String*);
int xjd1JsonCompare(const JsonNode*, const JsonNode*, int insensitive);
JsonNode *xjd1JsonNew(Pool*);
JsonNode *xjd1JsonEdit(JsonNode*);
JsonNode *xjd1JsonDeepCopy(JsonNode*);
void xjd1JsonFree(JsonNode*);
void xjd1JsonToNull(JsonNode*);
void xjd1DequoteString(char*,int);
int xjd1JsonInsert(JsonNode *, const char *, JsonNode *);
int xjd1JsonTidy(String *, const char *);

/******************************** memory.c ***********************************/
Pool *xjd1PoolNew(void);
void xjd1PoolClear(Pool*);
void xjd1PoolDelete(Pool*);
void *xjd1PoolMalloc(Pool*, int);
void *xjd1PoolMallocZero(Pool*, int);
char *xjd1PoolDup(Pool*, const char *, int);
void *xjd1MallocZero(int);

/******************************** pragma.c ***********************************/
int xjd1PragmaStep(xjd1_stmt*);

/******************************** query.c ************************************/
int xjd1QueryInit(Query*,xjd1_stmt*,void*);
int xjd1QueryRewind(Query*);
int xjd1QueryStep(Query*);
int xjd1QueryClose(Query*);
JsonNode *xjd1QueryDoc(Query*, int);

/******************************** stmt.c *************************************/
JsonNode *xjd1StmtDoc(xjd1_stmt*);
void xjd1StmtError(xjd1_stmt *,int,const char*,...);

/******************************** string.c ***********************************/
int xjd1Strlen30(const char *);
void xjd1StringInit(String*, Pool*, int);
String *xjd1StringNew(Pool*, int);
char *xjd1StringGet(String*);
int xjd1StringAppend(String*, const char*, int);
#define xjd1StringText(S)      ((S)->zBuf)
#define xjd1StringLen(S)       ((S)->nUsed)
void xjd1StringTruncate(String*);
void xjd1StringClear(String*);
void xjd1StringDelete(String*);
void xjd1StringRemovePrefix(String*,int);
int xjd1StringVAppendF(String*, const char*, va_list);
int xjd1StringAppendF(String*, const char*, ...);


/******************************** tokenize.c *********************************/
extern const unsigned char xjd1CtypeMap[];
#define xjd1Isspace(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x01)
#define xjd1Isalnum(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x06)
#define xjd1Isalpha(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x02)
#define xjd1Isdigit(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x04)
#define xjd1Isxdigit(x)  (xjd1CtypeMap[(unsigned char)(x)]&0x08)
#define xjd1Isident(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x46)
int xjd1RunParser(xjd1*, xjd1_stmt*, const char*, int*);
void xjd1ParseError(Parse *, int, const char *, ...);

/******************************** trace.c ************************************/
const char *xjd1TokenName(int);
void xjd1TraceCommand(String*,int,const Command*);
void xjd1TraceQuery(String*,int,const Query*);
void xjd1TraceDataSrc(String*,int,const DataSrc*);
void xjd1TraceExpr(String*,const Expr*);
void xjd1TraceExprList(String*,int, const ExprList*);

/******************************** update.c ***********************************/
int xjd1UpdateStep(xjd1_stmt*);

/******************************** func.c *************************************/
int xjd1FunctionInit(Expr *p, xjd1_stmt *pStmt, Query *pQuery, int bAggOk);
JsonNode *xjd1FunctionEval(Expr *p);
void xjd1FunctionClose(Expr *p);

int xjd1AggregateInit(xjd1_stmt *, Query *, Expr *);
int xjd1AggregateStep(Aggregate *, int *);
int xjd1AggregateFinalize(Aggregate *);
void xjd1AggregateClear(Query *);

#endif /* _XJD1INT_H */
