/*
 # This file is part of the Astrometry.net suite.
 # Licensed under a 3-clause BSD style license - see LICENSE
 */
#include <assert.h>

#include "intmap.h"

#define IMGLUE2(n,f) n ## _ ## f
#define IMGLUE(n,f) IMGLUE2(n,f)

#define key_t int
#define nl il
#define KL(x) IMGLUE(nl, x)

intmap_t* intmap_new(int datasize, int subblocksize, int blocksize,
                     int Ndense) {
    intmap_t* im = calloc(1, sizeof(intmap_t));
    if (!blocksize)
        blocksize = 4096;
    im->blocksize = subblocksize;
    im->datasize = datasize;
    if (Ndense) {
        im->ND = Ndense;
        im->dense = calloc(im->ND, sizeof(bl*));
    } else {
        im->keys = KL(new)(blocksize);
        im->lists = pl_new(blocksize);
    }
    return im;
}

void intmap_free(intmap_t* im) {
    int i;
    if (im->lists) {
        for (i=0; i<pl_size(im->lists); i++) {
            bl* lst = pl_get(im->lists, i);
            bl_free(lst);
        }
        pl_free(im->lists);
    }
    if (im->dense) {
        for (i=0; i<im->ND; i++) {
            bl* lst = im->dense[i];
            if (!lst)
                continue;
            bl_free(lst);
        }
        free(im->dense);
    }
    if (im->keys)
        KL(free)(im->keys);
    free(im);
}

bl* intmap_find(intmap_t* im, key_t key, anbool create) {
    key_t ind;
    assert(key >= 0);
    assert(im);
    if (!im->dense) {
        assert(im->keys);
        assert(im->lists);
        ind = KL(sorted_index_of)(im->keys, key);
        if (ind == -1) {
            bl* lst;
            if (!create)
                return NULL;
            lst = bl_new(im->blocksize, im->datasize);
            ind = KL(insert_unique_ascending)(im->keys, key);
            pl_insert(im->lists, ind, lst);
            return lst;
        }
        return pl_get(im->lists, ind);
    } else {
        bl* lst;
        assert(key < im->ND);
        assert(im->dense);
        lst = im->dense[key];
        if (lst)
            return lst;
        if (!create)
            return lst;
        lst = im->dense[key] = bl_new(im->blocksize, im->datasize);
        return lst;
    }
}

void intmap_append(intmap_t* it, int key, void* pval) {
    bl* lst = intmap_find(it, key, TRUE);
    bl_append(lst, pval);
}

anbool intmap_get_entry(intmap_t* im, int index,
                        key_t* p_key, bl** p_list) {
    assert(im);
    assert(index >= 0);
    if (im->dense) {
        if (index >= im->ND)
            return FALSE;
        if (p_key)
            *p_key = index;
        if (p_list)
            *p_list = im->dense[index];
        return TRUE;
    }

    assert(im->keys);
    assert(im->lists);
    if (index >= KL(size)(im->keys))
        return FALSE;
    if (p_key)
        *p_key = KL(get)(im->keys, index);
    if (p_list)
        *p_list = pl_get(im->lists, index);
    return TRUE;
}

#undef IMGLUE2
#undef IMGLUE
#undef key
#undef nl
#undef KL
