#ifndef MAP_KEY_TYPE
#error "Need map key type"
#endif

#ifndef MAP_VALUE_TYPE
#error "Need map value type"
#endif

#ifndef MAP_CMP
#error "Need map cmp"
#endif

#ifndef MAP_HASH
#error "Need map hash"
#endif

#ifndef MAP_NAME
#error "Need map name"
#endif

#ifndef MAP_MALLOC
#define MAP_MALLOC malloc
#endif

#ifndef MAP_CALLOC
#define MAP_CALLOC calloc
#endif

#ifndef MAP_REALLOC
#define MAP_REALLOC realloc
#endif

#ifndef MAP_FREE
#define MAP_FREE free
#endif

#define MAP(a) CONTS_JOIN(MAP_NAME, a)

#define MAP_TUPLE MAP(tuple)
#define MAP_NODE MAP(node)
#define MAP_BUCKET MAP(bucket)
#define MAP_ROOT MAP_NAME

#ifndef CONTS_MAP_H
#define CONTS_MAP_H

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

#include "conts.h"

static inline size_t conts_map_generic_hash(const char *s, size_t l)
{
	/* djb2 */
	size_t hash = 5381;
	for (size_t i = 0; i < l; ++i)
		hash = ((hash << 5) + hash) + s[i];

	return hash;
}

#define CONTS_MAP_NO_HASH(a) (a)
#define CONTS_MAP_STR_HASH(a) conts_map_generic_hash(a, strlen(a))

enum conts_map_state {
	CONTS_MAP_FREE = 0,
	CONTS_MAP_USED = 1,
	CONTS_MAP_GRAVE = 2
};

#endif /* CONTS_MAP_H */

static inline size_t MAP(generic_hash)(MAP_KEY_TYPE *key)
{
	return conts_map_generic_hash((const char *)key, sizeof(*key));
}

struct MAP_TUPLE {
	MAP_KEY_TYPE key;
	MAP_VALUE_TYPE data;
};

#define MAP_SPVEC(x) CONTS_JOIN(MAP(spvec), x)
#define MAP_ITER MAP(iter)

typedef struct {
	size_t i;
	struct MAP_TUPLE *t;
} MAP_ITER;

struct MAP_NODE {
	uint32_t used : 2;
	uint32_t hash : 30;
	struct MAP_TUPLE t;
};

/* use spvec as internal buffer representation */
#define SPVEC_TYPE struct MAP_NODE
#define SPVEC_NAME MAP(spvec)

#define SPVEC_MALLOC  MAP_MALLOC
#define SPVEC_CALLOC  MAP_CALLOC
#define SPVEC_REALLOC MAP_REALLOC
#define SPVEC_FREE    MAP_FREE

#include "spvec.h"

struct MAP_ROOT {
	size_t n; /* number of items */
	struct MAP(spvec) buf;
};

static inline struct MAP_ROOT MAP(create)(size_t count)
{
	/* ignore count for now */
	(void)count;
	return (struct MAP_ROOT){
		.n = 0,
		.buf = MAP_SPVEC(create)()
	};
}

static inline void MAP(destroy)(struct MAP_ROOT *root)
{
	MAP_SPVEC(destroy)(&root->buf);
}

static inline MAP_VALUE_TYPE *MAP(find_hash)(struct MAP_ROOT *root, MAP_KEY_TYPE key, uint32_t hash)
{

	for (size_t s = MAP_SPVEC(len)(&root->buf); s >= conts_spvec_size(0); s /= 2) {
		bool base = s == conts_spvec_size(0);
		size_t range = base ? s : s / 2;
		size_t offst = base ? 0 : s / 2;

		/* linear probing for now */
		for (size_t i = 0; i < range; ++i) {
			assert(__builtin_popcountll(range) == 1);
			size_t idx = (hash + i) & (range - 1);

			struct MAP_NODE *n = MAP_SPVEC(at)(&root->buf, idx + offst);

			/* unused node means chain must not exist in this range */
			if (n->used == CONTS_MAP_FREE)
				break;

			/* not the node we're looking for but we should
			 * keep following the chain*/
			if (n->hash != hash)
				continue;

			/* found a match */
			if (MAP_CMP(n->t.key, key) == 0)
				return &n->t.data;
		}
	}

	return NULL;
}

static inline MAP_VALUE_TYPE *MAP(find)(struct MAP_ROOT *root, MAP_KEY_TYPE key)
{
	uint32_t hash = MAP_HASH(key) & 0x3fffffff;
	return MAP(find_hash)(root, key, hash);
}

static inline MAP_VALUE_TYPE *MAP(insert)(struct MAP_ROOT *root, MAP_KEY_TYPE key, MAP_VALUE_TYPE data)
{
	/* mask top bit away to get 31 bit hash value as used by the nodes */
	uint32_t hash = MAP_HASH(key) & 0x3fffffff;
	MAP_VALUE_TYPE *exists = MAP(find_hash)(root, key, hash);
	if (exists)
		return exists;

	/* initialize */
	if (root->n == 0) {
		if (!MAP_SPVEC(reserve)(&root->buf, conts_spvec_size(0)))
			return NULL;
	}

	/* grow as needed */
	size_t s = MAP_SPVEC(len)(&root->buf);
	if (2 * root->n >= s) {
		s *= 2;
		if (!MAP_SPVEC(reserve)(&root->buf, s))
			return NULL;

		/* reserve should zero out all memory so no need to initialize
		 * any elements */
	}

	/* there must always be at least one free spot */
	root->n++;
	bool base = s == conts_spvec_size(0);
	size_t range = base ? s : s / 2;
	size_t offst = base ? 0 : s / 2;

	/* linear probing for now */
	for (size_t i = 0; i < range; ++i) {
		assert(__builtin_popcountll(range) == 1);
		size_t idx = (hash + i) & (range - 1);

		struct MAP_NODE *n = MAP_SPVEC(at)(&root->buf, idx + offst);
		if (n->used == CONTS_MAP_USED)
			continue;

		/* grave or free, either way we're taking it */
		n->used = CONTS_MAP_USED;
		n->hash = hash;
		n->t.key = key;
		n->t.data = data;
		return &n->t.data;
	}

	/** @todo use __builtin_unreachable or C attributes? */
	assert(false && "should be unreachable");
	return NULL;
}

static inline void MAP(remove_found)(struct MAP_ROOT *root, MAP_VALUE_TYPE *data)
{
	struct MAP_TUPLE *tuple = CONTAINER_OF(data, struct MAP_TUPLE, data);
	struct MAP_NODE *node = CONTAINER_OF(tuple, struct MAP_NODE, t);
	node->used = CONTS_MAP_GRAVE;
	root->n--;
}

static inline void MAP(remove)(struct MAP_ROOT *root, MAP_KEY_TYPE key)
{
	MAP_VALUE_TYPE *found = MAP(find)(root, key);
	if (!found)
		return;

	MAP(remove_found)(root, found);
}

static inline MAP_ITER MAP(next)(struct MAP_ROOT *root, MAP_ITER t)
{
	for (size_t i = t.i + 1; i < MAP_SPVEC(len)(&root->buf); ++i) {
		struct MAP_NODE *n = MAP_SPVEC(at)(&root->buf, i);
		if (n->used == CONTS_MAP_USED) {
			return (MAP_ITER){
				.i = i,
				.t = &n->t
			};
		}
	}

	/* no more elements to search through */
	return (MAP_ITER){
		.i = MAP_SPVEC(len)(&root->buf),
		.t = NULL
	};
}

static inline MAP_ITER MAP(begin)(struct MAP_ROOT *root)
{
	if (root->n == 0) {
		return (MAP_ITER){
			.i = MAP_SPVEC(len)(&root->buf),
			.t = NULL
		};
	}

	struct MAP_NODE *n = MAP_SPVEC(at)(&root->buf, 0);
	assert(n);

	MAP_ITER t = {.i = 0, .t = &n->t};
	if (n->used == CONTS_MAP_USED)
		return t;

	return MAP(next)(root, t);
}

static inline bool MAP(end)(struct MAP_ROOT *root, MAP_ITER t)
{
	return t.i >= MAP_SPVEC(len)(&root->buf);
}

static inline size_t MAP(len)(struct MAP_ROOT *root)
{
	return root->n;
}

#undef MAP
#undef MAP_NODE
#undef MAP_TUPLE
#undef MAP_BUCKET
#undef MAP_ROOT
#undef MAP_KEY_TYPE
#undef MAP_VALUE_TYPE
#undef MAP_CMP
#undef MAP_HASH
#undef MAP_NAME
#undef MAP_ITER

#undef MAP_MALLOC
#undef MAP_CALLOC
#undef MAP_REALLOC
#undef MAP_FREE
