#pragma once

#include "utils/type.h"
#include <stddef.h>

typedef enum LexerToken {
  LEXER_TOKEN_SYMBOL_CLOSE_CURLY_BRACKET,

  LEXER_TOKEN_ORDER0 = LEXER_TOKEN_SYMBOL_CLOSE_CURLY_BRACKET,

  LEXER_TOKEN_SYMBOL_CLOSE_PARENTHESIS,
  LEXER_TOKEN_ORDER1 = LEXER_TOKEN_SYMBOL_CLOSE_PARENTHESIS,
  LEXER_TOKEN_SYMBOL_CLOSE_BRACKET,
  LEXER_TOKEN_IDENTIFIER,
  LEXER_TOKEN_BUILTIN,
  LEXER_TOKEN_KEYWORD_TYPE,
  LEXER_TOKEN_KEYWORD_VOID,
  LEXER_TOKEN_KEYWORD_I8,
  LEXER_TOKEN_KEYWORD_U8,
  LEXER_TOKEN_KEYWORD_I16,
  LEXER_TOKEN_KEYWORD_U16,
  LEXER_TOKEN_KEYWORD_I32,
  LEXER_TOKEN_KEYWORD_U32,
  LEXER_TOKEN_KEYWORD_I64,
#ifdef FLOAT_16_SUPPORT
  LEXER_TOKEN_KEYWORD_F16,
#endif
  LEXER_TOKEN_KEYWORD_F32,
  LEXER_TOKEN_KEYWORD_F64,
  LEXER_TOKEN_KEYWORD_F128,
  LEXER_TOKEN_KEYWORD_U64,
  LEXER_TOKEN_KEYWORD_BOOL,
  LEXER_TOKEN_KEYWORD_TRUE,
  LEXER_TOKEN_KEYWORD_FALSE,
  LEXER_TOKEN_KEYWORD_NULL,
  LEXER_TOKEN_NUMBER,
  LEXER_TOKEN_CHAR,
  LEXER_TOKEN_STRING,
  LEXER_TOKEN_KEYWORD_UNDEFINED,

  LEXER_TOKEN_SYMBOL_FUNCTION_ARROW,
  LEXER_TOKEN_ORDER2 = LEXER_TOKEN_SYMBOL_FUNCTION_ARROW,
  LEXER_TOKEN_SYMBOL_POINTER,
  LEXER_TOKEN_KEYWORD_STRUCT,
  LEXER_TOKEN_SYMBOL_CLOSE_BRACKET_LEFT,

  LEXER_TOKEN_SYMBOL_DEREFERENCE,
  LEXER_TOKEN_ORDER3 = LEXER_TOKEN_SYMBOL_DEREFERENCE,
  LEXER_TOKEN_SYMBOL_ACCESS,

  LEXER_TOKEN_SYMBOL_PLUS,
  LEXER_TOKEN_ORDER4 = LEXER_TOKEN_SYMBOL_PLUS,
  LEXER_TOKEN_SYMBOL_MINUS,
  LEXER_TOKEN_SYMBOL_ADDRESS,
  LEXER_TOKEN_SYMBOL_LOGICAL_NOT,

  LEXER_TOKEN_SYMBOL_MULTIPLY,
  LEXER_TOKEN_ORDER5 = LEXER_TOKEN_SYMBOL_MULTIPLY,
  LEXER_TOKEN_SYMBOL_DIVIDE,
  LEXER_TOKEN_SYMBOL_MODULO,

  LEXER_TOKEN_SYMBOL_SUM,
  LEXER_TOKEN_ORDER6 = LEXER_TOKEN_SYMBOL_SUM,
  LEXER_TOKEN_SYMBOL_SUB,

  LEXER_TOKEN_SYMBOL_EQUAL,
  LEXER_TOKEN_ORDER7 = LEXER_TOKEN_SYMBOL_EQUAL,
  LEXER_TOKEN_SYMBOL_NOT_EQUAL,
  LEXER_TOKEN_SYMBOL_GREATER,
  LEXER_TOKEN_SYMBOL_SMALLER,
  LEXER_TOKEN_SYMBOL_GREATER_OR_EQUAL,
  LEXER_TOKEN_SYMBOL_SMALLER_OR_EQUAL,

  LEXER_TOKEN_SYMBOL_LOGICAL_AND,
  LEXER_TOKEN_ORDER8 = LEXER_TOKEN_SYMBOL_LOGICAL_AND,
  LEXER_TOKEN_SYMBOL_LOGICAL_OR,

  LEXER_TOKEN_SYMBOL_COLON,
  LEXER_TOKEN_ORDER9 = LEXER_TOKEN_SYMBOL_COLON,

  LEXER_TOKEN_SYMBOL_ASSIGN,
  LEXER_TOKEN_ORDER10 = LEXER_TOKEN_SYMBOL_ASSIGN,
  LEXER_TOKEN_SYMBOL_SUM_ASSIGN,
  LEXER_TOKEN_SYMBOL_SUB_ASSIGN,
  LEXER_TOKEN_SYMBOL_MULTIPLY_ASSIGN,
  LEXER_TOKEN_SYMBOL_DIVIDE_ASSIGN,
  LEXER_TOKEN_SYMBOL_MODULO_ASSIGN,

  LEXER_TOKEN_KEYWORD_RETURN,
  LEXER_TOKEN_ORDER11 = LEXER_TOKEN_KEYWORD_RETURN,
  LEXER_TOKEN_KEYWORD_PUTC,
  LEXER_TOKEN_KEYWORD_COMPTIME,

  LEXER_TOKEN_SYMBOL_EOL,
  LEXER_TOKEN_ORDER12 = LEXER_TOKEN_SYMBOL_EOL,
  LEXER_TOKEN_SYMBOL_COMMA,

  LEXER_TOKEN_KEYWORD_IF,
  LEXER_TOKEN_ORDER13 = LEXER_TOKEN_KEYWORD_IF,
  LEXER_TOKEN_KEYWORD_WHILE,

  LEXER_TOKEN_KEYWORD_ELSE,
  LEXER_TOKEN_END_ORDERS = LEXER_TOKEN_KEYWORD_ELSE,

  LEXER_TOKEN_SYMBOL,
  LEXER_TOKEN_SYMBOL_OPEN_PARENTHESIS,
  LEXER_TOKEN_SYMBOL_OPEN_BRACKET,
  LEXER_TOKEN_SYMBOL_OPEN_CURLY_BRACKET,

  LEXER_TOKEN_NONE,
} LexerToken;

extern const char *LEXER_TOKEN_STRINGS[];

extern const char *LEXER_SYMBOL_STRINGS[];
extern const LexerToken LEXER_SYMBOL_TOKENS[];
extern const size_t LEXER_SYMBOL_SIZE;

extern const char *LEXER_KEYWORD_STRINGS[];
extern const LexerToken LEXER_KEYWORD_TOKENS[];
extern const size_t LEXER_KEYWORD_SIZE;

struct ParserNode;

typedef struct LexerNode {
  char *str_begin;
  char *str_end;
  LexerToken token;
  struct ParserNode *parserNode;
} LexerNode;

typedef struct LexerNodeArray {
  LexerNode *data;
  size_t size;
} LexerNodeArray;

extern const LexerNodeArray LEXER_NODE_ARRAY_ERROR;

extern bool lexerNodeArrayIsError(LexerNodeArray array);
extern void lexerNodeArrayPrint(LexerNodeArray array);
extern void lexerNodeArrayDestroy(LexerNodeArray array);

extern LexerNodeArray lexer(char *str);

extern void lexerPushClear(LexerNodeArray *array, size_t *array_size,
                           char *iter, char **node_str_begin,
                           LexerToken *node_token, LexerToken token);

extern bool isIdentifier(char c);
extern bool isNumber(char c);
extern bool isSymbol(char c);
extern bool isCompleteSymbol(char *str, size_t str_size);
extern bool isSpace(char c);
extern bool isString(char c);