#pragma once
#include "type.h"
#define mapValue_null ((mapValue){.p=nullptr})

#ifndef _mapValue_size
#define _mapValue_size ( \
	sizeof(intmax_t) > sizeof(double) ? \
	sizeof(intmax_t) : sizeof(double) \
)
#endif
	
typedef union mapValue {
	/* ensure proper alignment & offer convenience 
	 * handles for punning */
	size_t      sz;
	ptrdiff_t   ofs;
	intptr_t    ip;
	uintmax_t   u;
	intmax_t    s;
	double      d;
	void   *    p;     void    const* p_ro;
	char   *    str;   char    const* str_ro;
	uint8_t*    bytes; uint8_t const* bytes_ro;
	/* maps can pack very small structures directly into
	 * the entry. if you use this feature, either define
	 * _mapValue_size as a project global yourself, or
	 * check to make sure your values fit into it on a
	 * given architecture. i think this is guaranteed
	 * to have at least 8 bytes of space (64-types are
	 * mandated as of C11, possibly earleir?) but i'm
	 * not 100% on that.*/
	char        str_pak   [_mapValue_size];
	uint8_t     bytes_pak [_mapValue_size];
} mapValue;

typedef struct mapLink {
	struct mapLink* next, * prev;
	mapValue ref;
	strv key;
} mapLink;

typedef struct mapBox {
	mapLink* head, * tail;
} mapBox;

typedef typeof(bool (*)(mapValue, void*)) mapPredicate;
typedef typeof(void (*)(mapValue))        mapDTor;

typedef struct map {
	size_t sz;
	//bool weak;
	mapDTor dtor;
	mapBox boxes [];
} map;

typedef struct mapResult {
	map* map;
	mapBox* box;
	mapLink* link;
} mapResult;


map*      mapNew(size_t init_sz);
mapBox*   mapBoxFor(map* map, strp key);
void      mapBoxInsert(mapBox* b, mapLink* l);
mapLink*  mapEnter(map* m, strp key, mapValue val);
bool      mapClobber(map* m, strp key, mapValue val, mapDTor dtor);
mapResult mapFind(map* map, strp key);
mapValue  mapRef(map* m, strp key);
mapResult mapRFindPred(map* map, void* arg, mapPredicate match);
mapResult mapRFind(map* m, mapValue val);
strp      mapRRefPred(map* m, void* val, mapPredicate p);
strp      mapRRef(map* m, mapValue val);
void      mapErase(map* map, strp key);
map*      mapDel(map* map);
map*      mapResize(map* map, size_t newsz);
