#include <stdlib.h>
#include <stdio.h>
#include "include/raylib.h"
#include "include/raymath.h"
#include "boids_game.h"
#include "lib.h"

// #define SCREEN_WIDTH 1300
// #define SCREEN_HEIGHT 1080
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define TARGET_FPS 60


typedef struct Boid {
    Point position;
    Vector2 acceleration;
    Vector2 velocity;
} Boid;

typedef struct GameState {
    Boid *boids;
    PointOption target_pos;
    int num_boids;
    float max_speed;
    float max_force;
} GameState;

static GameState *init() {
    InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Boids");

    int monitor = GetCurrentMonitor();
    int monitor_width = GetMonitorWidth(monitor);
    // int monitor_height = GetMonitorHeight(monitor);
    int win_pos_x = monitor_width - SCREEN_WIDTH - 7;
    int win_pos_y = 30;
    SetWindowPosition(win_pos_x, win_pos_y);

    SetTargetFPS(TARGET_FPS);

    GameState *state = malloc(sizeof(struct GameApi));
    state->target_pos = (PointOption){ .tag = NONE };
    state->num_boids = 16;
    state->max_speed = 5.0f;
    state->max_force = 0.05;
    Boid *boids = malloc(state->num_boids * sizeof(Boid));
    state->boids = boids;
    for (int i = 0; i < state->num_boids; i++) {
        Boid *boid = &boids[i];
        int rand_x = GetRandomValue(0, SCREEN_WIDTH);
        int rand_y = GetRandomValue(0, SCREEN_HEIGHT);
        boid->position = (Vector2){rand_x, rand_y};
        int rand_vx = GetRandomValue(0, 100);
        int rand_vy = GetRandomValue(0, 100);
        boid->velocity = (Vector2){rand_vx * 0.01f - 0.5f, rand_vy * 0.01f - 0.5f};
        // boid->velocity = Vector2Scale(boid->velocity, 10.0f);
        // int rand_ax = GetRandomValue(0, 100);
        // int rand_ay = GetRandomValue(0, 100);
        // boid->velocity = (Vector2){rand_ax * 0.01f - 0.5f, rand_ay * 0.01f - 0.5f};
        boid->acceleration = (Vector2){0};
    }

    printf("Initialized Game\n");
    return state;
}

static void finalize(GameState *state) {
    free(state->boids);
    CloseWindow();
}

static void reload(GameState *state) {
    (void)state;
    state->max_speed = 20.0f;
    state->max_force = 0.5;
    printf("Reloaded Game\n");
}

static void unload(GameState *state) {
    (void)state;
}

// We need this so all the raylib state is in the right place
static bool window_should_close() {
    return WindowShouldClose();
}

static void step(GameState *state) {
    // Process Input
    if (IsMouseButtonPressed(0)) {
        Vector2 mouse_pos = GetMousePosition();
        state->target_pos = (PointOption){.tag = SOME, .some.point = mouse_pos};
    }

    // Update
    for (int i = 0; i < state->num_boids; i++) {
        Boid *boid = &state->boids[i];
        if (state->target_pos.tag == SOME) {
            Vector2 desired = Vector2Subtract(state->target_pos.some.point, boid->position);
            desired = Vector2Normalize(desired);
            desired = Vector2Scale(desired, state->max_speed);
            Vector2 steer = Vector2Subtract(desired, boid->velocity);
            steer = Vector2ClampValue(steer, 0.0f, state->max_force);
            boid->acceleration = Vector2Add(boid->acceleration, steer);
        }
        boid->velocity = Vector2Add(boid->velocity, boid->acceleration);
        boid->velocity = Vector2ClampValue(boid->velocity, 0.0f, state->max_speed);
        boid->position = Vector2Add(boid->position, boid->velocity);
    }

    // Update
    BeginDrawing();
    {
        ClearBackground(RAYWHITE);
        // You can draw a triangle but you'd need to rotate all 3 vectors, and I don't
        // want to get distracted with that
        // DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, GREEN);
        // DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, GREEN);
        for (int i = 0; i < state->num_boids; i++) {
            Boid *boid = &state->boids[i];
            DrawCircle(boid->position.x, boid->position.y, 27, BLACK);
            DrawCircle(boid->position.x, boid->position.y, 20, GREEN);
        }
        DrawFPS(SCREEN_WIDTH - 80, 10);
    }
    EndDrawing();
}

const struct GameApi GAME_API = {
    .init = init,
    .reload = reload,
    .step = step,
    .unload = unload,
    .finalize = finalize,
    .window_should_close = window_should_close,
};
