// SPDX-License-Identifier: GPL-3.0-or-later

#include "pattern-array.h"

struct pattern_array *pattern_array_allocate()
{
    struct pattern_array *pa = callocz(1, sizeof(*pa));
    return pa;
}

void pattern_array_add_lblkey_with_sp(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *sp)
{
    if (!pa || !key) {
        simple_pattern_free(sp);
        return;
    }

    if (!sp)
        return;

    STRING *string_key = string_strdupz(key);
    Pvoid_t *Pvalue = JudyLIns(&pa->JudyL, (Word_t) string_key, PJE0);
    if (!Pvalue || Pvalue == PJERR ) {
        string_freez(string_key);
        simple_pattern_free(sp);
        return;
    }

    struct pattern_array *pai;
    if (*Pvalue)
        string_freez(string_key);
    else
        *Pvalue = callocz(1, sizeof(*pai));

    pai = *Pvalue;

    Pvalue = JudyLIns(&pai->JudyL, (Word_t) ++pai->key_count, PJE0);
    if (!Pvalue || Pvalue == PJERR) {
        simple_pattern_free(sp);
        return;
    }

    *Pvalue = sp;
}

bool pattern_array_label_match(
    struct pattern_array *pa,
    RRDLABELS *labels,
    char eq,
    size_t *searches)
{
    if (!pa || !labels)
        return true;

    Pvoid_t *Pvalue;
    Word_t Index = 0;
    bool first_then_next = true;
    while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first_then_next))) {
        // for each label key in the pattern array

        struct pattern_array *pai = *Pvalue;
        SIMPLE_PATTERN_RESULT match = SP_NOT_MATCHED;
        Word_t Index2 = 0;
        bool first_then_next2 = true;
        while ((Pvalue = JudyLFirstThenNext(pai->JudyL, &Index2, &first_then_next2))) {
            // for each pattern in the label key pattern list
            if (!*Pvalue)
                continue;

            match = rrdlabels_match_simple_pattern_parsed(labels, (SIMPLE_PATTERN *)(*Pvalue), eq, searches);

            if (match != SP_NOT_MATCHED)
                break;
        }
        if (match != SP_MATCHED_POSITIVE)
            return false;
    }
    return true;
}

struct pattern_array *pattern_array_add_key_simple_pattern(struct pattern_array *pa, const char *key, SIMPLE_PATTERN *pattern)
{
    if (unlikely(!pattern || !key))
        return pa;

    if (!pa)
        pa = pattern_array_allocate();

    pattern_array_add_lblkey_with_sp(pa, key, pattern);
    return pa;
}

struct pattern_array *pattern_array_add_simple_pattern(struct pattern_array *pa, SIMPLE_PATTERN *pattern, char sep)
{
    if (unlikely(!pattern))
        return pa;

    if (!pa)
        pa = pattern_array_allocate();

    char *label_key;
    while (pattern && (label_key = simple_pattern_iterate(&pattern))) {
        char key[RRDLABELS_MAX_NAME_LENGTH + 1], *key_sep;

        if (unlikely(!label_key || !(key_sep = strchr(label_key, sep))))
            return pa;

        *key_sep = '\0';
        strncpyz(key, label_key, RRDLABELS_MAX_NAME_LENGTH);
        *key_sep = sep;

        pattern_array_add_lblkey_with_sp(pa, key, string_to_simple_pattern(label_key));
    }
    return pa;
}

struct pattern_array *pattern_array_add_key_value(struct pattern_array *pa, const char *key, const char *value, char sep)
{
    if (unlikely(!key || !value))
        return pa;

    if (!pa)
        pa = pattern_array_allocate();

    char label_key[RRDLABELS_MAX_NAME_LENGTH + RRDLABELS_MAX_VALUE_LENGTH + 2];
    snprintfz(label_key, sizeof(label_key) - 1, "%s%c%s", key, sep, value);
    pattern_array_add_lblkey_with_sp(
        pa, key, simple_pattern_create(label_key, SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS, SIMPLE_PATTERN_EXACT, true));
    return pa;
}

void pattern_array_free(struct pattern_array *pa)
{
    if (!pa)
        return;

    Pvoid_t *Pvalue;
    Word_t Index = 0;
    bool first = true;
    while ((Pvalue = JudyLFirstThenNext(pa->JudyL, &Index, &first))) {
        struct pattern_array *pai = *Pvalue;

        Word_t Index2 = 0;
        bool first2 = true;
        while ((Pvalue = JudyLFirstThenNext(pai->JudyL, &Index2, &first2))) {
            SIMPLE_PATTERN *sp = (SIMPLE_PATTERN *)*Pvalue;
            simple_pattern_free(sp);
        }

        JudyLFreeArray(&(pai->JudyL), PJE0);
        string_freez((STRING *)Index);
        freez(pai);
    }

    JudyLFreeArray(&(pa->JudyL), PJE0);
    freez(pa);
}

