#ifndef KEKS_POOL_H
#define KEKS_POOL_H

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

#include "atom.h"
#include "err.h"

// TEXINFO: KEKSItem
// @deftp {Data type} {struct KEKSItem}
// Each item contain the @code{.atom} structure. But item can be a part
// of the list or map, so @code{.next} contains the pool index value to
// the next element of the list or map. It equals to 0, if it is the
// last one.
//
// Map is a list of pairs: first value is always an UTF-8 string with
// the key name, next one is its value.
//
// Blob's first element always exists and it is the next binary string
// item, until its length is less than chunk length.
// @end deftp
struct KEKSItem {
    size_t next;
    struct KEKSAtom atom;
};

// TEXINFO: KEKSItems
// @deftp {Data type} {struct KEKSItems}
// Items pool @code{.list} contains concatenated @code{KEKSItem}s. Item's
// @code{.next} can be used as an index in that pool:
// @code{items->list[item.next]}.
//
// Items is just a list of possibly linked lists. You may insert an item
// at arbitrary LIST place by appending it to the @code{.list} and by
// modifying the necessary @code{.next} of the desired preceding
// element.
//
// @code{.cap} and @code{.len} are capacity and length of the
// @code{.list}. If you use @ref{KEKSItemsParse}, then it parses the
// buffer and continuously appends new items to that list. If its length
// matches with the capacity, then @ref{KEKSItemsGrow} is called to
// enlarge the underlying buffer, possibly reallocating the whole of it.
// So be careful with the pointers to the list's items, because they
// could be invalidated after the growth. Using their indices instead
// will be safer.
//
// Corresponding @code{.offsets} store the offset of the decoded item
// relative to the previously provided buffer.
// @end deftp
struct KEKSItems {
    struct KEKSItem *list;
    size_t *offsets;
    size_t len;
    size_t reallocs;
    ptrdiff_t cap;
};

// TEXINFO: KEKSItemsInit
// @deftypefun {enum KEKSErr} KEKSItemsInit @
//     (struct KEKSItems *items, const ptrdiff_t initialLen)
// Initialise the @ref{KEKSItems} structure by allocating an initial
// capacity for the underlying storage.
//
// If you do not want to use heap allocation and want to use
// preallocated fixed buffer, then just point @code{.list} to it and set
// the proper @code{.cap}acity.
//
// If you do not want to store items offsets during decoding, or you use
// @ref{KEKSItems} only for encoding purposes, then set @code{.offsets}
// to NULL (do not forget to free it, if it was initialised before).
// @end deftypefun
enum KEKSErr
KEKSItemsInit(struct KEKSItems *, const ptrdiff_t initialLen);

// TEXINFO: KEKSItemsFree
// @deftypefun void KEKSItemsFree (struct KEKSItems *items)
// Free the @ref{KEKSItems} structure occupied memory.
// @end deftypefun
void
KEKSItemsFree(struct KEKSItems *);

// TEXINFO: KEKSItemsNoOffsets
// @deftypefun void KEKSItemsNoOffsets (struct KEKSItems *items)
// Disable offsets storage during decoding.
// @end deftypefun
void
KEKSItemsNoOffsets(struct KEKSItems *);

// TEXINFO: KEKSItemsGrow
// @deftypefun {enum KEKSErr} KEKSItemsGrow (struct KEKSItems *items)
// Enlarge underlying storage of items, increasing its capacity. If
// @code{.cap} equals to -1, then nothing will happen and
// @code{KEKSErrNoMem} error will be returned. You can use that -1 value
// to omit attempts to call heap allocation functions.
// @end deftypefun
enum KEKSErr
KEKSItemsGrow(struct KEKSItems *);

// TEXINFO: KEKSItemsParse
// @deftypefun {enum KEKSErr} KEKSItemsParse ( @
//     struct KEKSItems *items, @
//     size_t *off, @
//     const unsigned char *buf, @
//     const size_t len)
// Parse the provided @var{buf} appending newly created linked
// @ref{KEKSItem}s in @var{items}. If there is not enough capacity
// available, then @ref{KEKSItemsGrow} is automatically called. If
// @code{.offsets} equal to NULL, then no offsets will be stored.
//
// @code{off}set is a position in @var{buf} from which the parsing will
// be done. Its final value is the position when we stopped.
// @end deftypefun
enum KEKSErr
KEKSItemsParse(
    struct KEKSItems *,
    size_t *off,
    const unsigned char *buf,
    const size_t len);

// TEXINFO: KEKSItemsEncode
// @deftypefun bool KEKSItemsEncode ( @
//     const struct KEKSItems *items, @
//     size_t idx, @
//     size_t *off, @
//     unsigned char *buf, @
//     const size_t cap)
// Encode the previously filled @var{items} to the @var{buf}. @code{idx}
// tells what exact item must be encoded. So you can easily encode only
// the subpart of the @var{items}. @code{off} stored the resulting
// length of the encoded data.
// @end deftypefun
bool
KEKSItemsEncode(
    const struct KEKSItems *,
    size_t idx,
    size_t *off,
    unsigned char *buf,
    const size_t cap);

// TEXINFO: KEKSItemsGetByKeyLen
// @deftypefun size_t KEKSItemsGetByKeyLen ( @
//     const struct KEKSItems *items, @
//     const size_t itemIdx, @
//     const char *key, @
//     const size_t keyLen)
// Get the index of the key with @var{key} name of @var{keyLen} length
// in @var{items}'es element @var{itemIdx}. Returns zero if none found.
// @end deftypefun
size_t
KEKSItemsGetByKeyLen(
    const struct KEKSItems *,
    const size_t itemIdx,
    const char *key,
    const size_t keyLen);

// TEXINFO: KEKSItemsGetByKey
// @deftypefun size_t KEKSItemsGetByKey ( @
//     const struct KEKSItems *items, @
//     const size_t itemIdx, @
//     const char *key)
// Get the index of the null-terminated key with @var{key} name in
// @var{items}'es element @var{itemIdx}. Returns zero if none found.
// @end deftypefun
size_t
KEKSItemsGetByKey(const struct KEKSItems *, const size_t itemIdx, const char *key);

// TEXINFO: KEKSStrEqual
// @deftypefun bool KEKSStrEqual (const struct KEKSAtom *atom, const char *s)
// Returns true if string atom's value equal to null-terminated @var{s}.
// @end deftypefun
bool
KEKSStrEqual(const struct KEKSAtom *, const char *s);

#endif // KEKS_POOL_H
