/**
 * @file types.h
 * @brief Declares the interpreter's fixed budget and its primitive value type.
 */

#ifndef _AKBASIC_TYPES_H_
#define _AKBASIC_TYPES_H_

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

/*
 * The budget, transcribed from the Go reference's main.go:14-30. Everything the
 * interpreter uses is drawn from a pool sized by one of these; nothing calls
 * malloc.
 */

/* Per-environment pools */
#define AKBASIC_MAX_LEAVES		32	/* ~16 operations per source line */
/*
 * Arguments one call may carry. A line holds 32 tokens, so a call site cannot
 * spell more than a handful anyway; this is the ceiling on the array a call
 * builds them in, and it is checked rather than assumed.
 */
#define AKBASIC_MAX_CALL_ARGUMENTS	16
#define AKBASIC_MAX_TOKENS		32
#define AKBASIC_MAX_VALUES		64
#define AKBASIC_MAX_VARIABLES		128

/*
 * Whole-runtime pools.
 *
 * AKBASIC_MAX_SOURCE_LINES, AKBASIC_MAX_LINE_LENGTH, AKBASIC_MAX_ARRAY_VALUES,
 * AKBASIC_MAX_ENVIRONMENTS and AKBASIC_MAX_FUNCTIONS were cut from their
 * original values against measurements taken off examples/breakout and
 * examples/megademo, the two most demanding programs this interpreter runs --
 * see the memory-footprint discussion this commit's PR body links. Each is
 * sized at roughly 1.5-2x the peak the reference corpus actually reaches, not
 * at the peak itself.
 *
 * AKBASIC_MAX_LINE_LENGTH in particular follows Commodore BASIC's own 80-column
 * line limit rather than a measurement, which is why sink_stdio.c now refuses a
 * line that fills the buffer with no terminator instead of silently truncating
 * it: at 256 bytes that failure mode was theoretical, and at 80 it is not.
 *
 * AKBASIC_MAX_SOURCE_PATH_LENGTH is its own constant rather than a reuse of
 * AKBASIC_MAX_LINE_LENGTH, which is where it used to come from. `sourcepath`
 * (runtime.h) holds a directory, not a line of BASIC, and the two ideas do not
 * scale together: shrinking the line limit to 80 broke every golden test in
 * this checkout, because this repository's own working directory is deeper
 * than that. PATH_MAX is the actual bound a filesystem path is subject to, so
 * it is the one this borrows.
 */
#define AKBASIC_MAX_SOURCE_LINES	2048
#define AKBASIC_MAX_LINE_LENGTH		80	/* Commodore BASIC's own line limit */
#define AKBASIC_MAX_SOURCE_PATH_LENGTH	PATH_MAX
#define AKBASIC_MAX_ARRAY_DEPTH		64	/* dimensions per array */
#define AKBASIC_MAX_ARRAY_ELEMENTS	1024	/* elements in one array */
#define AKBASIC_MAX_ARRAY_VALUES	2048	/* array elements across all variables */
#define AKBASIC_MAX_STRING_LENGTH	256	/* see TODO.md 1.2 */
#define AKBASIC_MAX_ENVIRONMENTS	12	/* new: Go allocated these unbounded */
#define AKBASIC_MAX_FUNCTIONS		8	/* new: Go used an unbounded map */
#define AKBASIC_MAX_LABELS		64	/* new: Go used an unbounded map */
/*
 * Leaves a DO/LOOP condition may use. Its own small pool rather than a second
 * AKBASIC_MAX_LEAVES one, because a leaf carries two 256-byte strings and this
 * is per environment: `DO WHILE A# = 1 AND B# = 2` is six leaves, and sixteen
 * leaves nobody uses on every one of 32 scopes is 250 KB of nothing.
 */
#define AKBASIC_MAX_CONDITION_LEAVES	16

/*
 * Structure types. These bound the *descriptors* -- names and slot offsets --
 * and not the data: an instance's fields are ordinary values drawn from the same
 * AKBASIC_MAX_ARRAY_VALUES pool an array uses, because a declared TYPE has a
 * known slot count and so is laid out exactly as an array is.
 */
#define AKBASIC_MAX_STRUCT_TYPES	16	/* distinct TYPE declarations	*/
#define AKBASIC_MAX_STRUCT_FIELDS	16	/* fields in one TYPE		*/
#define AKBASIC_MAX_STRUCT_NAME		32	/* a type or field name		*/
/*
 * How deep a chain of by-value nesting may go. A TYPE cannot contain itself by
 * value -- that is refused at declaration -- so this is not what stops infinite
 * recursion; it bounds the *rendering* of a structure, where a pointer can make
 * the graph cyclic and PRINT would otherwise not come back.
 */
#define AKBASIC_MAX_STRUCT_DEPTH	4

/* Commodore convention: true is -1, not 1. */
#define AKBASIC_TRUE			-1
#define AKBASIC_FALSE			0

/** @brief Runtime execution modes. */
#define AKBASIC_MODE_REPL		1
#define AKBASIC_MODE_RUN		2
#define AKBASIC_MODE_RUNSTREAM		3
#define AKBASIC_MODE_QUIT		4

/** @brief The type carried by a value, taken from an identifier's suffix. */
typedef enum
{
    AKBASIC_TYPE_UNDEFINED = 0,
    AKBASIC_TYPE_INTEGER,		/* 1 -- identifier suffix '#' */
    AKBASIC_TYPE_FLOAT,			/* 2 -- identifier suffix '%' */
    AKBASIC_TYPE_STRING,		/* 3 -- identifier suffix '$' */
    AKBASIC_TYPE_BOOLEAN,		/* 4 */
    AKBASIC_TYPE_STRUCT,		/* 5 -- identifier suffix '@' */
    AKBASIC_TYPE_POINTER		/* 6 -- identifier suffix '@', declared PTR TO */
} akbasic_Type;

/*
 * A value. The string lives inline rather than behind a pointer so that a copy
 * is a struct assignment with no allocator, refcount or lifetime question --
 * see TODO.md 1.2 for the size tradeoff that buys.
 *
 * The reference's BasicValue carries a `name` field. It is dead: nothing ever
 * writes it a non-empty string, and its only reader is BasicEnvironment.update(),
 * which has no callers. Both are dropped here.
 */
typedef struct akbasic_Value
{
    akbasic_Type valuetype;
    char stringval[AKBASIC_MAX_STRING_LENGTH];
    int64_t intval;
    double floatval;
    int64_t boolvalue;
    bool mutable_;

    /*
     * What a STRUCT or POINTER value carries. Not a payload: a *reference* to
     * one, because an instance is a run of slots and cannot fit in the one slot
     * a value occupies.
     *
     * These are their own members rather than a reuse of `intval`, and that is
     * deliberate -- section 6 item 5 was an operator reading a numeric field
     * that happened to be the wrong one, and putting a slot index somewhere
     * arithmetic already looks would set the same trap again.
     *
     * The distinction between the two types is what an *assignment* does with
     * this reference, not what the reference is: a STRUCT copies the slots it
     * points at, and a POINTER copies the reference itself.
     */
    int structtype;			/* index into the type table, -1 for none */
    struct akbasic_Value *structbase;	/* first slot of the instance	          */
    /*
     * The host's own struct, when this describes a host binding. The slots above
     * are then a *shadow*: a read refreshes one from here and a write converts
     * back into here, so a script sees current values and its writes land, with
     * one storage model instead of two.
     */
    void *hostbase;
} akbasic_Value;

#endif // _AKBASIC_TYPES_H_
