/*
 * (C) Copyright 2005- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
 * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
 */

#include "grib_api_internal.h"

/* Note: all non-alpha are mapped to 0 */
static const int mapping[] = {
    0,  /* 00 */
    0,  /* 01 */
    0,  /* 02 */
    0,  /* 03 */
    0,  /* 04 */
    0,  /* 05 */
    0,  /* 06 */
    0,  /* 07 */
    0,  /* 08 */
    0,  /* 09 */
    0,  /* 0a */
    0,  /* 0b */
    0,  /* 0c */
    0,  /* 0d */
    0,  /* 0e */
    0,  /* 0f */
    0,  /* 10 */
    0,  /* 11 */
    0,  /* 12 */
    0,  /* 13 */
    0,  /* 14 */
    0,  /* 15 */
    0,  /* 16 */
    0,  /* 17 */
    0,  /* 18 */
    0,  /* 19 */
    0,  /* 1a */
    0,  /* 1b */
    0,  /* 1c */
    0,  /* 1d */
    0,  /* 1e */
    0,  /* 1f */
    0,  /* 20 */
    0,  /* 21 */
    0,  /* 22 */
    38, /* # */
    0,  /* 24 */
    0,  /* 25 */
    0,  /* 26 */
    0,  /* 27 */
    0,  /* 28 */
    0,  /* 29 */
    0,  /* 2a */
    0,  /* 2b */
    0,  /* 2c */
    0,  /* 2d */
    0,  /* 2e */
    0,  /* 2f */
    1,  /* 0 */
    2,  /* 1 */
    3,  /* 2 */
    4,  /* 3 */
    5,  /* 4 */
    6,  /* 5 */
    7,  /* 6 */
    8,  /* 7 */
    9,  /* 8 */
    10, /* 9 */
    0,  /* 3a */
    0,  /* 3b */
    0,  /* 3c */
    0,  /* 3d */
    0,  /* 3e */
    0,  /* 3f */
    0,  /* 40 */
    11, /* A */
    12, /* B */
    13, /* C */
    14, /* D */
    15, /* E */
    16, /* F */
    17, /* G */
    18, /* H */
    19, /* I */
    20, /* J */
    21, /* K */
    22, /* L */
    23, /* M */
    24, /* N */
    25, /* O */
    26, /* P */
    27, /* Q */
    28, /* R */
    29, /* S */
    30, /* T */
    31, /* U */
    32, /* V */
    33, /* W */
    34, /* X */
    35, /* Y */
    36, /* Z */
    0,  /* 5b */
    0,  /* 5c */
    0,  /* 5d */
    0,  /* 5e */
    37, /* _ */
    0,  /* 60 */
    11, /* a */
    12, /* b */
    13, /* c */
    14, /* d */
    15, /* e */
    16, /* f */
    17, /* g */
    18, /* h */
    19, /* i */
    20, /* j */
    21, /* k */
    22, /* l */
    23, /* m */
    24, /* n */
    25, /* o */
    26, /* p */
    27, /* q */
    28, /* r */
    29, /* s */
    30, /* t */
    31, /* u */
    32, /* v */
    33, /* w */
    34, /* x */
    35, /* y */
    36, /* z */
    0,  /* 7b */
    0,  /* 7c */
    0,  /* 7d */
    0,  /* 7e */
    0,  /* 7f */
    0,  /* 80 */
    0,  /* 81 */
    0,  /* 82 */
    0,  /* 83 */
    0,  /* 84 */
    0,  /* 85 */
    0,  /* 86 */
    0,  /* 87 */
    0,  /* 88 */
    0,  /* 89 */
    0,  /* 8a */
    0,  /* 8b */
    0,  /* 8c */
    0,  /* 8d */
    0,  /* 8e */
    0,  /* 8f */
    0,  /* 90 */
    0,  /* 91 */
    0,  /* 92 */
    0,  /* 93 */
    0,  /* 94 */
    0,  /* 95 */
    0,  /* 96 */
    0,  /* 97 */
    0,  /* 98 */
    0,  /* 99 */
    0,  /* 9a */
    0,  /* 9b */
    0,  /* 9c */
    0,  /* 9d */
    0,  /* 9e */
    0,  /* 9f */
    0,  /* a0 */
    0,  /* a1 */
    0,  /* a2 */
    0,  /* a3 */
    0,  /* a4 */
    0,  /* a5 */
    0,  /* a6 */
    0,  /* a7 */
    0,  /* a8 */
    0,  /* a9 */
    0,  /* aa */
    0,  /* ab */
    0,  /* ac */
    0,  /* ad */
    0,  /* ae */
    0,  /* af */
    0,  /* b0 */
    0,  /* b1 */
    0,  /* b2 */
    0,  /* b3 */
    0,  /* b4 */
    0,  /* b5 */
    0,  /* b6 */
    0,  /* b7 */
    0,  /* b8 */
    0,  /* b9 */
    0,  /* ba */
    0,  /* bb */
    0,  /* bc */
    0,  /* bd */
    0,  /* be */
    0,  /* bf */
    0,  /* c0 */
    0,  /* c1 */
    0,  /* c2 */
    0,  /* c3 */
    0,  /* c4 */
    0,  /* c5 */
    0,  /* c6 */
    0,  /* c7 */
    0,  /* c8 */
    0,  /* c9 */
    0,  /* ca */
    0,  /* cb */
    0,  /* cc */
    0,  /* cd */
    0,  /* ce */
    0,  /* cf */
    0,  /* d0 */
    0,  /* d1 */
    0,  /* d2 */
    0,  /* d3 */
    0,  /* d4 */
    0,  /* d5 */
    0,  /* d6 */
    0,  /* d7 */
    0,  /* d8 */
    0,  /* d9 */
    0,  /* da */
    0,  /* db */
    0,  /* dc */
    0,  /* dd */
    0,  /* de */
    0,  /* df */
    0,  /* e0 */
    0,  /* e1 */
    0,  /* e2 */
    0,  /* e3 */
    0,  /* e4 */
    0,  /* e5 */
    0,  /* e6 */
    0,  /* e7 */
    0,  /* e8 */
    0,  /* e9 */
    0,  /* ea */
    0,  /* eb */
    0,  /* ec */
    0,  /* ed */
    0,  /* ee */
    0,  /* ef */
    0,  /* f0 */
    0,  /* f1 */
    0,  /* f2 */
    0,  /* f3 */
    0,  /* f4 */
    0,  /* f5 */
    0,  /* f6 */
    0,  /* f7 */
    0,  /* f8 */
    0,  /* f9 */
    0,  /* fa */
    0,  /* fb */
    0,  /* fc */
    0,  /* fd */
    0,  /* fe */
    0,  /* ff */
};

/* ECC-388 */
#ifdef DEBUG
static const size_t NUM_MAPPINGS = sizeof(mapping) / sizeof(mapping[0]);

#define DebugCheckBounds(index, value)                                                                  \
    do {                                                                                                \
        if (!((index) >= 0 && (index) < NUM_MAPPINGS)) {                                                \
            printf("ERROR: string='%s' index=%ld @ %s +%d \n", value, (long)index, __FILE__, __LINE__); \
            abort();                                                                                    \
        }                                                                                               \
    } while (0)
#else
#define DebugCheckBounds(index, value)
#endif


#define SIZE 39

#if GRIB_PTHREADS
static pthread_once_t once   = PTHREAD_ONCE_INIT;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static void init()
{
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&mutex, &attr);
    pthread_mutexattr_destroy(&attr);
}
#elif GRIB_OMP_THREADS
static int once = 0;
static omp_nest_lock_t mutex;

static void init()
{
    GRIB_OMP_CRITICAL(lock_grib_trie_c)
    {
        if (once == 0) {
            omp_init_nest_lock(&mutex);
            once = 1;
        }
    }
}
#endif

struct grib_trie
{
    grib_trie* next[SIZE];
    grib_context* context;
    int first;
    int last;
    void* data;
};

grib_trie* grib_trie_new(grib_context* c)
{
#ifdef RECYCLE_TRIE
    grib_trie* t = grib_context_malloc_clear_persistent(c, sizeof(grib_trie));
#else
    grib_trie* t = (grib_trie*)grib_context_malloc_clear(c, sizeof(grib_trie));
#endif
    t->context = c;
    t->first   = SIZE;
    t->last    = -1;
    return t;
}

void grib_trie_delete_container(grib_trie* t)
{
    GRIB_MUTEX_INIT_ONCE(&once, &init);
    GRIB_MUTEX_LOCK(&mutex);
    if (t) {
        int i;
        for (i = t->first; i <= t->last; i++)
            if (t->next[i]) {
                grib_trie_delete_container(t->next[i]);
            }
#ifdef RECYCLE_TRIE
        grib_context_free_persistent(t->context, t);
#else
        grib_context_free(t->context, t);
#endif
    }
    GRIB_MUTEX_UNLOCK(&mutex);
}

void grib_trie_delete(grib_trie* t)
{
    GRIB_MUTEX_INIT_ONCE(&once, &init);
    GRIB_MUTEX_LOCK(&mutex);
    if (t) {
        int i;
        for (i = t->first; i <= t->last; i++)
            if (t->next[i]) {
                grib_context_free(t->context, t->next[i]->data);
                grib_trie_delete(t->next[i]);
            }
#ifdef RECYCLE_TRIE
        grib_context_free_persistent(t->context, t);
#else
        grib_context_free(t->context, t);
#endif
    }
    GRIB_MUTEX_UNLOCK(&mutex);
}

void grib_trie_clear(grib_trie* t)
{
    if (t) {
        int i;
        t->data = NULL;
        for (i = t->first; i <= t->last; i++)
            if (t->next[i])
                grib_trie_clear(t->next[i]);
    }
}

void* grib_trie_insert(grib_trie* t, const char* key, void* data)
{
    grib_trie* last = t;
    const char* k   = key;
    void* old       = NULL;

    if (!t) {
        Assert(!"grib_trie_insert: grib_trie==NULL");
        return NULL;
    }

    GRIB_MUTEX_INIT_ONCE(&once, &init);
    GRIB_MUTEX_LOCK(&mutex);

    while (*k && t) {
        last = t;
        DebugCheckBounds((int)*k, key);
        t = t->next[mapping[(int)*k]];
        if (t)
            k++;
    }

    if (*k == 0) {
        old     = t->data;
        t->data = data;
    }
    else {
        t = last;
        while (*k) {
            int j = 0;
            DebugCheckBounds((int)*k, key);
            j = mapping[(int)*k++];
            if (j < t->first)
                t->first = j;
            if (j > t->last)
                t->last = j;
            t = t->next[j] = grib_trie_new(t->context);
        }
        old     = t->data;
        t->data = data;
    }
    GRIB_MUTEX_UNLOCK(&mutex);
    return data == old ? NULL : old;
}

void* grib_trie_insert_no_replace(grib_trie* t, const char* key, void* data)
{
    grib_trie* last = t;
    const char* k   = key;

    if (!t) {
        Assert(!"grib_trie_insert_no_replace: grib_trie==NULL");
        return NULL;
    }

    while (*k && t) {
        last = t;
        DebugCheckBounds((int)*k, key);
        t = t->next[mapping[(int)*k]];
        if (t)
            k++;
    }

    if (*k != 0) {
        t = last;
        while (*k) {
            int j = 0;
            DebugCheckBounds((int)*k, key);
            j = mapping[(int)*k++];
            if (j < t->first)
                t->first = j;
            if (j > t->last)
                t->last = j;
            t = t->next[j] = grib_trie_new(t->context);
        }
    }

    if (!t->data)
        t->data = data;

    return t->data;
}

void* grib_trie_get(grib_trie* t, const char* key)
{
    const char* k = key;
    GRIB_MUTEX_INIT_ONCE(&once, &init);
    GRIB_MUTEX_LOCK(&mutex);

    while (*k && t) {
        DebugCheckBounds((int)*k, key);
        t = t->next[mapping[(int)*k++]];
    }

    if (*k == 0 && t != NULL && t->data != NULL) {
        GRIB_MUTEX_UNLOCK(&mutex);
        return t->data;
    }
    GRIB_MUTEX_UNLOCK(&mutex);
    return NULL;
}

/*
To be rewritten.
void grib_trie_remove(grib_trie* trie,const char* key)
{
  const char *k = key;
  grib_trie_internal *t=trie->trie;

  grib_context *c = trie->context;

  while(*k && t)
    t = t->next[mapping[(int)*k++]];

  if(*k == 0 && t != NULL)
    t->data->data = NULL;
  else
    grib_context_log(c,GRIB_LOG_WARNING,"grib_trie_remove: key not found [%s]",key);

}
*/
