#include <box2d/box2d.h>
#include <raylib.h>
#include <iostream>
#include <cstring>

#include "court.h"
#include "draw.h"
#include "controllers.h"
#include "player.h"

#define BVOLLEY_PHYSICS_DEBUG_DRAW 0
#define BVOLLEY_DRAW_STATS 0

constexpr int SCREEN_WIDTH = 1280;
constexpr int SCREEN_HEIGHT = 720;
constexpr int FRAME_RATE = 60;

int main() {
  SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT);
  InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Beach Volleyball Game");

  SetTargetFPS(FRAME_RATE);

  RenderTexture2D target = LoadRenderTexture(SCREEN_WIDTH, SCREEN_HEIGHT);
  SetTextureFilter(target.texture, TEXTURE_FILTER_ANISOTROPIC_4X);

  Camera2D camera;
  camera.target = (Vector2) {0, -325.0f};
  camera.offset = (Vector2) {SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f};
  camera.rotation = 0.0f;
  camera.zoom = 0.95f;

  b2WorldDef world_def = b2DefaultWorldDef();
  world_def.gravity = b2Vec2{0.0f, -9.81f};
  world_def.enableSleep = true;

  b2WorldId world_id = b2CreateWorld(&world_def);

  Court court(world_id);
  Player player_left(world_id, court.get_ball_id(), true);
  Player player_right(world_id, court.get_ball_id(), false);

  court.set_left_shape_id(player_left.get_shape_id());
  court.set_right_shape_id(player_right.get_shape_id());

  player_left.set_controller_function(&dummy_shooter_controller);
  player_right.set_controller_function(&simple_prediction_controller);

#if BVOLLEY_PHYSICS_DEBUG_DRAW
  b2DebugDraw debug_draw{};
  debug_draw.context = nullptr;
  debug_draw.drawShapes = true;
  debug_draw.drawContacts = true;
  debug_draw.DrawSolidPolygonFcn = dd_solid_polygon;
  debug_draw.DrawSolidCircleFcn = dd_solid_circle;
  debug_draw.DrawPointFcn = dd_draw_point;
#endif

  while (!WindowShouldClose()) {
    if (IsKeyDown(KEY_RIGHT)) {
      camera.target.x += 5.0f;
    }
    if (IsKeyDown(KEY_LEFT)) {
      camera.target.x -= 5.0f;
    }
    if (IsKeyDown(KEY_UP)) {
      camera.target.y -= 5.0f;
    }
    if (IsKeyDown(KEY_DOWN)) {
      camera.target.y += 5.0f;
    }
    if (IsKeyDown(KEY_COMMA)) {
      camera.zoom /= 1.01f;
    }
    if (IsKeyDown(KEY_PERIOD)) {
      camera.zoom *= 1.01f;
    }
    if (IsKeyPressed(KEY_ESCAPE)) {
      CloseWindow();
    }
    if (IsKeyPressed(KEY_R)) {
      court.reset_court();
    }

    player_left.update();
    player_right.update();

    b2World_Step(world_id, 1.0f / 60.0f, 4);
    court.update();

    BeginTextureMode(target);
    BeginMode2D(camera);

    ClearBackground({20, 20, 20, 255});

    court.draw();
    player_left.draw();
    player_right.draw();

#if BVOLLEY_PHYSICS_DEBUG_DRAW
    b2World_Draw(world_id, &debug_draw);
#endif

    EndMode2D();

    court.draw_ui(SCREEN_WIDTH, SCREEN_HEIGHT);

#if BVOLLEY_DRAW_STATS
    {
      char buffer[1024];
      snprintf(buffer, sizeof(buffer), "%d\n%.6f\n%.2f",
               GetFPS(), GetFrameTime() * 1000.0f, GetTime());


      const b2BodyId ball_id = court.get_ball_id();
      const b2Vec2 ball_position = b2Body_GetPosition(ball_id);
      const b2Vec2 ball_velocity = b2Body_GetLinearVelocity(ball_id);
      snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer),
               "\nPos: (%.2f, %2.2f)\nVel: (%.2f, %.2f)",
               ball_position.x, ball_position.y, ball_velocity.x, ball_velocity.y);

      DrawText(buffer, 10, 10, 8, WHITE);
    }
#endif

    EndTextureMode();

    const float scale = std::min(static_cast<float>(GetScreenWidth()) / SCREEN_WIDTH,
                                 static_cast<float>(GetScreenHeight()) / SCREEN_HEIGHT);
    BeginDrawing();
    ClearBackground(BLACK);
    DrawTexturePro(
      target.texture,
      (Rectangle) {0.0f, 0.0f, static_cast<float>(target.texture.width), static_cast<float>(-target.texture.height)},
      (Rectangle) {(GetScreenWidth() - static_cast<float>(SCREEN_WIDTH) * scale) * 0.5f,
                   (GetScreenHeight() - static_cast<float>(SCREEN_HEIGHT) * scale) * 0.5f,
                   static_cast<float>(SCREEN_WIDTH) * scale, static_cast<float>(SCREEN_HEIGHT) * scale},
      (Vector2) {0, 0}, 0.0f, WHITE);
    EndDrawing();
  }

  b2DestroyWorld(world_id);
  UnloadRenderTexture(target);
  CloseWindow();
}
