#include "raylib.h"
#include <emscripten/emscripten.h>
#include <emscripten/websocket.h>
#include <stdio.h>

const int screen_width = 800;
const int screen_height = 450;

Camera camera = {0};
Ray ray = {0};
Model parth;
Texture2D texture;
Vector3 parth_pos = {0.0f, 0.0f, 0.0f};
BoundingBox parth_bound_box;

void MainGameLoop(void);
EM_BOOL onopen(int eventType,
               const EmscriptenWebSocketOpenEvent *websocketEvent,
               void *userData);
EM_BOOL onerror(int eventType,
                const EmscriptenWebSocketErrorEvent *websocketEvent,
                void *userData);
EM_BOOL onclose(int eventType,
                const EmscriptenWebSocketCloseEvent *websocketEvent,
                void *userData);
EM_BOOL onmessage(int eventType,
                  const EmscriptenWebSocketMessageEvent *websocketEvent,
                  void *userData);

int main(int argc, char **argv) {
  InitWindow(screen_width, screen_height, "Project Verne");

  camera.position = (Vector3){-9.0f, 9.0f, 4.0f};
  camera.target = (Vector3){9.0f, 9.0f, 0.0f};
  camera.up = (Vector3){0.0f, 1.0f, 0.0f};
  camera.fovy = 60.0f;
  camera.projection = CAMERA_PERSPECTIVE;

  parth = LoadModel("assets/parthanon.obj");
  texture = LoadTexture("assets/parthanon_8k.png");
  parth.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;

  parth_bound_box = GetMeshBoundingBox(parth.meshes[0]);

  if (!emscripten_websocket_is_supported()) {
    return 0;
  }
  EmscriptenWebSocketCreateAttributes ws_attrs = {"wss://echo.websocket.org",
                                                  NULL, EM_TRUE};

  EMSCRIPTEN_WEBSOCKET_T ws = emscripten_websocket_new(&ws_attrs);
  emscripten_websocket_set_onopen_callback(ws, NULL, onopen);
  emscripten_websocket_set_onerror_callback(ws, NULL, onerror);
  emscripten_websocket_set_onclose_callback(ws, NULL, onclose);
  emscripten_websocket_set_onmessage_callback(ws, NULL, onmessage);

#if defined(PLATFORM_WEB)
  emscripten_set_main_loop(MainGameLoop, 0, 1);
#else
  SetTargetFPS(60); // Set our game to run at 60 frames-per-second

  DisableCursor();

  while (!WindowShouldClose()) {
    MainGameLoop();
  }
#endif

  UnloadModel(parth);
  UnloadTexture(texture);

  CloseWindow();
  return 0;
}

/**
 * Main game loop
 */
void MainGameLoop(void) {
  // Lock mouse cursor if mouse click on canvas
  if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
    DisableCursor();
  if (IsKeyPressed(KEY_ESCAPE))
    EnableCursor();

  UpdateCamera(&camera, CAMERA_THIRD_PERSON);

  RayCollision collision = {0};
  collision.distance = 100000.0f; // arbitrary reasonable distance.
  collision.hit = false;

  // ray pointing down so we are always just above the floor.
  ray = (Ray){camera.target, (Vector3){0.0f, -1.0f, 0.0f}};

  RayCollision bb_hit = GetRayCollisionBox(ray, parth_bound_box);
  if ((bb_hit.hit) && (bb_hit.distance < collision.distance)) {
    collision = bb_hit;

    RayCollision mesh_collide = {0};
    for (int m = 0; m < parth.meshCount; m++) {
      mesh_collide = GetRayCollisionMesh(ray, parth.meshes[m], parth.transform);
      if (mesh_collide.hit) {
        if ((!collision.hit) || (collision.distance > mesh_collide.distance)) {
          collision = mesh_collide;
        }
        break;
      }
    }

    if (mesh_collide.hit) {
      collision = mesh_collide;
    }
  }

  BeginDrawing();

  ClearBackground(RAYWHITE);

  BeginMode3D(camera);

  DrawModel(parth, parth_pos, 1.0f, WHITE);

  if (collision.hit) {
    camera.target.y = collision.point.y + 0.1;
    if (camera.target.y < 0.0f) {
      camera.target.y = 0.0f;
    }
  }

  DrawCube(camera.target, 0.1f, 0.1f, 0.1f, RED);

  DrawGrid(10, 10.0f);

  EndMode3D();

  DrawFPS(10, 10);

  EndDrawing();
}

EM_BOOL onopen(int eventType,
               const EmscriptenWebSocketOpenEvent *websocketEvent,
               void *userData) {
  puts("onopen");

  EMSCRIPTEN_RESULT result;
  result = emscripten_websocket_send_utf8_text(websocketEvent->socket, "hoge");
  if (result) {
    printf("Failed to emscripten_websocket_send_utf8_text(): %d\n", result);
  }
  return EM_TRUE;
}
EM_BOOL onerror(int eventType,
                const EmscriptenWebSocketErrorEvent *websocketEvent,
                void *userData) {
  puts("onerror");

  return EM_TRUE;
}
EM_BOOL onclose(int eventType,
                const EmscriptenWebSocketCloseEvent *websocketEvent,
                void *userData) {
  puts("onclose");

  return EM_TRUE;
}
EM_BOOL onmessage(int eventType,
                  const EmscriptenWebSocketMessageEvent *websocketEvent,
                  void *userData) {
  puts("onmessage");
  if (websocketEvent->isText) {
    // For only ascii chars.
    printf("message: %s\n", websocketEvent->data);
  }

  EMSCRIPTEN_RESULT result;
  result =
      emscripten_websocket_close(websocketEvent->socket, 1000, "no reason");
  if (result) {
    printf("Failed to emscripten_websocket_close(): %d\n", result);
  }
  return EM_TRUE;
}
