#pragma once
#include "../libs/tree-sitter/lib/include/tree_sitter/api.h"
#include "constants.h"
#include "node.h"
#include "snake.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct Node Node;

typedef struct Coords {
  size_t row;
  size_t col;
} Coords;

typedef struct Cluster {
  char *utf8;
  uint8_t width;
} Cluster;

typedef struct Highlight {
  Color *fg;
  Color *bg;
  uint8_t flags;
  size_t start_col;
  size_t end_col;
} Highlight;

typedef struct Line {
  Cluster *clusters;
  size_t cluster_count;
  Highlight *highlights;
  size_t highlight_count;
} Line;

typedef struct VirtLine {
  Line **lines;
  size_t line_count;
  _Bool is_tmp;
} VirtLine;

typedef struct Fold {
  size_t start;
  size_t end;
  uint32_t hash;
  _Bool folded;
} Fold;

typedef struct ColHighlight {
  Color *fg;
  Color *bg;
  uint8_t flags;
} ColHighlight;

typedef void (*HlFunc)(Node *e);
typedef void (*LspUpdateFunc)(Node *e);
typedef void (*LspChangeFunc)(Node *e, size_t start_line, size_t start_col,
                              size_t end_line, size_t end_col,
                              const char *new_text);

typedef struct BufferData {
  Line **lines;
  VirtLine **virtual_lines;
  Fold *folds;
  TSTree *tree;
  TSParser *parser;
  uint8_t lang;
  _Bool selecting;
  size_t fold_count;
  size_t line_count;
  size_t cursor_row;
  size_t cursor_col;
  size_t scroll_row;
  size_t scroll_col;
  size_t desired_col;
  size_t selection_start_row;
  size_t selection_start_col;
  LspUpdateFunc lsp_update_func;
  LspChangeFunc lsp_change_func;
} BufferData;

struct Node {
  uint8_t type;
  Node *parent;
  union {
    struct {
      Node **children;
      uint8_t count;
      uint8_t size;
      uint8_t calc_x;
      uint8_t calc_y;
      uint8_t calc_rows;
      uint8_t calc_cols;
      _Bool hidden;
      _Bool direction;
    } Block;
    struct {
      uint8_t size;
      Color *fg;
      Color *bg;
      uint8_t calc_x;
      uint8_t calc_y;
      uint8_t calc_rows;
      uint8_t calc_cols;
      BufferData *data;
      HlFunc hl_func_basic;
      HlFunc hl_func_syntax;
      char *filename;
      char *folder;
      _Bool hidden;
    } Buffer;
    struct {
      uint8_t row;
      uint8_t col;
      uint8_t width;
      uint8_t height;
      Line *lines;
      uint8_t line_count;
    } Bubble;
    struct {
      uint8_t x;
      uint8_t y;
      uint8_t width;
      uint8_t height;
      Color *border_bg;
      Color *border_fg;
      Node *root_block_node;
      _Bool visible;
      _Bool border;
    } Window;
    struct {
      Node **windows;
      Node *focused;
      uint8_t count;
    } Root;
  };
};

extern Node root;

void init_root_node();
void draw_buffer(Node *node);
Node *create_buffer(Node *parent_block, const Color *fg, const Color *bg,
                    uint8_t size, BufferData *data);
Node *create_window(uint8_t x, uint8_t y, uint8_t width, uint8_t height,
                    _Bool border, _Bool direction);
void free_all_nodes(Node *node);
void calculate_block(Node *block);
Node *create_block(Node *parent_block, _Bool direction, uint8_t size);
Node *create_bubble(int row, int col, int width, int height, char *text);
void draw_bubble(Node *node);
