#include "window.h"


Window::~Window() {
  glfwDestroyWindow(gl_window);
  glfwTerminate();
}

bool setup_window(Window *window) {
  // Initialize GLFW
  glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_WAYLAND);

  if (!glfwInit()) {
    printf("GLFW initialization failed!");
    glfwTerminate();
    return false;
  }

  // Setup GLFW window properties
  // OpenGL version
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
  // Core profile means it will not be backwards compatible
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

  window->gl_window = glfwCreateWindow(window->width, window->height, "Test Window", nullptr, nullptr);

  if (!window->gl_window) {
    const char *desc;
    int code = glfwGetError(&desc);
    printf("GLFW window creation failed!");
    printf("GLFW error code: %d, description: %s\n", code,
           desc ? desc : "No description given");
    glfwTerminate();
    return false;
  }

  // Get Buffer size information
  glfwGetFramebufferSize(window->gl_window, &window->buffer_width, &window->buffer_height);

  // Set context for GLEW to use
  glfwMakeContextCurrent(window->gl_window);

  glfwSetKeyCallback(window->gl_window, handle_keys);
  glfwSetCursorPosCallback(window->gl_window, handle_mouse);
  // Captures the mouse
  glfwSetInputMode(window->gl_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

  // Allow modern extension features
  glewExperimental = GL_TRUE;

  GLenum err = glewInit();
  if (err != GLEW_OK && err != GLEW_ERROR_NO_GLX_DISPLAY) {
    printf("OpenGL version: %s\n", glGetString(GL_VERSION));
    fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    printf("glewInit() returned: %d\n", err);
    glfwDestroyWindow(window->gl_window);
    glfwTerminate();
    return false;
  }

  glEnable(GL_DEPTH_TEST);

  // Setup viewport size
  glViewport(0, 0, window->buffer_width, window->buffer_height);

  glfwSetWindowUserPointer(window->gl_window, window);

  return true;
}

void handle_keys(GLFWwindow *gl_window, int key, int code, int action, int mode) {
    Window *window = static_cast<Window *>(glfwGetWindowUserPointer(gl_window));

    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
      glfwSetWindowShouldClose(gl_window, GL_TRUE);
    }

    if (key >= 0 && key < 1024) {
      if (action == GLFW_PRESS) {
        window->ascii_keys[key] = true;
      } else if (action == GLFW_RELEASE) {
        window->ascii_keys[key] = false;
      }
    }
}

void handle_mouse(GLFWwindow *gl_window, f64 x_position, f64 y_position) {
    Window *window = static_cast<Window *>(glfwGetWindowUserPointer(gl_window));

    if (window->first_mouse_movement) {
      window->last_x_position = x_position;
      window->last_y_position = y_position;
      window->first_mouse_movement = false;
    }

    window->x_change_position = x_position - window->last_x_position;
    // The order here is to avoid up/down inversion
    window->y_change_position = window->last_y_position - y_position;

    window->last_x_position = x_position;
    window->last_y_position = y_position;

    // @Todo: reset x/y_change_position back to 0.0f
}

