#define TB_IMPL
#include "termbox2.h"

// Needs to be included after including termbox2.h
#include "erl_nif.h"

int tui_init = 0;
char *buf;

static ERL_NIF_TERM ex_start(ErlNifEnv *env, int argc,
                             const ERL_NIF_TERM argv[]) {
  buf = enif_alloc(1024 + 1);
  tb_init();
  // tb_set_output_mode(TB_OUTPUT_256);
  tui_init = 1;
  // pthread_create(&event_handler_pt, NULL, event_handler_pthread_routine,
  // NULL);
  return enif_make_int(env, 0);
}

static ERL_NIF_TERM ex_stop(ErlNifEnv *env, int argc,
                            const ERL_NIF_TERM argv[]) {
  tui_init = 0;
  tb_shutdown();
  enif_free(buf);
  return enif_make_int(env, 0);
}

static ERL_NIF_TERM present(ErlNifEnv *env, int argc,
                            const ERL_NIF_TERM argv[]) {
  tb_present();
  return enif_make_int(env, 0);
}

static ERL_NIF_TERM clear_region(ErlNifEnv *env, int argc,
                                 const ERL_NIF_TERM argv[]) {
  int x, y, width, height;
  enif_get_int(env, argv[0], &x);
  enif_get_int(env, argv[1], &y);
  enif_get_int(env, argv[2], &width);
  enif_get_int(env, argv[3], &height);
  for (int i = x; i < x + width; ++i) {
    for (int j = y; j < y + height; ++j) {
      tb_set_cell(i, j, ' ', TB_DEFAULT, TB_DEFAULT);
    }
  }
  return enif_make_int(env, 0);
}

static ERL_NIF_TERM clear(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  tb_clear();
  return enif_make_int(env, 0);
}

static ERL_NIF_TERM width(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  return enif_make_int(env, tb_width());
}

static ERL_NIF_TERM height(ErlNifEnv *env, int argc,
                           const ERL_NIF_TERM argv[]) {
  return enif_make_int(env, tb_height());
}

static ERL_NIF_TERM poll(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  int timeout;
  struct tb_event event = {0};
  enif_get_int(env, argv[0], &timeout);
  int result = tb_peek_event(&event, timeout);
  if (result == TB_OK) {
    ErlNifEnv *env = enif_alloc_env();
    ERL_NIF_TERM result = enif_make_tuple2(
        env, enif_make_atom(env, "event"),
        enif_make_tuple8(
            env, enif_make_uint(env, event.type),
            enif_make_uint(env, event.mod), enif_make_uint(env, event.key),
            enif_make_uint(env, event.ch), enif_make_int(env, event.w),
            enif_make_int(env, event.h), enif_make_int(env, event.x),
            enif_make_int(env, event.y)));
    return result;
  } else {
    return enif_make_atom(env, "none");
  }
}

static ERL_NIF_TERM tb2_puts(ErlNifEnv *env, int argc,
                             const ERL_NIF_TERM argv[]) {
  int x, y, attr;
  enif_get_int(env, argv[0], &x);
  enif_get_int(env, argv[1], &y);
  enif_get_int(env, argv[2], &attr);
  ErlNifBinary bin;
  // NOTE: Cody helped here, chatgpt not so much
  enif_inspect_binary(env, argv[3], &bin);
  int len = bin.size;
  if (len > tb_width() - x) {
    len = tb_width() - x;
  }
  memcpy(buf, bin.data, len);
  buf[len] = '\0';
  if (x >= 0 && x < tb_width() && y >= 0 && y < tb_height()) {
    tb_print(x, y, TB_DEFAULT | attr, TB_DEFAULT, buf);
  }
  // tb_printf(x, y, TB_DEFAULT | TB_BOLD, TB_DEFAULT, buf);
  return enif_make_int(env, 0);
}

static ErlNifFunc nif_funcs[] = {{"present", 0, present, 0},
                                 {"clear", 0, clear, 0},
                                 {"clear_region", 4, clear_region, 0},
                                 {"width", 0, width, 0},
                                 {"height", 0, height, 0},
                                 {"ex_start", 0, ex_start, 0},
                                 {"ex_stop", 0, ex_stop, 0},
                                 {"poll", 1, poll, 0},
                                 {"puts", 4, tb2_puts, 0}};

ERL_NIF_INIT(Elixir.ElementTui.TermBox2Ex, nif_funcs, NULL, NULL, NULL, NULL)
