// Copyright (c) 2021 Dominic Msters
// 
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

#include "glwfwplatform.h"

game_t *GAME_STATE;

GLFWwindow *window = NULL;

int32_t main() {
  // Attempt to init GLFW
  if(!glfwInit()) return 1;

  // Setup window hints
  glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, false);

  // Create Window
  window = glfwCreateWindow(
    WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT, "", NULL, NULL
  );
  if(!window) {
    glfwTerminate();
    return 1;
  }

  // Load GLAD
  glfwMakeContextCurrent(window);
  glfwSwapInterval(0);
  gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);

  // Setup window listeners
  glfwSetWindowSizeCallback(window, &glfwOnResize);
  glfwSetKeyCallback(window, &glfwOnKey);
  glfwSetErrorCallback(&glfwOnError);
  glfwSetCursorPosCallback(window, &glfwOnCursor);

  // Prepare the game
  game_t *game = malloc(sizeof(game_t));
  GAME_STATE = game;
  input_t *input = &game->engine.input;

  printf("Game is %zu bytes.\n", sizeof(game_t));
  
  // Init the render resolution
  renderSetResolution(&game->engine.render,
    WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT
  );

  // Init the game
  if(gameInit(game)) {
    // Bind initial keys
    inputBind(input, INPUT_NULL,          (inputsource_t)GLFW_KEY_ESCAPE);
    inputBind(input, INPUT_DEBUG_UP,      (inputsource_t)GLFW_KEY_W);
    inputBind(input, INPUT_DEBUG_DOWN,    (inputsource_t)GLFW_KEY_S);
    inputBind(input, INPUT_DEBUG_LEFT,    (inputsource_t)GLFW_KEY_A);
    inputBind(input, INPUT_DEBUG_RIGHT,   (inputsource_t)GLFW_KEY_D);
    inputBind(input, INPUT_DEBUG_RAISE,   (inputsource_t)GLFW_KEY_Q);
    inputBind(input, INPUT_DEBUG_LOWER,   (inputsource_t)GLFW_KEY_E);
    inputBind(input, INPUT_DEBUG_FINE,    (inputsource_t)GLFW_KEY_LEFT_CONTROL);
    inputBind(input, INPUT_DEBUG_PLUS,    (inputsource_t)GLFW_KEY_EQUAL);
    inputBind(input, INPUT_DEBUG_MINUS,   (inputsource_t)GLFW_KEY_MINUS);

    inputBind(input, INPUT_UP,            (inputsource_t)GLFW_KEY_UP);
    inputBind(input, INPUT_DOWN,          (inputsource_t)GLFW_KEY_DOWN);
    inputBind(input, INPUT_LEFT,          (inputsource_t)GLFW_KEY_LEFT);
    inputBind(input, INPUT_RIGHT,         (inputsource_t)GLFW_KEY_RIGHT);
    inputBind(input, INPUT_UP,            (inputsource_t)GLFW_KEY_W);
    inputBind(input, INPUT_DOWN,          (inputsource_t)GLFW_KEY_S);
    inputBind(input, INPUT_LEFT,          (inputsource_t)GLFW_KEY_A);
    inputBind(input, INPUT_RIGHT,         (inputsource_t)GLFW_KEY_D);
    inputBind(input, INPUT_ACCEPT,        (inputsource_t)GLFW_KEY_E);
    inputBind(input, INPUT_ACCEPT,        (inputsource_t)GLFW_KEY_ENTER);
    inputBind(input, INPUT_ACCEPT,        (inputsource_t)GLFW_KEY_SPACE);

    // Bind the fake inputs
    inputBind(input, INPUT_MOUSE_X,       (inputsource_t)INPUT_MOUSE_X);
    inputBind(input, INPUT_MOUSE_Y,       (inputsource_t)INPUT_MOUSE_Y);
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);

    // Update the window title.
    glfwSetWindowTitle(window, SETTING_GAME_NAME);

    double time = 0;

    // Main Render Loop 
    while(!glfwWindowShouldClose(window)) {
      glfwPollEvents();

      // Determine the delta.
      double newTime = glfwGetTime();
      float fDelta = (float)(newTime - time);
      time = newTime;

      // Tick the engine.
      if(!gameUpdate(game, fDelta)) break;
      glfwSwapBuffers(window);
      sleep(0);//Fixes some weird high CPU bug, not actually necessary.
    }

    // Game has finished running, cleanup.
    gameDispose(game);
  }
  free(game);
  
  // Terminate the GLFW context.
  glfwSetWindowSizeCallback(window, NULL);
  glfwTerminate();

  return 0;
}

void glfwOnResize(GLFWwindow *window, int32_t width, int32_t height) {
  renderSetResolution(&GAME_STATE->engine.render, (float)width, (float)height);
}

void glfwOnKey(GLFWwindow *window,
  int32_t key, int32_t scancode, int32_t action, int32_t mods
) {
  input_t *input = &GAME_STATE->engine.input;
  if(action == GLFW_PRESS) {
    input->buffer[key] = 1;
  } else if(action == GLFW_RELEASE) {
    input->buffer[key] = 0;
  }
}

void glfwOnError(int error, const char* description) {
  fputs(description, stderr);
}

void glfwOnCursor(GLFWwindow *window, double x, double y) {
  input_t *input = &GAME_STATE->engine.input;
  input->buffer[INPUT_MOUSE_X] = (float)x;
  input->buffer[INPUT_MOUSE_Y] = (float)y;
}