#include "window.h"
#include "logger.h"

bool init_window(Window *window, u32 width, u32 height, string title) {
  glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_WAYLAND);

  if (!glfwInit()) {
    log(1, "%s: glfwInit() error\n", __FUNCTION__);
    glfwTerminate();
    return false;
  }

  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

  window->glfw_window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);

  if (!window->glfw_window) {
    log(1, "%s: Could not create window\n", __FUNCTION__);
    const char *desc;
    int code = glfwGetError(&desc);
    log(1, "GLFW error code: %d, description: %s\n", code,
           desc ? desc : "No description given");
    glfwTerminate();
    return false;
  }

  glfwMakeContextCurrent(window->glfw_window);

  window->renderer = new Renderer();
  window->renderer->render_data.glfw_window = window->glfw_window;
  bool success = init_renderer(window->renderer, width, height);
  if (!success) {
    log(1, "%s: could not initialize renderer\n", __FUNCTION__);
    glfwTerminate();
    return false;
  }

  log(1, "%s: Renderer initialized\n", __FUNCTION__);

  glfwSetWindowUserPointer(window->glfw_window, window->renderer);

  glfwSetWindowSizeCallback(window->glfw_window, [](GLFWwindow *glfw_window, s32 width, s32 height) {
    Renderer* renderer = static_cast<Renderer*>(glfwGetWindowUserPointer(glfw_window));
    set_renderer_size(renderer, width, height);
  });

  glfwSetKeyCallback(window->glfw_window, [](GLFWwindow *glfw_window, s32 key, s32 scancode, s32 action, s32 mods) {
    Renderer* renderer = static_cast<Renderer*>(glfwGetWindowUserPointer(glfw_window));
    handle_key_events(renderer, key, scancode, action, mods);
  });

  glfwSetMouseButtonCallback(window->glfw_window, [](GLFWwindow *glfw_window, s32 button, s32 action, s32 mods) {
    Renderer* renderer = static_cast<Renderer*>(glfwGetWindowUserPointer(glfw_window));
    handle_mouse_button_events(renderer, button, action, mods);
  });

  glfwSetCursorPosCallback(window->glfw_window, [](GLFWwindow *glfw_window, f64 x_pos, f64 y_pos) {
    Renderer* renderer = static_cast<Renderer*>(glfwGetWindowUserPointer(glfw_window));
    handle_mouse_position_events(renderer, x_pos, y_pos);
  });

  window->model = new Model();
  init_model(window->model);

  log(1, "%s: Window initialized\n", __FUNCTION__);
  return true;
}

void main_loop(Window *window) {
  glfwSwapInterval(1);
  upload_renderer_data(window->renderer, &window->model->vertex_data);

  while (!glfwWindowShouldClose(window->glfw_window)) {
    draw_renderer(window->renderer);

    glfwSwapBuffers(window->glfw_window);
    glfwPollEvents();
  }
}

void cleanup(Window *window) {
  log(1, "%s: Terminating Window\n", __FUNCTION__);
  delete window->renderer;
  delete window->model;
  glfwDestroyWindow(window->glfw_window);
  glfwTerminate();
}

void handle_window_resize_events(GLFWwindow *gl_window, s32 width, s32 height) {
  Window *window = static_cast<Window *>(glfwGetWindowUserPointer(gl_window));

  set_renderer_size(window->renderer, width, height);
}
