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

#define NETDATA_RRD_INTERNALS
#include "rrd.h"

char *translate_label_source(LABEL_SOURCE l) {
    switch (l) {
        case LABEL_SOURCE_AUTO:
            return "AUTO";
        case LABEL_SOURCE_NETDATA_CONF:
            return "NETDATA.CONF";
        case LABEL_SOURCE_DOCKER :
            return "DOCKER";
        case LABEL_SOURCE_ENVIRONMENT  :
            return "ENVIRONMENT";
        case LABEL_SOURCE_KUBERNETES :
            return "KUBERNETES";
        default:
            return "Invalid label source";
    }
}

int is_valid_label_value(char *value) {
    while(*value) {
        if(*value == '"' || *value == '\'' || *value == '*' || *value == '!') {
            return 0;
        }

        value++;
    }

    return 1;
}

int is_valid_label_key(char *key) {
    //Prometheus exporter
    if(!strcmp(key, "chart") || !strcmp(key, "family")  || !strcmp(key, "dimension"))
        return 0;

    //Netdata and Prometheus  internal
    if (*key == '_')
        return 0;

    while(*key) {
        if(!(isdigit(*key) || isalpha(*key) || *key == '.' || *key == '_' || *key == '-'))
            return 0;

        key++;
    }

    return 1;
}

void strip_last_symbol(
    char *str,
    char symbol,
    SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
{
    char *end = str;

    while (*end && *end != symbol) {
        if (unlikely(skip_escaped_characters && *end == '\\')) {
            end++;
            if (unlikely(!*end))
                break;
        }
        end++;
    }
    if (likely(*end == symbol))
        *end = '\0';
}

char *strip_double_quotes(char *str, SKIP_ESCAPED_CHARACTERS_OPTION skip_escaped_characters)
{
    if (*str == '"') {
        str++;
        strip_last_symbol(str, '"', skip_escaped_characters);
    }

    return str;
}

struct label *create_label(char *key, char *value, LABEL_SOURCE label_source)
{
    size_t key_len = strlen(key), value_len = strlen(value);
    size_t n = sizeof(struct label) + key_len + 1 + value_len + 1;
    struct label *result = callocz(1,n);
    if (result != NULL) {
        char *c = (char *)result;
        c += sizeof(struct label);
        strcpy(c, key);
        result->key = c;
        c += key_len + 1;
        strcpy(c, value);
        result->value = c;
        result->label_source = label_source;
        result->key_hash = simple_hash(result->key);
    }
    return result;
}

void free_label_list(struct label *labels)
{
    while (labels != NULL)
    {
        struct label *current = labels;
        labels = labels->next;
        freez(current);
    }
}

void replace_label_list(struct label_index *labels, struct label *new_labels)
{
    netdata_rwlock_wrlock(&labels->labels_rwlock);
    struct label *old_labels = labels->head;
    labels->head = new_labels;
    netdata_rwlock_unlock(&labels->labels_rwlock);

    free_label_list(old_labels);
}

struct label *add_label_to_list(struct label *l, char *key, char *value, LABEL_SOURCE label_source)
{
    struct label *lab = create_label(key, value, label_source);
    lab->next = l;
    return lab;
}

void update_label_list(struct label **labels, struct label *new_labels)
{
    free_label_list(*labels);
    *labels = NULL;

    while (new_labels != NULL)
    {
        *labels = add_label_to_list(*labels, new_labels->key, new_labels->value, new_labels->label_source);
        new_labels = new_labels->next;
    }
}

struct label *label_list_lookup_key(struct label *head, char *key, uint32_t key_hash)
{
    while (head != NULL)
    {
        if (head->key_hash == key_hash && !strcmp(head->key, key))
            return head;
        head = head->next;
    }
    return NULL;
}

int label_list_contains_key(struct label *head, char *key, uint32_t key_hash)
{
    return (label_list_lookup_key(head, key, key_hash) != NULL);
}

int label_list_contains(struct label *head, struct label *check)
{
    return label_list_contains_key(head, check->key, check->key_hash);
}

struct label *label_list_lookup_keylist(struct label *head, char *key)
{
    SIMPLE_PATTERN *pattern = NULL;

    pattern = simple_pattern_create(key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT);

    while (head != NULL)
    {
        if (simple_pattern_matches(pattern, head->key))
            break;
        head = head->next;
    }
    simple_pattern_free(pattern);
    return head;
}

int label_list_contains_keylist(struct label *head, char *keylist)
{
    return (label_list_lookup_keylist(head, keylist) != NULL);
}


/* Create a list with entries from both lists.
   If any entry in the low priority list is masked by an entry in the high priority list then delete it.
*/
struct label *merge_label_lists(struct label *lo_pri, struct label *hi_pri)
{
    struct label *result = hi_pri;
    while (lo_pri != NULL)
    {
        struct label *current = lo_pri;
        lo_pri = lo_pri->next;
        if (!label_list_contains(result, current)) {
            current->next = result;
            result = current;
        }
        else
            freez(current);
    }
    return result;
}