// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

// symtab.h -- DVX BASIC symbol table
//
// Tracks variables, constants, subroutines, functions, and labels
// during compilation. Supports nested scopes (global + one local
// scope per SUB/FUNCTION).
//
// Embeddable: no DVX dependencies, pure C.

#ifndef DVXBASIC_SYMTAB_H
#define DVXBASIC_SYMTAB_H

#include "../compiler/opcodes.h"
#include "../compiler/lexer.h"
#include "../runtime/vm.h"

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

// ============================================================
// Symbol kinds
// ============================================================

typedef enum {
    SYM_VARIABLE,
    SYM_CONST,
    SYM_SUB,
    SYM_FUNCTION,
    SYM_LABEL,
    SYM_TYPE_DEF    // user-defined TYPE
} BasSymKindE;

// BasScopeE moved to opcodes.h (shared with runtime).

// ============================================================
// Symbol entry
// ============================================================

// Symbol names share BAS_MAX_IDENT (vm.h) with every name buffer they are
// copied into; CONST strings are copied straight from a token, so that
// limit is defined in terms of the token buffer.
#define BAS_MAX_PARAMS         16
#define BAS_MAX_CONST_STR      BAS_MAX_TOKEN_LEN  // max CONST string-literal length

// FNV-1a parameters shared by the case-insensitive symbol hash (symtab.c)
// and the constant-pool interning hash (codegen.c).
#define BAS_FNV1A_OFFSET       0x811C9DC5u
#define BAS_FNV1A_PRIME        0x01000193u

// UDT field definition
typedef struct {
    char    name[BAS_MAX_IDENT];
    uint8_t dataType;  // BAS_TYPE_*
    int32_t udtTypeId; // if dataType == BAS_TYPE_UDT, index of the TYPE_DEF symbol
} BasFieldDefT;

typedef struct {
    // Fields are ordered by alignment (4-byte, then 2-byte, then 1-byte) to
    // minimize struct padding on the 32-bit target.
    uint32_t     nameHash;     // FNV-1a over uppercase(name); fast-reject during lookup
    BasSymKindE  kind;
    BasScopeE    scope;
    int32_t      index;        // slot index (local or global)
    int32_t      codeAddr;     // PC address for SUB/FUNCTION/LABEL
    int32_t      localCount;  // number of local variables (for SUB/FUNCTION, set on leave)
    int32_t      udtTypeId;    // for variables of BAS_TYPE_UDT: index of TYPE_DEF symbol
    int32_t      fixedLen;     // for STRING * n: fixed length (0 = variable-length)

    // For SUB/FUNCTION: parameter info
    int32_t      paramCount;
    int32_t      requiredParams;                    // count of non-optional params

    // Forward-reference backpatch list (code addresses to patch when defined)
    int32_t     *patchAddrs;  // stb_ds dynamic array
    int32_t      patchCount;

    // For CONST: the constant value
    union {
        int32_t  constInt;
        double   constDbl;
    };

    // For TYPE_DEF: field definitions
    BasFieldDefT *fields;     // stb_ds dynamic array
    int32_t       fieldCount;

    uint16_t     externLibIdx; // constant pool index for library name (if isExtern)
    uint16_t     externFuncIdx; // constant pool index for function name (if isExtern)

    uint8_t      dataType;     // BAS_TYPE_* for variables/functions
    bool         isDefined;    // false = forward-declared
    bool         isArray;
    bool         isShared;
    bool         isExtern;     // true = external library function (DECLARE LIBRARY)
    bool         formScopeEnded; // true = form scope ended, invisible to lookups
    bool         hasSignature; // true once paramCount/paramTypes describe the real declaration (false for a call-site stub)
    bool         isImplicit;   // variable created by first use (no DIM); STRING locals need a prologue init
    bool         isTyped;      // variable declared with an explicit numeric type (suffix, AS clause or DEFxxx); stores convert to it

    char         name[BAS_MAX_IDENT];
    char         formName[BAS_MAX_IDENT]; // form name for SCOPE_FORM vars
    uint8_t      paramTypes[BAS_MAX_PARAMS];
    bool         paramByVal[BAS_MAX_PARAMS];
    bool         paramOptional[BAS_MAX_PARAMS];     // true = OPTIONAL parameter
    char         constStr[BAS_MAX_CONST_STR];
} BasSymbolT;

// ============================================================
// Symbol table
// ============================================================

typedef struct {
    // Array of POINTERS to heap-allocated symbols (stb_ds array of pointers).
    // Pointers-of-pointers rather than array-of-structs because callers
    // routinely hold a BasSymbolT * across parsing operations that may
    // trigger basSymTabAdd (which grows this array).  An array of structs
    // would be reallocated on growth, invalidating any held pointer.
    // Indirection via a stable heap pointer per symbol avoids that.
    BasSymbolT **symbols;
    int32_t     count;
    int32_t     nextGlobalIdx;    // next global variable slot
    int32_t     nextLocalIdx;     // next local variable slot (reset per SUB/FUNCTION)
    bool        inLocalScope;     // true when inside SUB/FUNCTION
    bool        inFormScope;      // true inside BEGINFORM...ENDFORM
    char        formScopeName[BAS_MAX_IDENT]; // current form name
    int32_t     nextFormVarIdx;   // next form-level variable slot
    int32_t     formScopeSymStart; // symbol count at BEGINFORM (for marking ended)
} BasSymTabT;

// ============================================================
// API
// ============================================================

void basSymTabInit(BasSymTabT *tab);

// Free all symbols and the symbol array (called at parser teardown).
void basSymTabFree(BasSymTabT *tab);

// Add a symbol. Returns the symbol pointer, or NULL if the name already
// exists in the current scope, is too long for BasSymbolT.name, or memory
// allocation fails.  Names are never truncated: a truncated name would
// hash and compare differently from the source spelling.
BasSymbolT *basSymTabAdd(BasSymTabT *tab, const char *name, BasSymKindE kind, uint8_t dataType);

// Look up a symbol by name. Searches local scope first, then global.
// Case-insensitive.
BasSymbolT *basSymTabFind(BasSymTabT *tab, const char *name);

// Look up a symbol in the global scope only.
BasSymbolT *basSymTabFindGlobal(BasSymTabT *tab, const char *name);

// Enter local scope (called at SUB/FUNCTION start).
void basSymTabEnterLocal(BasSymTabT *tab);

// Leave local scope (called at END SUB/FUNCTION). Removes local symbols.
void basSymTabLeaveLocal(BasSymTabT *tab);

// Allocate the next variable slot (global, local, or form depending on scope).
int32_t basSymTabAllocSlot(BasSymTabT *tab);

// Allocate the next global variable slot unconditionally, ignoring the
// current local/form scope flags.  Used when a symbol's final scope is
// SCOPE_GLOBAL but the table is currently inside a SUB/FUNCTION or form.
int32_t basSymTabAllocGlobalSlot(BasSymTabT *tab);

// Enter form scope (called at BEGINFORM). Form-level DIMs create SCOPE_FORM variables.
void basSymTabEnterFormScope(BasSymTabT *tab, const char *formName);

// Leave form scope (called at ENDFORM). Marks form-scope symbols as ended.
// Returns the number of form variables allocated.
int32_t basSymTabLeaveFormScope(BasSymTabT *tab);

#endif // DVXBASIC_SYMTAB_H
