#pragma once

#include <compiler/parser/parser.h>

#include "utils/types.h"

typedef enum TreeToken {
  TREE_TOKEN_NONE = 0,
  TREE_TOKEN_GLOBAL_SCOPE,
  TREE_TOKEN_LOCAL_SCOPE,
  TREE_TOKEN_FUNCTION_CALL,
  TREE_TOKEN_DEFINE_VARIABLE,
  TREE_TOKEN_DEFINE_CONSTANT,
  TREE_TOKEN_IDENTIFIER,
  TREE_TOKEN_VALUE_STRING,
  TREE_TOKEN_STRUCT,
  TREE_TOKEN_FUNCTION,
} TreeToken;

extern const char *TREE_TOKEN_STRINGS[];

typedef struct ParsedTree {
  char const *strBegin;
  char const *strEnd;
  TreeToken token;
  void *metadata;
} ParsedTree;

typedef struct TreeStructMetadata {
} TreeStructMetadata;

typedef ParsedTree *TypeId;

typedef struct TreeDefineVariableMetadata {
  char const *nameBegin;
  char const *nameEnd;
  TypeId type;
  ParsedTree *value;
  ParsedTree *tree;
} TreeDefineVariableMetadata;

typedef struct TreeFunctionCallMetadata {
  TreeDefineVariableMetadata *function;
  ParsedTree **values;
  size_t values_size;
} TreeFunctionCallMetadata;

typedef struct TreeScopeMetadata {
  ParsedTree **lines;
  size_t lines_size;
  TreeDefineVariableMetadata **variables;
  size_t variables_size;
} TreeScopeMetadata;

typedef struct TreeIdentifierMetadata {
  TreeDefineVariableMetadata *variable;
} TreeIdentifierMetadata;

typedef struct TreeFunctionMetadata {
  TreeDefineVariableMetadata **params;
  size_t params_size;
  TreeScopeMetadata *scope;
  TypeId returnType;
} TreeFunctionMetadata;

typedef SizedString TreeStringValueMetadata;

extern void _printParsedTreeNode(const ParsedTree *parsedTree,int indent);
extern void printParsedTreeNode(const ParsedTree *parsedTree);

extern void deleteParsedTree(ParsedTree *parsedTree);

extern ParsedTree *treeParser(SourceCode code);
extern ParsedTree *_treeParser(const ParsedNode *node, SourceCode code);

extern ParsedTree *treeParseNode(const ParsedNode *node, SourceCode code,
                                 const TreeScopeMetadata *scopes[],
                                 size_t scopes_size);

extern ParsedTree *treeParseRoot(const ParsedNode *root, SourceCode code);
extern ParsedTree *treeParseLocalScope(const ParsedNode *node, SourceCode code,
                                       const TreeScopeMetadata *scopes[],
                                       size_t scopes_size);

extern TreeDefineVariableMetadata *treeParseDefineVariable(
    ParsedTree *tree, const ParsedNode *node, SourceCode code,
    const TreeScopeMetadata *scopes[], size_t scopes_size);
extern TreeDefineVariableMetadata *getVariable(
    const char *strBegin, const char *strEnd, SourceCode code,
    const TreeScopeMetadata *scopes[], size_t scopes_size);
extern ParsedTree *treeParseIdentifier(const ParsedNode *node, SourceCode code,
                                       const TreeScopeMetadata *scopes[],
                                       size_t scopes_size);
extern ParsedTree *treeParseFunctionCall(const ParsedNode *node,
                                         SourceCode code,
                                         const TreeScopeMetadata *scopes[],
                                         size_t scopes_size);
extern ParsedTree *treeParseValueString(const ParsedNode *node,
                                        SourceCode code);
extern ParsedTree *treeParseVariableDefinition(
    const ParsedNode *node, SourceCode code, const TreeScopeMetadata *scopes[],
    size_t scopes_size, TreeToken token);
extern ParsedTree *treeParseStruct(const ParsedNode *node, SourceCode code,
                                   const TreeScopeMetadata *scopes[],
                                   size_t scopes_size);
extern ParsedTree *treeParseFunction(const ParsedNode *node, SourceCode code,
                                     const TreeScopeMetadata *scopes[],
                                     size_t scopes_size);

extern TypeId getTreeExpressionType(ParsedTree *const tree);
extern TypeId getType(const TreeDefineVariableMetadata *define);
extern bool isType(ParsedTree *const tree);
extern SizedString *nodeToString(ParsedNode const *tree, SourceCode code);