// Kyler Olsen
// YREA SLS
// Lexer Header
// October 2025

#ifndef SLS_LEXER_H
#define SLS_LEXER_H

#include <stddef.h>

#include "sls/bool.h"
#include "sls/errors.h"

#if __SIZEOF_POINTER__ == 4
    // 32-bit system
    #define SLS_INTEGER_TYPE_DEFAULT INTEGER_I32
    #define SLS_FLOAT_TYPE_DEFAULT NUMERIC_F32
#elif __SIZEOF_POINTER__ == 8
    // 64-bit system
    #define SLS_INTEGER_TYPE_DEFAULT INTEGER_I64
    #define SLS_FLOAT_TYPE_DEFAULT NUMERIC_F64
#else
    // Fallback
    #define SLS_INTEGER_TYPE_DEFAULT INTEGER_I16
    #define SLS_FLOAT_TYPE_DEFAULT NUMERIC_F32
#endif

extern const size_t TYPE_NAMES_SAFE_LENGTH;

typedef struct {
    SlsStr filename;
    SlsStr source_code;
    size_t pos;
    size_t column;
    size_t line;
} LexerInfo;

typedef enum {
    TOKEN_EOF,
    TOKEN_IDENTIFIER,
    TOKEN_INTEGER,
    TOKEN_FLOAT,
    TOKEN_DOUBLE,
    TOKEN_CHARACTER,
    TOKEN_STRING,
    TOKEN_BOOLEAN,
    TOKEN_ARRAY,
    TOKEN_TOKEN_STRING,
    TOKEN_TYPE_TUPLE,
} TokenType;

extern const char *TOKEN_TYPES_NAMES[];
extern const size_t TOKEN_TYPE_COUNT;

typedef enum {
    ARRAY_IDENTIFIER,
    ARRAY_I64,
    ARRAY_I32,
    ARRAY_I16,
    ARRAY_I8,
    ARRAY_U64,
    ARRAY_U32,
    ARRAY_U16,
    ARRAY_U8,
    ARRAY_FLOAT,
    ARRAY_DOUBLE,
    ARRAY_CHARACTER,
    ARRAY_STRING,
    ARRAY_BOOLEAN,
    ARRAY_STRUCT_INLINE,
} ArrayType;

extern const char *ARRAY_TYPES_NAMES[];
extern const size_t ARRAY_TYPE_COUNT;

typedef struct {
    SlsStr name;
    Boolean is_literal;
} Identifier;

typedef enum {
    INTEGER_I64,
    INTEGER_I32,
    INTEGER_I16,
    INTEGER_I8,
    INTEGER_U64,
    INTEGER_U32,
    INTEGER_U16,
    INTEGER_U8,
} IntegerBuiltInType;

extern const char *INTEGER_TYPES_NAMES[];
extern const size_t INTEGER_TYPE_COUNT;

typedef struct {
    uint64_t value;
    IntegerBuiltInType type;
} IntegerLiteral;

typedef struct Token Token;

typedef struct {
    Token *tokens;
    size_t length;
} TokenString;

typedef struct {
    Identifier *input_identifiers;
    size_t input_length;
    Identifier *output_identifiers;
    size_t output_length;
} TypeTuple;

typedef struct {
    void **values;
    SlsStr name;
} StructInline;

typedef struct ArrayLiteral {
    ArrayType type;
    union {
        Identifier *identifiers; // type == ARRAY_IDENTIFIER
        uint64_t *integer_literals; // type in { ARRAY_I64, ARRAY_I32, ARRAY_I16, ARRAY_I8, ARRAY_U64, ARRAY_U32, ARRAY_U16, ARRAY_U8, }
        float *float_literals;     // type == ARRAY_FLOAT
        double *double_literals; // type == ARRAY_DOUBLE
        uint8_t *character_literal; // type == ARRAY_CHARACTER
        SlsStr *string_literals; // type == ARRAY_STRING
        Boolean *boolean_literals; // type == ARRAY_BOOLEAN
        TokenString *token_strings; // type == ARRAY_TOKEN_STRING
        TypeTuple *type_tuples; // type == ARRAY_TYPE_TUPLE
        StructInline struct_inline; // type == ARRAY_STRUCT_INLINE
    };
    size_t *shape;
    size_t dimensions;
} ArrayLiteral;

struct Token {
    TokenType type;
    union {
        Identifier identifier; // type == TOKEN_IDENTIFIER
        IntegerLiteral integer_literal; // type == TOKEN_INTEGER
        float float_literal; // type == TOKEN_FLOAT
        double double_literal; // type == TOKEN_DOUBLE
        uint8_t character_literal; // type == TOKEN_CHARACTER
        SlsStr string_literal; // type == TOKEN_STRING
        Boolean boolean_literal; // type == TOKEN_BOOLEAN
        ArrayLiteral array_literal; // type == TOKEN_ARRAY
        TokenString token_string; // type == TOKEN_TOKEN_STRING
        TypeTuple type_tuple; // type == TOKEN_TYPE_TUPLE
    };
};

typedef struct LexerTokenResult {
    SlsResultType type;
    union {
        Token result; // type == SLS_RESULT
        SlsError error; // type == SLS_ERROR
    };
    FileInfo file_info;
    struct LexerTokenResult *next;
} LexerTokenResult;

typedef struct {
    SlsResultType type;
    union {
        LexerTokenResult *result; // type == SLS_RESULT
        SlsError error; // type == SLS_ERROR
    };
} LexerResult;

TokenString copy_token_string(TokenString token_string);
void init_lexer(LexerInfo *lexer_info, SlsStr filename, SlsStr source_code);
LexerTokenResult *get_token(LexerTokenResult *head, size_t i);
void clean_token_string(TokenString token_string);
void clean_token_result(LexerTokenResult *head);
LexerResult lexical_analysis(LexerInfo *lexer_info);

#endif // SLS_LEXER_H
