// 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.

// values.h -- DVX BASIC value representation and string heap
//
// Tagged union value type for the VM's evaluation stack, variables,
// and array elements. Strings are reference-counted for automatic
// memory management without a garbage collector.
//
// Embeddable: no DVX dependencies, pure C.

#ifndef DVXBASIC_VALUES_H
#define DVXBASIC_VALUES_H

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

// ============================================================
// Reference-counted string
// ============================================================

typedef struct {
    int32_t  refCount;
    int32_t  len;
    int32_t  cap;      // allocated capacity (>= len + 1)
    char     data[];   // flexible array member, null-terminated
} BasStringT;

// Maximum string length in bytes (one byte reserved for the null terminator).
#define BAS_STRING_MAX_LEN (INT32_MAX - 1)

// Allocate a new string from a C string. refCount starts at 1.
BasStringT *basStringNew(const char *text, int32_t len);

// Allocate an empty string with a given capacity.
BasStringT *basStringAlloc(int32_t cap);

// Increment reference count.
BasStringT *basStringRef(BasStringT *s);

// Decrement reference count. Frees if count reaches zero.
void basStringUnref(BasStringT *s);

// Concatenate two strings. Returns a new string (refCount 1).
BasStringT *basStringConcat(const BasStringT *a, const BasStringT *b);

// Substring. Returns a new string (refCount 1).
BasStringT *basStringSub(const BasStringT *s, int32_t start, int32_t len);

// Compare two strings. Returns <0, 0, >0 like strcmp.
int32_t basStringCompare(const BasStringT *a, const BasStringT *b);

// Compare two strings case-insensitively. Returns <0, 0, >0.
int32_t basStringCompareCI(const BasStringT *a, const BasStringT *b);

// The empty string singleton (never freed).
extern BasStringT *basEmptyString;

// ============================================================
// Forward declarations
// ============================================================

typedef struct BasValueTag BasValueT;

// ============================================================
// Reference-counted array
// ============================================================

#define BAS_ARRAY_MAX_DIMS 8

typedef struct {
    int32_t    refCount;
    uint8_t    elementType;              // BAS_TYPE_*
    int32_t    dims;                     // number of dimensions
    int32_t    lbound[BAS_ARRAY_MAX_DIMS]; // lower bound per dimension
    int32_t    ubound[BAS_ARRAY_MAX_DIMS]; // upper bound per dimension
    int32_t    totalElements;
    BasValueT *elements;                 // flat array of values
} BasArrayT;

// Allocate a new array. refCount starts at 1.
BasArrayT *basArrayNew(int32_t dims, int32_t *lbounds, int32_t *ubounds, uint8_t elementType);

// Free all elements and release the array.
void basArrayFree(BasArrayT *arr);

// Increment reference count.
BasArrayT *basArrayRef(BasArrayT *arr);

// Decrement reference count. Frees if count reaches zero.
void basArrayUnref(BasArrayT *arr);

// Compute flat index from multi-dimensional indices. Returns -1 if out of bounds.
int32_t basArrayIndex(BasArrayT *arr, int32_t *indices, int32_t ndims);

// ============================================================
// Reference-counted user-defined type instance
// ============================================================

typedef struct {
    int32_t    refCount;
    int32_t    typeId;       // index into type definition table
    int32_t    fieldCount;
    BasValueT *fields;       // array of field values
} BasUdtT;

// Allocate a new UDT instance. refCount starts at 1.
BasUdtT *basUdtNew(int32_t typeId, int32_t fieldCount);

// Free all fields and release the UDT.
void basUdtFree(BasUdtT *udt);

// Increment reference count.
BasUdtT *basUdtRef(BasUdtT *udt);

// Decrement reference count. Frees if count reaches zero.
void basUdtUnref(BasUdtT *udt);

// Deep copy: a new instance (refCount 1) whose fields are copies of the
// source's, nested UDT fields cloned recursively.  Gives TYPE variables
// value semantics on assignment and BYVAL parameter passing.
BasUdtT *basUdtClone(const BasUdtT *udt);

// ============================================================
// Tagged value
// ============================================================

struct BasValueTag {
    uint8_t type;  // BAS_TYPE_*
    union {
        int16_t     intVal;    // BAS_TYPE_INTEGER
        int32_t     longVal;   // BAS_TYPE_LONG
        float       sngVal;    // BAS_TYPE_SINGLE
        double      dblVal;    // BAS_TYPE_DOUBLE
        BasStringT *strVal;    // BAS_TYPE_STRING (ref-counted)
        int16_t     boolVal;   // BAS_TYPE_BOOLEAN (True=-1, False=0)
        BasArrayT  *arrVal;    // BAS_TYPE_ARRAY (ref-counted)
        BasUdtT    *udtVal;    // BAS_TYPE_UDT (ref-counted)
        void       *objVal;    // BAS_TYPE_OBJECT (opaque host pointer)
        BasValueT  *refVal;    // BAS_TYPE_REF (ByRef pointer to variable slot)
        struct {
            BasArrayT *arr;    // BAS_TYPE_ELEM_REF: array holding the element (a counted reference)
            int32_t    idx;    // flat element index into arr->elements
        } elemRef;
    };
};

// Create values.  Out-of-line so they can be resolved via the DXE
// dynamic-symbol table when basrt.lib is loaded.
BasValueT basValInteger(int16_t v);
BasValueT basValLong(int32_t v);
BasValueT basValSingle(float v);
BasValueT basValDouble(double v);
BasValueT basValBool(bool v);
BasValueT basValObject(void *obj);
BasValueT basValString(BasStringT *s);
BasValueT basValStringFromC(const char *text);

// Copy a value (increments string/array/udt refcount if applicable).
BasValueT basValCopy(BasValueT v);

// Release a value (decrements refcount if applicable).
void basValRelease(BasValueT *v);

// Convert a value to a specific type. Returns the converted value.
// The original is NOT released -- caller manages lifetime.
BasValueT basValToInteger(BasValueT v);
BasValueT basValToLong(BasValueT v);
BasValueT basValToSingle(BasValueT v);
BasValueT basValToDouble(BasValueT v);
BasValueT basValToString(BasValueT v);
BasValueT basValToBool(BasValueT v);

// Get the numeric value as a double (for mixed-type arithmetic).
double basValToNumber(BasValueT v);

// Numeric value truncated toward zero and saturated to the int32 range
// (NaN yields 0).  Use for counts, indices and flags read from BASIC
// values where an out-of-range double must not hit the UB cast.
int32_t basValToInt32(BasValueT v);

// Numeric value rounded half-to-even (VB CINT/CLNG semantics) and range
// checked against [lo, hi].  Returns false (and leaves *out untouched)
// when the value is NaN or outside the range -- the caller raises Overflow.
bool basValRoundToInt32(BasValueT v, int32_t lo, int32_t hi, int32_t *out);

// VAL() semantics: leading whitespace, optional sign, decimal digits with
// optional fraction and E/D exponent, or an &H / &O prefixed integer.
// Stops at the first character that cannot continue the number.
double basParseNumber(const char *s);

// Get the string representation. Returns a new ref-counted string.
BasStringT *basValFormatString(BasValueT v);

// Check if a value is truthy (non-zero number, non-empty string).
bool basValIsTruthy(BasValueT v);

// Compare two values. Returns -1, 0, or 1.
// Numeric types are compared numerically. Strings lexicographically.
int32_t basValCompare(BasValueT a, BasValueT b);

// Compare two values case-insensitively (for OPTION COMPARE TEXT).
int32_t basValCompareCI(BasValueT a, BasValueT b);

#endif // DVXBASIC_VALUES_H
