/*
 * txtquery operations with ltree
 * Teodor Sigaev <teodor@stack.net>
 * contrib/ltree/ltxtquery_op.c
 */
#include "postgres.h"
#include "knl/knl_variable.h"

#include <ctype.h>

#include "ltree.h"

PG_FUNCTION_INFO_V1(ltxtq_exec);
PG_FUNCTION_INFO_V1(ltxtq_rexec);

/*
 * check for boolean condition
 */
bool ltree_execute(ITEM* curitem, void* checkval, bool calcnot, bool (*chkcond)(void* checkval, ITEM* val))
{
    if (curitem->type == VAL)
        return (*chkcond)(checkval, curitem);
    else if (curitem->val == (int4)'!') {
        return (calcnot) ? ((ltree_execute(curitem + 1, checkval, calcnot, chkcond)) ? false : true) : true;
    } else if (curitem->val == (int4)'&') {
        if (ltree_execute(curitem + curitem->left, checkval, calcnot, chkcond))
            return ltree_execute(curitem + 1, checkval, calcnot, chkcond);
        else
            return false;
    } else { /* |-operator */
        if (ltree_execute(curitem + curitem->left, checkval, calcnot, chkcond))
            return true;
        else
            return ltree_execute(curitem + 1, checkval, calcnot, chkcond);
    }
    return false;
}

typedef struct {
    ltree* node;
    char* operand;
} CHKVAL;

static bool checkcondition_str(void* checkval, ITEM* val)
{
    ltree_level* level = LTREE_FIRST(((CHKVAL*)checkval)->node);
    int tlen = ((CHKVAL*)checkval)->node->numlevel;
    char* op = ((CHKVAL*)checkval)->operand + val->distance;
    int (*cmpptr)(const char*, const char*, size_t);

    cmpptr = (val->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
    while (tlen > 0) {
        if (val->flag & LVAR_SUBLEXEME) {
            if (compare_subnode(level, op, val->length, cmpptr, (val->flag & LVAR_ANYEND)))
                return true;
        } else if ((val->length == level->len || (level->len > val->length && (val->flag & LVAR_ANYEND))) &&
                   (*cmpptr)(op, level->name, val->length) == 0)
            return true;

        tlen--;
        level = LEVEL_NEXT(level);
    }

    return false;
}

Datum ltxtq_exec(PG_FUNCTION_ARGS)
{
    ltree* val = PG_GETARG_LTREE(0);
    ltxtquery* query = PG_GETARG_LTXTQUERY(1);
    CHKVAL chkval;
    bool result = false;

    chkval.node = val;
    chkval.operand = GETOPERAND(query);

    result = ltree_execute(GETQUERY(query), &chkval, true, checkcondition_str);

    PG_FREE_IF_COPY(val, 0);
    PG_FREE_IF_COPY(query, 1);
    PG_RETURN_BOOL(result);
}

Datum ltxtq_rexec(PG_FUNCTION_ARGS)
{
    PG_RETURN_DATUM(DirectFunctionCall2(ltxtq_exec, PG_GETARG_DATUM(1), PG_GETARG_DATUM(0)));
}