/**
 * @file symtab.h
 * @brief Declares a fixed-capacity open-addressed string-keyed table.
 *
 * The Go reference used five maps. Three of them -- an environment's variables,
 * functions and labels -- become this: one implementation, capacity supplied by
 * the caller, keyed by aksl_strhash_djb2 with linear probing. A full table is an
 * error, not a resize.
 */

#ifndef _AKBASIC_SYMTAB_H_
#define _AKBASIC_SYMTAB_H_

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

#include <akerror.h>

#include <akbasic/types.h>

/*
 * Slots are sized for the largest table any caller needs, so one slot array type
 * serves variables, functions and labels. Capacity is a member; the table is
 * kept below a 75% load factor by construction because `capacity` counts slots
 * and the caller's logical maximum is smaller.
 *
 * 172 rather than the old 256: no caller passes akbasic_symtab_init() anything
 * larger than AKBASIC_MAX_VARIABLES (128), and 172 keeps that under the 75%
 * load factor the comment above promises (128 / 0.75 = 170.7).
 *
 * AKBASIC_SYMTAB_MAX_KEY 24 rather than 64: the longest identifier across
 * examples/breakout and examples/megademo -- variables, DIMmed arrays, labels
 * and DEF FN names alike -- is 11 characters (TITLESCREEN, CLEARPOWERS).
 */
#define AKBASIC_SYMTAB_MAX_SLOTS	172
#define AKBASIC_SYMTAB_MAX_KEY		24

typedef struct
{
    bool used;
    char key[AKBASIC_SYMTAB_MAX_KEY];
    void *value;
    int64_t ivalue;
} akbasic_SymbolSlot;

typedef struct
{
    int capacity;
    int count;
    akbasic_SymbolSlot slots[AKBASIC_SYMTAB_MAX_SLOTS];
} akbasic_SymbolTable;

/**
 * @brief Symbol table initialize.
 * @param obj Object to initialize, inspect, or modify.
 * @param capacity Number of slots to use; must be positive and no larger than AKBASIC_SYMTAB_MAX_SLOTS.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When `obj` is NULL.
 * @throws AKBASIC_ERR_BOUNDS When `capacity` is out of range.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_symtab_init(akbasic_SymbolTable *obj, int capacity);

/**
 * @brief Store a pointer and an integer against a key, replacing any existing entry.
 * @param obj Object to initialize, inspect, or modify.
 * @param key Lookup key; copied into the slot.
 * @param value Pointer payload, may be NULL.
 * @param ivalue Integer payload, used by the label table.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When `obj` or `key` is NULL.
 * @throws AKBASIC_ERR_BOUNDS When the key is too long or the table is full.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_symtab_set(akbasic_SymbolTable *obj, const char *key, void *value, int64_t ivalue);

/**
 * @brief Look a key up.
 * @param obj Object to initialize, inspect, or modify.
 * @param key Lookup key.
 * @param value Output destination for the pointer payload; may be NULL.
 * @param ivalue Output destination for the integer payload; may be NULL.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When `obj` or `key` is NULL.
 * @throws AKERR_KEY When the key is not present.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_symtab_get(akbasic_SymbolTable *obj, const char *key, void **value, int64_t *ivalue);

/**
 * @brief Drop every entry.
 * @param obj Object to initialize, inspect, or modify.
 * @return `NULL` on success, otherwise an error context owned by the caller.
 * @throws AKERR_NULLPOINTER When `obj` is NULL.
 */
akerr_ErrorContext AKERR_NOIGNORE *akbasic_symtab_clear(akbasic_SymbolTable *obj);

#endif // _AKBASIC_SYMTAB_H_
