#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <curl/curl.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

#include "toml.h"
#define MAX_VERTEX_BUFFER 512 * 1024
#define MAX_ELEMENT_BUFFER 128 * 1024
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#define NK_IMPLEMENTATION
#define NK_GLFW_GL3_IMPLEMENTATION
#define NK_KEYSTATE_BASED_INPUT
#include "nuklear.h"
#include "nuklear_glfw_gl3.h"

#include "constants.h"
#include "logger.h"
#include "main.h"
#include "process_url.h"
#include "style.h"
#include "ui.h"
#include "unifont.h"

extern int win_width, win_height;
static char *cookie;
extern void load_ui(struct ui_struct *);
static void error_callback(int e, const char *d) {
  printf("Error %d: %s\n", e, d);
}

int main(void) {
  struct ui_struct ui;

  /* Set locale*/
  setlocale(LC_ALL, ".UTF-8");

  /* Parse config */
  // NOTICE: string parsed from toml should always be freed afterwards!!!
  {
    FILE *fp;
    const char *config_file = "config.toml";
    char errbuf[UCHAR_MAX];

    fp = fopen(config_file, "r");
    if (!fp) {
      fprintf(stderr, "[tomlc99] Cannot open %s, applying default config...\n",
              config_file);
    } else {
      toml_table_t *conf = toml_parse_file(fp, errbuf, sizeof(errbuf));
      fclose(fp);

      if (!conf) {
        fprintf(stderr, "[tomlc99] Cannot parse %s\n", config_file);
      }
      toml_datum_t cookie_datum = toml_string_in(conf, "cookie");
      if (cookie_datum.ok) {
        printf("[tomlc99] Found cookie string in config.toml, applying...\n");
        cookie = cookie_datum.u.s;
      }
    }
  }

  /* Curl */
  curl_init(cookie);

  /* GLFW */
  struct nk_glfw glfw = {0};
  static GLFWwindow *win;
  glfwSetErrorCallback(error_callback);
  if (!glfwInit()) {
    fprintf(stdout, "[GFLW] failed to init!\n");
    exit(EXIT_FAILURE);
  }
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

  win = glfwCreateWindow(win_width, win_height, APP_NAME, NULL, NULL);

  if (!win) {
    // Window creation failed
    fprintf(stdout, "[GLFW] failed to create window. Does this platform "
                    "support OpenGL 3.3+?\n");
    printf("GL_VERSION_3_3: %s",
           glewIsSupported("GL_VERSION_3_3") ? "yes" : "no");
    exit(EXIT_FAILURE);
  }

  glfwMakeContextCurrent(win);

  /* Glew */
  glewExperimental = GL_TRUE;
  GLenum err;
  if ((err = glewInit()) != GLEW_OK) {
    /* Problem: glewInit failed, something is seriously wrong. */
    fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    exit(EXIT_FAILURE);
  }

  /* Logger setup */
  struct logger *logger = setup_logger();
  // Put it in ui_struct
  ui.logger = logger;

  /* UI stat */
  status_t stat = {0};
  stat.is_done = true;
  ui.stat = &stat;

  /* create context */
  ui.ctx = nk_glfw3_init(&glfw, win, NK_GLFW3_INSTALL_CALLBACKS);

  /* Font */
  ui.logger->font_height = 24;
  {
    struct nk_font_atlas *atlas;
    struct nk_font_config cfg = nk_font_config(0);
    /* NOTICE: Some CJK fonts may have incorrect glyph indexes.
      https://github.com/Immediate-Mode-UI/Nuklear/issues/399
      https://github.com/Immediate-Mode-UI/Nuklear/issues/542
      FIX: https://github.com/Immediate-Mode-UI/Nuklear/pull/531
     */
    cfg.range = nk_font_chinese_glyph_ranges();
    // const nk_rune ranges[] = {0x4E00, 0x9FAF, 0x0020, 0x00FF, 0x3000,
    //                           0x30FF, 0x31F0, 0x31FF, 0xFF00, 0xFFEF,
    //
    //                           0};
    // cfg.range = ranges;
    cfg.oversample_h = 1;
    cfg.oversample_v = 1;
    nk_glfw3_font_stash_begin(&glfw, &atlas);
    atlas->default_font = nk_font_atlas_add_compressed_base85(
        atlas, unifont_compressed_data_base85, ui.logger->font_height, &cfg);
    nk_glfw3_font_stash_end(&glfw);
  }

  /* TextEdit */
  struct nk_text_edit text_edit;
  nk_textedit_init_default(&text_edit);

  logger->text_edit = &text_edit;

  /* Scrollbar */
  struct nk_scroll scrollbar = {0};
  logger->scrollbar = &scrollbar;

  /* Theming */
  set_style(ui.ctx, THEME_DARK);

  while (!glfwWindowShouldClose(win)) {
    /* Input */
    glfwPollEvents();
    nk_glfw3_new_frame(&glfw);

    /* Response to window resize */
    glfwGetWindowSize(win, &win_width, &win_height);
    nk_window_set_bounds(ui.ctx, APP_NAME,
                         nk_rect(0, 0, win_width, win_height));

    /* GUI */
    if (nk_begin(ui.ctx, APP_NAME, nk_rect(0, 0, win_width, win_height),
                 NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_TITLE)) {
      load_ui(&ui);
    }
    nk_end(ui.ctx);

    /* Draw */
    glfwGetWindowSize(win, &win_width, &win_height);
    glViewport(0, 0, win_width, win_height);
    glClear(GL_COLOR_BUFFER_BIT);
    nk_glfw3_render(&glfw, NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER,
                    MAX_ELEMENT_BUFFER);
    glfwSwapBuffers(win);
  }
  curl_cleanup(&stat);
  nk_glfw3_shutdown(&glfw);
  glfwTerminate();
  return 0;
}