#include <stdio.h>
#include <stdlib.h>
#include "vga_ball.h"
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <signal.h>

int vga_ball_fd;
void print_binary(uint8_t value)
{
    for (int i = 7; i >= 0; --i)
    {
        printf("%d", (value >> i) & 1);
    }
    printf("\n");
}
int concatenate_bits(uint8_t left_one_bit, uint8_t left_three_bits, uint8_t one_bit, uint8_t three_bits)
{
    // Ensure the inputs are within the valid range
    left_three_bits &= 0x07;
    left_one_bit &= 0x01;
    one_bit &= 0x01;    // Mask the input to keep only the least significant bit
    three_bits &= 0x07; // Mask the input to keep only the least significant 3 bits

    // Shift the one_bit 3 positions to the left and OR with the three_bits
    uint8_t result = (left_one_bit << 7) | (left_three_bits << 4) | (one_bit << 3) | three_bits;
    // print_binary(result);
    return result;
}
// GREEN TANK FUNCTIONS
void print_gloc()
{
    tank_arg_t vla;

    if (ioctl(vga_ball_fd, GTANK_READ_LOC, &vla))
    {
        perror("ioctl(VGA_BALL_READ_BACKGROUND) failed");
        return;
    }
    printf("green x: %d y: %d \n",
           vla.location.x, vla.location.y);
}
void print_gloc_test()
{
    tank_arg_t vla;

    if (ioctl(vga_ball_fd, test_read_gtank, &vla))
    {
        perror("ioctl(VGA_BALL_READ_BACKGROUND) failed");
        return;
    }
    printf("green test_read_gtank x: %d y: %d \n",
           vla.location.x, vla.location.y);
}
void set_gloc(const tank_loc_t *c)
{
    tank_arg_t vla;
    vla.location = *c;
    if (ioctl(vga_ball_fd, GTANK_WRITE_LOC, &vla))
    {
        perror("ioctl(LOC) failed");
        return;
    }
}
void set_tank_data(int gvis, int gdir, int bvis, int bdir)
{
    int ret = concatenate_bits((uint8_t)gvis, (uint8_t)gdir, (uint8_t)bvis, (uint8_t)bdir);
    tank_arg_t vla;
    vla.data.gtank_dis_dir_btank_dis_dir = ret;
    if (ioctl(vga_ball_fd, TANK_DATA_WRITE, &vla))
    {
        perror("ioctl(DATA) failed");
        return;
    }
}

// BLUE TANK FUNCTIONS
void print_bloc()
{
    tank_arg_t vla;

    if (ioctl(vga_ball_fd, BTANK_READ_LOC, &vla))
    {
        perror("ioctl(VGA_BALL_READ_BACKGROUND) failed");
        return;
    }
    printf("blue %d %d \n",
           vla.location.x, vla.location.y);
}
void set_bloc(const tank_loc_t *c)
{
    tank_arg_t vla;
    vla.location = *c;
    if (ioctl(vga_ball_fd, BTANK_WRITE_LOC, &vla))
    {
        perror("ioctl(LOC) failed");
        return;
    }
}

// GREEN BULLET FUNCTIONS
void print_gbulletloc()
{
    tank_arg_t vla;

    if (ioctl(vga_ball_fd, GBULLET_READ_LOC, &vla))
    {
        perror("ioctl(VGA_BALL_READ_BACKGROUND) failed");
        return;
    }
    printf("green bullet %d %d \n",
           vla.location.x, vla.location.y);
}
void set_gbulletloc(const tank_loc_t *c)
{
    tank_arg_t vla;
    vla.location = *c;
    if (ioctl(vga_ball_fd, GBULLET_WRITE_LOC, &vla))
    {
        perror("ioctl(LOC) failed");
        return;
    }
}
void set_gbulletdata(int vis, int dir)
{
    int ret = concatenate_bits(0, 0, (uint8_t)vis, (uint8_t)dir);
    tank_arg_t vla;
    vla.data.gtank_dis_dir_btank_dis_dir = ret;
    if (ioctl(vga_ball_fd, GBULLET_DATA, &vla))
    {
        perror("ioctl(DATA) failed");
        return;
    }
    // free(vla);
}

void select_audio(int choice, int screenstart)
{
    tank_arg_t vla;
    vla.sound_selection = (unsigned short)concatenate_bits(0, 0, (uint8_t)screenstart, (uint8_t)choice);
    if (ioctl(vga_ball_fd, AUDIO_SELECT, &vla))
    {
        perror("ioctl(LOC) failed");
        return;
    }
}

// BLUE BULLET FUNCTIONS
void print_bbulletloc()
{
    tank_arg_t vla;

    if (ioctl(vga_ball_fd, BBULLET_READ_LOC, &vla))
    {
        perror("ioctl(VGA_BALL_READ_BACKGROUND) failed");
        return;
    }
    printf("blue bullet%d %d \n",
           vla.location.x, vla.location.y);
}
void set_bbulletloc(const tank_loc_t *c)
{
    tank_arg_t vla;
    vla.location = *c;
    if (ioctl(vga_ball_fd, BBULLET_WRITE_LOC, &vla))
    {
        perror("ioctl(LOC) failed");
        return;
    }
}
void set_bbulletdata(int vis, int dir)
{
    int ret = concatenate_bits(0, 0, (uint8_t)vis, (uint8_t)dir);
    tank_arg_t vla;
    vla.data.gtank_dis_dir_btank_dis_dir = ret;
    if (ioctl(vga_ball_fd, BBULLET_DATA, &vla))
    {
        perror("ioctl(DATA) failed");
        return;
    }
}

// EXPLOSION
void set_explosion(const object_data_t *c)
{
    tank_arg_t vla;
    vla.explosion_data = *c;
    if (ioctl(vga_ball_fd, EXP_WRITE_DATA, &vla))
    {
        perror("ioctl(LOC) failed");
        return;
    }
}

// CONTROLS
unsigned short read_controls()
{
    tank_arg_t vla;
    if (ioctl(vga_ball_fd, READ_CONTROLS, &vla))
    {
        perror("ioctl(CONTROLS) failed");
    }
    return vla.controller_input;
}

// SET SCORE
void set_score(int score_1, int score_2)
{
    tank_arg_t vla;
    int score_1_first_digit = score_1 / 10;
    int score_1_second_digit = score_1 % 10;

    int score_2_first_digit = score_2 / 10;
    int score_2_second_digit = score_2 % 10;
    unsigned short combined_binary = (score_2_first_digit << 0) |
                                     (score_2_second_digit << 4) |
                                     (score_1_first_digit << 8) |
                                     (score_1_second_digit << 12);
    // printf("score_1_first_digit: %d\n", score_1_first_digit);
    // printf("score_1_second_digit: %d\n", score_1_second_digit);
    // printf("score_2_first_digit: %d\n", score_2_first_digit);
    // printf("score_2_second_digit: %d\n", score_2_second_digit);
    vla.score_data.score = combined_binary;
    if (ioctl(vga_ball_fd, SCORE_WRITE_DATA, &vla))
    {
        perror("ioctl(LOC) failed");
        return;
    }
}

typedef struct
{
    int topLeft_x, topLeft_y, bottomRight_x, bottomRight_y;
} hitBox_t;
typedef struct
{

    int direction;
    // the top left corner of the tank
    int x_pos;
    int y_pos;
    bool visible;
    bool fired;
    int score;
    hitBox_t *hitBox;
} game_tank_data_t;

typedef struct
{
    game_tank_data_t *gtank;
    game_tank_data_t *btank;
    bool stun;
    int audio_select;
} game_data_t;

// Thread functions for controlling tanks
pthread_mutex_t tank_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t audio_mutex = PTHREAD_MUTEX_INITIALIZER;
void *green_tank_thread(void *arg);
void *blue_tank_thread(void *arg);
void *invis_green_tank_thread(void *arg);
void *invis_blue_tank_thread(void *arg);
void *audio_thread_fcn(void *arg);
void *explosion_green_thread_fcn(void *arg)
{
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *green_tank = data->gtank;
    object_data_t *explosion = (object_data_t *)malloc(sizeof(object_data_t));

    explosion->location.x = green_tank->x_pos;
    explosion->location.y = green_tank->y_pos;

    int i = 0;
    while (i < 5)
    {
        explosion->index = i;
        set_explosion(explosion);
        usleep(90000);
        i++;
    }
    explosion->location.x = 0;
    explosion->location.y = 0;
    set_explosion(explosion);
    free(explosion);
    pthread_exit(NULL);
}
void *explosion_blue_thread_fcn(void *arg)
{
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *blue_tank = data->btank;
    object_data_t *explosion = (object_data_t *)malloc(sizeof(object_data_t));

    explosion->location.x = blue_tank->x_pos;
    explosion->location.y = blue_tank->y_pos;

    int i = 0;
    while (i < 5)
    {
        explosion->index = i;
        set_explosion(explosion);
        usleep(90000);
        i++;
    }
    explosion->location.x = 0;
    explosion->location.y = 0;
    set_explosion(explosion);
    free(explosion);
    pthread_exit(NULL);
}

// CHECK OBSTACLES WITH TANK LOC

int isOverlap(hitBox_t *h1, hitBox_t *h2)
{
    // Check if h1 overlaps h2
    if ((h1->topLeft_y <= h2->topLeft_y && h2->topLeft_y <= h1->bottomRight_y) &&
        ((h1->bottomRight_x >= h2->topLeft_x && h2->topLeft_x >= h1->topLeft_x) ||
         (h1->bottomRight_x >= h2->bottomRight_x && h2->bottomRight_x >= h1->topLeft_x)))
        return 1;
    if ((h1->topLeft_y <= h2->bottomRight_y && h2->bottomRight_y <= h1->bottomRight_y) &&
        ((h1->bottomRight_x >= h2->topLeft_x && h2->topLeft_x >= h1->topLeft_x) ||
         (h1->bottomRight_x >= h2->bottomRight_x && h2->bottomRight_x >= h1->topLeft_x)))
        return 1;

    // Check if h2 overlaps h1
    if ((h2->topLeft_y <= h1->topLeft_y && h1->topLeft_y <= h2->bottomRight_y) &&
        ((h1->topLeft_x >= h2->topLeft_x && h2->bottomRight_x >= h1->topLeft_x) ||
         (h1->bottomRight_x >= h2->topLeft_x && h2->bottomRight_x >= h1->bottomRight_x)))
        return 1;
    if ((h2->topLeft_y <= h1->bottomRight_y && h1->bottomRight_y <= h2->bottomRight_y) &&
        ((h1->topLeft_x >= h2->topLeft_x && h2->bottomRight_x >= h1->topLeft_x) ||
         (h1->bottomRight_x >= h2->topLeft_x && h2->bottomRight_x >= h1->bottomRight_x)))
        return 1;

    return 0; // no overlap
}

bool bullet_collision(game_data_t *data, hitBox_t *bullet_hitbox)
{
    hitBox_t *obstacle_hitbox = malloc(sizeof(hitBox_t));

    // LEFT BORDER
    obstacle_hitbox->topLeft_x = 0;
    obstacle_hitbox->topLeft_y = 0;
    obstacle_hitbox->bottomRight_x = 40;
    obstacle_hitbox->bottomRight_y = 480;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // RIGHT BORDER
    obstacle_hitbox->topLeft_x = 600;
    obstacle_hitbox->topLeft_y = 0;
    obstacle_hitbox->bottomRight_x = 640;
    obstacle_hitbox->bottomRight_y = 480;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // TOP BORDER
    obstacle_hitbox->topLeft_x = 0;
    obstacle_hitbox->topLeft_y = 0;
    obstacle_hitbox->bottomRight_x = 640;
    obstacle_hitbox->bottomRight_y = 50;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // BOTTOM BORDER
    obstacle_hitbox->topLeft_x = 0;
    obstacle_hitbox->topLeft_y = 440;
    obstacle_hitbox->bottomRight_x = 640;
    obstacle_hitbox->bottomRight_y = 480;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // YELLOW WALL LEFT
    obstacle_hitbox->topLeft_x = 118;
    obstacle_hitbox->topLeft_y = 152;
    obstacle_hitbox->bottomRight_x = 129;
    obstacle_hitbox->bottomRight_y = 332;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // YELLOW WALL RIGHT
    obstacle_hitbox->topLeft_x = 513;
    obstacle_hitbox->topLeft_y = 152;
    obstacle_hitbox->bottomRight_x = 524;
    obstacle_hitbox->bottomRight_y = 332;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // TOP MIDDLE RED
    obstacle_hitbox->topLeft_x = 313;
    obstacle_hitbox->topLeft_y = 121;
    obstacle_hitbox->bottomRight_x = 332;
    obstacle_hitbox->bottomRight_y = 204;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // BOTTOM MIDDLE RED
    obstacle_hitbox->topLeft_x = 313;
    obstacle_hitbox->topLeft_y = 281;
    obstacle_hitbox->bottomRight_x = 332;
    obstacle_hitbox->bottomRight_y = 364;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // METAL BOX BOTTOM LEFT
    obstacle_hitbox->topLeft_x = 92;
    obstacle_hitbox->topLeft_y = 156 + 4 * 36 + 7;
    obstacle_hitbox->bottomRight_x = 114;
    obstacle_hitbox->bottomRight_y = 156 + 4 * 36 + 7 + 22;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // METAL BOX TOP LEFT
    obstacle_hitbox->topLeft_x = 92;
    obstacle_hitbox->topLeft_y = 156;
    obstacle_hitbox->bottomRight_x = 114;
    obstacle_hitbox->bottomRight_y = 178;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // METAL BOX TOP RIGHT
    obstacle_hitbox->topLeft_x = 499 + 16 + 12;
    obstacle_hitbox->topLeft_y = 160;
    obstacle_hitbox->bottomRight_x = 499 + 16 + 12 + 22;
    obstacle_hitbox->bottomRight_y = 178;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;

    // METAL BOX BOTTOM RIGHT
    obstacle_hitbox->topLeft_x = 499 + 16 + 7;
    obstacle_hitbox->topLeft_y = 156 + 4 * 36 + 10;
    obstacle_hitbox->bottomRight_x = 499 + 16 + 12 + 22;
    obstacle_hitbox->bottomRight_y = 156 + 4 * 36 + 7 + 22;
    if (isOverlap(bullet_hitbox, obstacle_hitbox))
        goto hitCleanUp;
    free(obstacle_hitbox);
    return 0;

hitCleanUp:
    free(obstacle_hitbox);
    return 1;
}

bool check_obstacle_collision(game_tank_data_t *tank, int bounce)
{
    // first check walls
    int loc_x = tank->x_pos;
    int loc_y = tank->y_pos;

    bool hit = 0;
    hitBox_t *obstacle_hitbox = malloc(sizeof(hitBox_t));
    printf("Hitbox 1 Coordinates: \n");
    printf("Top Left: (%d, %d)\n", tank->hitBox->topLeft_x, tank->hitBox->topLeft_y);
    printf("Bottom Right: (%d, %d)\n", tank->hitBox->bottomRight_x, tank->hitBox->bottomRight_y);

    // LEFT BORDER
    obstacle_hitbox->topLeft_x = 0;
    obstacle_hitbox->topLeft_y = 0;
    obstacle_hitbox->bottomRight_x = 40;
    obstacle_hitbox->bottomRight_y = 480;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // RIGHT BORDER
    obstacle_hitbox->topLeft_x = 600;
    obstacle_hitbox->topLeft_y = 0;
    obstacle_hitbox->bottomRight_x = 640;
    obstacle_hitbox->bottomRight_y = 480;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // TOP BORDER
    obstacle_hitbox->topLeft_x = 0;
    obstacle_hitbox->topLeft_y = 0;
    obstacle_hitbox->bottomRight_x = 640;
    obstacle_hitbox->bottomRight_y = 50;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // BOTTOM BORDER
    obstacle_hitbox->topLeft_x = 0;
    obstacle_hitbox->topLeft_y = 440;
    obstacle_hitbox->bottomRight_x = 640;
    obstacle_hitbox->bottomRight_y = 480;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // YELLOW WALL LEFT
    obstacle_hitbox->topLeft_x = 118;
    obstacle_hitbox->topLeft_y = 152;
    obstacle_hitbox->bottomRight_x = 129;
    obstacle_hitbox->bottomRight_y = 332;
    // printf("YELLOW Hitbox 1 Coordinates: \n");
    // printf("Top Left: (%d, %d)\n", obstacle_hitbox->topLeft_x, obstacle_hitbox->topLeft_y);
    // printf("Bottom Right: (%d, %d)\n", obstacle_hitbox->bottomRight_x, obstacle_hitbox->bottomRight_y);

    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // YELLOW WALL RIGHT
    obstacle_hitbox->topLeft_x = 513;
    obstacle_hitbox->topLeft_y = 152;
    obstacle_hitbox->bottomRight_x = 524;
    obstacle_hitbox->bottomRight_y = 332;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // TOP MIDDLE RED
    obstacle_hitbox->topLeft_x = 313;
    obstacle_hitbox->topLeft_y = 121;
    obstacle_hitbox->bottomRight_x = 332;
    obstacle_hitbox->bottomRight_y = 204;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // BOTTOM MIDDLE RED
    obstacle_hitbox->topLeft_x = 313;
    obstacle_hitbox->topLeft_y = 281;
    obstacle_hitbox->bottomRight_x = 332;
    obstacle_hitbox->bottomRight_y = 364;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // METAL BOX BOTTOM LEFT
    obstacle_hitbox->topLeft_x = 92;
    obstacle_hitbox->topLeft_y = 156 + 4 * 36 + 7;
    obstacle_hitbox->bottomRight_x = 114;
    obstacle_hitbox->bottomRight_y = 156 + 4 * 36 + 7 + 22;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // METAL BOX TOP LEFT
    obstacle_hitbox->topLeft_x = 92;
    obstacle_hitbox->topLeft_y = 156;
    obstacle_hitbox->bottomRight_x = 114;
    obstacle_hitbox->bottomRight_y = 178;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // METAL BOX TOP RIGHT
    obstacle_hitbox->topLeft_x = 499 + 16 + 12;
    obstacle_hitbox->topLeft_y = 160;
    obstacle_hitbox->bottomRight_x = 499 + 16 + 12 + 22;
    obstacle_hitbox->bottomRight_y = 178;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;

    // METAL BOX BOTTOM RIGHT
    obstacle_hitbox->topLeft_x = 499 + 16 + 7;
    obstacle_hitbox->topLeft_y = 156 + 4 * 36 + 10;
    obstacle_hitbox->bottomRight_x = 499 + 16 + 12 + 22;
    obstacle_hitbox->bottomRight_y = 156 + 4 * 36 + 7 + 22;
    if (isOverlap(tank->hitBox, obstacle_hitbox))
        goto BOUNCE;
    return 0;
    // the starter barriers
BOUNCE:

    printf("HIT NOW BOUNCE\n");
    switch (tank->direction)
    {
    case 0:
        tank->x_pos += bounce;
        tank->y_pos -= bounce;
        break;
    case 1:
        tank->y_pos -= bounce;
        break;
    case 2:
        tank->x_pos -= bounce;
        tank->y_pos -= bounce;
        break;
    case 3:
        tank->x_pos -= bounce;
        break;
    case 4:
        tank->x_pos -= bounce;
        tank->y_pos += bounce;
        break;
    case 5:
        tank->y_pos += bounce;
        break;
    case 6:
        tank->x_pos += bounce;
        tank->y_pos += bounce;
        break;
    case 7:
        tank->x_pos += bounce;
        break;
    default:
        printf("wack shit in bounce\n");
        break;
    }

    free(obstacle_hitbox);
    return 1;
}

void update_tank_hitbox(game_tank_data_t *tank)
{
    tank->hitBox->topLeft_x = tank->x_pos + 3;
    tank->hitBox->topLeft_y = tank->y_pos + 3;
    tank->hitBox->bottomRight_x = tank->x_pos + 24;
    tank->hitBox->bottomRight_y = tank->y_pos + 24;
}
void update_bullet_hitbox(hitBox_t *h, int x, int y)
{
    h->topLeft_x = x + 3;
    h->topLeft_y = y + 3;
    h->bottomRight_x = x + 9;
    h->bottomRight_y = y + 9;
}

volatile sig_atomic_t stop_threads = 0;

void alarm_handler(int signum)
{
    stop_threads = 1;
}

int main()
{
    game_tank_data_t *green_tank = malloc(sizeof(game_tank_data_t));
    game_tank_data_t *blue_tank = malloc(sizeof(game_tank_data_t));
    hitBox_t *blue_hitbox = malloc(sizeof(hitBox_t));
    hitBox_t *green_hitbox = malloc(sizeof(hitBox_t));
    game_data_t *data = malloc(sizeof(game_data_t));
    data->stun = 0;
    char game_running = 1;
    static const char filename[] = "/dev/vga_ball";

    //   printf("VGA ball Userspace program started\n");

    if ((vga_ball_fd = open(filename, O_RDWR)) == -1)
    {
        fprintf(stderr, "could not open %s\n", filename);
        return -1;
    }
    printf("start\n");
    pthread_t green_thread, blue_thread, audio_thread;
    select_audio(0, 1);
    // set up hitboxes
    green_hitbox->topLeft_x = 3;
    green_hitbox->topLeft_y = 3;
    green_hitbox->bottomRight_x = 24;
    green_hitbox->bottomRight_y = 24;

    blue_hitbox->topLeft_x = 3;
    blue_hitbox->topLeft_y = 3;
    blue_hitbox->bottomRight_x = 24;
    blue_hitbox->bottomRight_y = 24;

    data->btank = blue_tank;
    data->gtank = green_tank;
    green_tank->fired = 0;
    blue_tank->fired = 0;
    green_tank->direction = 3;
    blue_tank->direction = 7;
    green_tank->hitBox = green_hitbox;
    blue_tank->hitBox = blue_hitbox;
    set_score(0, 0);
    signal(SIGALRM, alarm_handler);

    int game_mode = 1;
    int controls, contr1_fire, contr1_down;
    int i;
    // core game loop
    while (1)
    {
        controls = read_controls();
        contr1_fire = (controls >> 9) & 1;
        contr1_down = (controls >> 8) & 1;

        set_score(game_mode, game_mode);
        if (!contr1_fire)
        {
            sleep(1);
            alarm(50);
            select_audio(0, 0);
            set_score(0, 0);
            printf("gamemode %d\n", game_mode);
            if (game_mode == 1)
            {

                pthread_create(&green_thread, NULL, green_tank_thread, (void *)data);
                pthread_create(&blue_thread, NULL, blue_tank_thread, (void *)data);
                pthread_create(&audio_thread, NULL, audio_thread_fcn, (void *)data);
                // Wait for both threads to finish
                pthread_join(green_thread, NULL);
                pthread_join(blue_thread, NULL);
                pthread_join(audio_thread, NULL);
            }
            else if (game_mode == 2)
            {

                pthread_create(&green_thread, NULL, invis_green_tank_thread, (void *)data);
                pthread_create(&blue_thread, NULL, invis_blue_tank_thread, (void *)data);
                pthread_create(&audio_thread, NULL, audio_thread_fcn, (void *)data);
                // Wait for both threads to finish
                pthread_join(green_thread, NULL);
                pthread_join(blue_thread, NULL);
                pthread_join(audio_thread, NULL);
            }
            stop_threads = 0;
            select_audio(0, 1);
        }
        if (!contr1_down && i > 10)
        {
            game_mode++;
            if (game_mode == 3)
                game_mode = 1;
            i = 0;
        }
        i++;
        usleep(100000);
    }

    close(vga_ball_fd);
}

// audio selection
void *audio_thread_fcn(void *arg)
{
    game_data_t *data = (game_data_t *)arg;
    while (!stop_threads)
    {
        select_audio(0, 0);
        // audio update
        pthread_mutex_lock(&audio_mutex);

        switch (data->audio_select)
        {
        case 1:
            select_audio(1, 0);
            sleep(1);
            data->audio_select = 0;
            break;
        case 2:
            select_audio(2, 0);
            usleep(90000);
            data->audio_select = 0;
            break;
        case 3:
            select_audio(3, 0);
            usleep(10000);
            data->audio_select = 0;
            break;
        default:
            data->audio_select = 0;
            break;
        }
        pthread_mutex_unlock(&audio_mutex);
    }
}

void tank_knockback(game_data_t *data, int bullet_dir, bool green)
{
    tank_loc_t *loc = (tank_loc_t *)malloc(sizeof(loc));
    game_tank_data_t *green_tank = data->gtank;
    game_tank_data_t *blue_tank = data->btank;
    pthread_t spinning;

    int i = 0;
    int speed = 1;
    pthread_mutex_lock(&audio_mutex);
    data->audio_select = 1;
    pthread_mutex_unlock(&audio_mutex);
    if (green)
    {
        loc->x = green_tank->x_pos;
        loc->y = green_tank->y_pos;
    }
    else
    {
        loc->x = blue_tank->x_pos;
        loc->y = blue_tank->y_pos;
    }
    while (i < 30)
    {
        switch (bullet_dir)
        {
        case 0:
            loc->y -= speed + 1;
            break;
        case 1:
            loc->x -= speed;
            loc->y -= speed;
            break;
        case 2:
            loc->x -= speed + 1;
            break;
        case 3:
            loc->x -= speed;
            loc->y += speed;
            break;
        case 4:
            loc->y += speed + 1;
            break;
        case 5:
            loc->x += speed;
            loc->y += speed;
            break;
        case 6:
            loc->x += speed + 1;
            break;
        case 7:
            loc->x += speed;
            loc->y -= speed;
            break;
        default:
            printf("wack shit\n");
            break;
        }
        printf("updating pos\n");
        printf("x: %d\n", green_tank->x_pos);
        printf("y: %d\n", green_tank->y_pos);

        // pthread_mutex_lock(&tank_mutex);
        //  updates the hitboxes
        // if collide with left side
        if (loc->x < 40)
            loc->x = 572;
        // collide with top
        else if (loc->y < 50)
            loc->y = 412;
        // collide with right side
        else if (loc->x + 27 > 600)
            loc->x = 41;
        // colide with bottom
        else if (loc->y + 27 > 440)
            loc->y = 50;
        if (green)
        {

            // update with the new values

            green_tank->x_pos = loc->x;
            green_tank->y_pos = loc->y;

            update_tank_hitbox(green_tank);
            check_obstacle_collision(green_tank, 10);
            loc->x = green_tank->x_pos;
            loc->y = green_tank->y_pos;
            set_gloc(loc);
        }
        else
        {

            blue_tank->x_pos = loc->x;
            blue_tank->y_pos = loc->y;

            update_tank_hitbox(blue_tank);
            check_obstacle_collision(blue_tank, 10);
            loc->x = blue_tank->x_pos;
            loc->y = blue_tank->y_pos;
            set_bloc(loc);
        }
        i++;
        usleep(1000);
    }
    i = 0;
    while (i < 20)
    {
        i++;
        if (green)
            set_tank_data(1, i, 1, blue_tank->direction);

        else
            set_tank_data(1, green_tank->direction, 1, i);
        usleep(80000);
    }
    set_tank_data(1, green_tank->direction, 1, blue_tank->direction);

    data->stun = 0;
    free(loc);
}

// Thread function to handle green bullet fire
void *green_bullet_thread_fcn(void *arg)
{
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *green_tank = data->gtank;
    game_tank_data_t *blue_tank = data->btank;
    hitBox_t *bullet_hitbox = malloc(sizeof(hitBox_t));
    int loc_x;
    int loc_y;
    int speed = 2;
    int bullet_dir = (green_tank->direction + 3) % 8;
    tank_loc_t *newLoc = (tank_loc_t *)malloc(sizeof(tank_loc_t));
    // Instantiate the bullet depending on the direction that the tank is facing
    printf("This is the bullet dir: %d", bullet_dir);
    printf("This is the gtank dir: %d", green_tank->direction);
    switch (bullet_dir)
    {
    case 0:
        loc_x = green_tank->x_pos + 10;
        loc_y = green_tank->y_pos - 8;
        break;
    case 1:
        loc_x = green_tank->x_pos - 2;
        loc_y = green_tank->y_pos - 3;
        break;
    case 2:
        loc_x = green_tank->x_pos - 8;
        loc_y = green_tank->y_pos + 8;
        break;
    case 3:
        loc_x = green_tank->x_pos - 2;
        loc_y = green_tank->y_pos + 20;
        break;
    case 4:
        loc_x = green_tank->x_pos + 10;
        loc_y = green_tank->y_pos + 25;
        break;
    case 5:
        loc_x = green_tank->x_pos + 27 - 6;
        loc_y = green_tank->y_pos + 19;
        break;
    case 6:
        loc_x = green_tank->x_pos + 26;
        loc_y = green_tank->y_pos + 8;
        break;
    case 7:
        loc_x = green_tank->x_pos + 27 - 6;
        loc_y = green_tank->y_pos - 3;
        break;
    default:
        printf("wack shit\n");
        break;
    }
    newLoc->x = loc_x;
    newLoc->y = loc_y;
    set_gbulletdata(1, bullet_dir);
    set_gbulletloc(newLoc);
    update_bullet_hitbox(bullet_hitbox, loc_x, loc_y);
    // Make the bullet fly
    while (!bullet_collision(data, bullet_hitbox) && !data->stun)
    {
        bullet_dir = (green_tank->direction + 3) % 8;
        set_gbulletdata(1, bullet_dir);
        switch (bullet_dir)
        {
        case 0:
            loc_y -= speed + 1;
            break;
        case 1:
            loc_x -= speed;
            loc_y -= speed;
            break;
        case 2:
            loc_x -= speed + 1;
            break;
        case 3:
            loc_x -= speed;
            loc_y += speed;
            break;
        case 4:
            loc_y += speed + 1;
            break;
        case 5:
            loc_x += speed;
            loc_y += speed;
            break;
        case 6:
            loc_x += speed + 1;
            break;
        case 7:
            loc_x += speed;
            loc_y -= speed;
            break;
        default:
            printf("wack shit\n");
            break;
        }
        newLoc->x = loc_x;
        newLoc->y = loc_y;
        set_gbulletloc(newLoc);
        update_bullet_hitbox(bullet_hitbox, loc_x, loc_y);

        // check if hit the opposing tank
        if (isOverlap(bullet_hitbox, blue_tank->hitBox))
        {
            green_tank->score++;
            set_score(blue_tank->score, green_tank->score);
            set_bbulletdata(0, 0);
            set_gbulletdata(0, 0);
            data->stun = 1;
            free(newLoc);
            green_tank->fired = 0;
            pthread_t explosion;
            pthread_create(&explosion, NULL, explosion_blue_thread_fcn, (void *)data);
            pthread_detach(explosion);
            tank_knockback(data, bullet_dir, 0);
            pthread_exit(NULL);
        }
        usleep(10000);
    }
    set_gbulletdata(0, 0);
    free(newLoc);
    green_tank->fired = 0;
    pthread_exit(NULL);
}
// Thread function for controlling the green tank
void *green_tank_thread(void *arg)
{
    uint16_t controls = 0;
    int contr2_up, contr2_left, contr2_right, contr2_down, contr2_fire;
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *green_tank = data->gtank;
    game_tank_data_t *blue_tank = data->btank;
    pthread_t green_bullet_thread;
    green_tank->direction = 0;
    green_tank->visible = 1;

    bool update_pos = 1;
    bool update_dir = 1;
    green_tank->x_pos = 46;
    green_tank->y_pos = 232;

    int rotate_delay = 0;
    // Green tank control loop
    while (!stop_threads)
    {
        // Read controls and update tank positions, directions, and bullet firing
        controls = read_controls();
        contr2_fire = (controls >> 4) & 1;
        contr2_down = (controls >> 3) & 1;
        contr2_right = (controls >> 2) & 1;
        contr2_left = (controls >> 1) & 1;
        contr2_up = (controls >> 0) & 1;

        // printf("contr2_up: %d\n", contr2_up);
        // printf("contr2_left: %d\n", contr2_left);
        // printf("contr2_right: %d\n", contr2_right);
        // printf("contr2_down: %d\n", contr2_down);
        // printf("contr2_fire: %d\n", contr2_fire);

        int speed = 3;
        // Update green tank position
        if (contr2_up == 0 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            switch (green_tank->direction)
            {
            case 0:
                green_tank->x_pos -= speed;
                green_tank->y_pos += speed;
                break;
            case 1:
                green_tank->y_pos += speed;
                break;
            case 2:
                green_tank->x_pos += speed;
                green_tank->y_pos += speed;
                break;
            case 3:
                green_tank->x_pos += speed;
                break;
            case 4:
                green_tank->x_pos += speed;
                green_tank->y_pos -= speed;
                break;
            case 5:
                green_tank->y_pos -= speed;
                break;
            case 6:
                green_tank->x_pos -= speed;
                green_tank->y_pos -= speed;
                break;
            case 7:
                green_tank->x_pos -= speed;
                break;
            default:
                printf("wack shit %d\n", green_tank->direction);
                break;
            }
            pthread_mutex_unlock(&tank_mutex);
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);
            update_pos = 1;
        }

        // Update green tank rotation
        else if (contr2_right == 0 && rotate_delay > 3 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            green_tank->direction--;
            if (green_tank->direction < 0)
                green_tank->direction = 7;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }
        else if (contr2_left == 0 && rotate_delay > 3 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);

            green_tank->direction++;
            if (green_tank->direction > 7)
                green_tank->direction = 0;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }

        // Fire Bullet (make a thread to handle the bullet firing)
        if (contr2_fire == 0 && !green_tank->fired && !data->stun)
        {
            printf("Fire bullet\n");
            pthread_create(&green_bullet_thread, NULL, green_bullet_thread_fcn, (void *)data);
            pthread_detach(green_bullet_thread);
            green_tank->fired = 1;
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 2;
            pthread_mutex_unlock(&audio_mutex);
            usleep(90000);
        }

        // Update score and apply spinning knockback if the tank is hit

        // Write to the hardware
        if (update_pos)
        {
            printf("updating pos\n");
            printf("x: %d\n", green_tank->x_pos);
            printf("y: %d\n", green_tank->y_pos);

            tank_loc_t *loc = (tank_loc_t *)malloc(sizeof(loc));
            // pthread_mutex_lock(&tank_mutex);
            //  updates the hitboxes
            update_tank_hitbox(green_tank);
            check_obstacle_collision(green_tank, 5);

            // update with the new values
            loc->x = green_tank->x_pos;
            loc->y = green_tank->y_pos;

            set_gloc(loc);
            // pthread_mutex_unlock(&tank_mutex);
            update_pos = 0;
            free(loc);
        }
        else if (update_dir)
        {
            printf("updating dir\n");
            //  pthread_mutex_lock(&tank_mutex);
            set_tank_data(1, green_tank->direction, 1, blue_tank->direction);
            // pthread_mutex_unlock(&tank_mutex);
            update_dir = 0;
        }
        // Sleep for a short period to control game speed
        if (data->stun)
            update_dir = 1;
        rotate_delay++;
        usleep(100000);
    }
    return NULL;
}
void *blue_bullet_thread_fcn(void *arg)
{
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *green_tank = data->gtank;
    game_tank_data_t *blue_tank = data->btank;
    hitBox_t *bullet_hitbox = malloc(sizeof(hitBox_t));
    int loc_x;
    int loc_y;
    int speed = 2;
    int bullet_dir = (blue_tank->direction + 3) % 8;
    tank_loc_t *newLoc = (tank_loc_t *)malloc(sizeof(tank_loc_t));
    // Instantiate the bullet depending on the direction that the tank is facing
    // printf("This is the bullet dir: %d", bullet_dir);
    // printf("This is the gtank dir: %d", blue_tank->direction);
    switch (bullet_dir)
    {
    case 0:
        loc_x = blue_tank->x_pos + 10;
        loc_y = blue_tank->y_pos - 8;
        break;
    case 1:
        loc_x = blue_tank->x_pos - 2;
        loc_y = blue_tank->y_pos - 3;
        break;
    case 2:
        loc_x = blue_tank->x_pos - 8;
        loc_y = blue_tank->y_pos + 8;
        break;
    case 3:
        loc_x = blue_tank->x_pos - 2;
        loc_y = blue_tank->y_pos + 20;
        break;
    case 4:
        loc_x = blue_tank->x_pos + 10;
        loc_y = blue_tank->y_pos + 25;
        break;
    case 5:
        loc_x = blue_tank->x_pos + 27 - 6;
        loc_y = blue_tank->y_pos + 19;
        break;
    case 6:
        loc_x = blue_tank->x_pos + 26;
        loc_y = blue_tank->y_pos + 8;
        break;
    case 7:
        loc_x = blue_tank->x_pos + 27 - 6;
        loc_y = blue_tank->y_pos - 3;
        break;
    default:
        printf("wack shit\n");
        break;
    }
    newLoc->x = loc_x;
    newLoc->y = loc_y;
    set_bbulletdata(1, bullet_dir);
    set_bbulletloc(newLoc);
    update_bullet_hitbox(bullet_hitbox, loc_x, loc_y);
    // Make the bullet fly
    while (!bullet_collision(data, bullet_hitbox) && !data->stun)
    {
        bullet_dir = (blue_tank->direction + 3) % 8;
        set_bbulletdata(1, bullet_dir);
        switch (bullet_dir)
        {
        case 0:
            loc_y -= speed + 1;
            break;
        case 1:
            loc_x -= speed;
            loc_y -= speed;
            break;
        case 2:
            loc_x -= speed + 1;
            break;
        case 3:
            loc_x -= speed;
            loc_y += speed;
            break;
        case 4:
            loc_y += speed + 1;
            break;
        case 5:
            loc_x += speed;
            loc_y += speed;
            break;
        case 6:
            loc_x += speed + 1;
            break;
        case 7:
            loc_x += speed;
            loc_y -= speed;
            break;
        default:
            printf("wack shit\n");
            break;
        }
        newLoc->x = loc_x;
        newLoc->y = loc_y;
        set_bbulletloc(newLoc);
        update_bullet_hitbox(bullet_hitbox, loc_x, loc_y);

        if (isOverlap(bullet_hitbox, green_tank->hitBox))
        {
            blue_tank->score++;
            set_score(blue_tank->score, green_tank->score);
            data->stun = 1;
            set_bbulletdata(0, 0);
            set_gbulletdata(0, 0);
            free(newLoc);
            pthread_t explosion;
            pthread_create(&explosion, NULL, explosion_green_thread_fcn, (void *)data);
            pthread_detach(explosion);
            tank_knockback(data, bullet_dir, 1);
            blue_tank->fired = 0;

            pthread_exit(NULL);
        }
        usleep(10000);
    }
    set_bbulletdata(0, 0);
    free(newLoc);
    blue_tank->fired = 0;
    pthread_exit(NULL);
}
// Thread function for controlling the blue tank
void *blue_tank_thread(void *arg)
{
    uint16_t controls = 0;
    int contr1_up, contr1_left, contr1_right, contr1_down, contr1_fire;
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *green_tank = data->gtank;
    game_tank_data_t *blue_tank = data->btank;
    pthread_t blue_bullet_thread;
    blue_tank->direction = 0;
    blue_tank->visible = 1;

    bool update_pos = 1;
    bool update_dir = 1;
    blue_tank->x_pos = 571;
    blue_tank->y_pos = 232;
    blue_tank->fired = 0;
    int rotate_delay = 0;
    set_tank_data(1, green_tank->direction, blue_tank->visible, blue_tank->direction);
    // Green tank control loop
    while (!stop_threads)
    {
        // Read controls and update tank positions, directions, and bullet firing
        controls = read_controls();
        contr1_fire = (controls >> 9) & 1;
        contr1_down = (controls >> 8) & 1;
        contr1_right = (controls >> 7) & 1;
        contr1_left = (controls >> 6) & 1;
        contr1_up = (controls >> 5) & 1;

        // printf("contr2_up: %d\n", contr2_up);
        // printf("contr2_left: %d\n", contr2_left);
        // printf("contr2_right: %d\n", contr2_right);
        // printf("contr2_down: %d\n", contr2_down);
        // printf("contr2_fire: %d\n", contr2_fire);

        int speed = 3;

        // Update blue tank position
        if (contr1_up == 0 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            switch (blue_tank->direction)
            {
            case 0:
                blue_tank->x_pos -= speed;
                blue_tank->y_pos += speed;
                break;
            case 1:
                blue_tank->y_pos += speed;
                break;
            case 2:
                blue_tank->x_pos += speed;
                blue_tank->y_pos += speed;
                break;
            case 3:
                blue_tank->x_pos += speed;
                break;
            case 4:
                blue_tank->x_pos += speed;
                blue_tank->y_pos -= speed;
                break;
            case 5:
                blue_tank->y_pos -= speed;
                break;
            case 6:
                blue_tank->x_pos -= speed;
                blue_tank->y_pos -= speed;
                break;
            case 7:
                blue_tank->x_pos -= speed;
                break;
            default:
                printf("wack shit %d\n", blue_tank->direction);
                break;
            }
            pthread_mutex_unlock(&tank_mutex);

            // audio update
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);
            update_pos = 1;
        }
        // 270ms
        //  Update green tank rotation
        else if (contr1_right == 0 && rotate_delay > 3 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            blue_tank->direction--;
            if (blue_tank->direction < 0)
                blue_tank->direction = 7;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }
        else if (contr1_left == 0 && rotate_delay > 3 && !data->stun)
        {

            pthread_mutex_lock(&tank_mutex);
            blue_tank->direction++;
            if (blue_tank->direction > 7)
                blue_tank->direction = 0;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }

        // Fire Bullet (make a thread to handle the bullet firing)
        if (contr1_fire == 0 && !blue_tank->fired && !data->stun)
        {
            printf("Fire bullet\n");

            pthread_create(&blue_bullet_thread, NULL, blue_bullet_thread_fcn, (void *)data);
            pthread_detach(blue_bullet_thread);
            blue_tank->fired = 1;
            // audio update
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 2;
            pthread_mutex_unlock(&audio_mutex);
            usleep(90000);
        }
        // Update score and apply spinning knockback if the tank is hit

        // Write to the hardware
        if (update_pos)
        {
            printf("updating pos\n");
            printf("x: %d\n", blue_tank->x_pos);
            printf("y: %d\n", blue_tank->y_pos);

            tank_loc_t *loc = (tank_loc_t *)malloc(sizeof(loc));
            // pthread_mutex_lock(&tank_mutex);
            //  updates the hitboxes
            update_tank_hitbox(blue_tank);
            check_obstacle_collision(blue_tank, 5);

            // update with the new values
            loc->x = blue_tank->x_pos;
            loc->y = blue_tank->y_pos;

            set_bloc(loc);
            // pthread_mutex_unlock(&tank_mutex);
            update_pos = 0;
            free(loc);
        }
        else if (update_dir)
        {
            // printf("updating dir\n");
            //  pthread_mutex_lock(&tank_mutex);
            set_tank_data(1, green_tank->direction, 1, blue_tank->direction);
            // pthread_mutex_unlock(&tank_mutex);
            update_dir = 0;
        }
        if (data->stun)
            update_dir = 1;
        // Sleep for a short period to control game speed
        rotate_delay++;
        usleep(100000);
    }
    return NULL;
}

void *invis_green_tank_thread(void *arg)
{
    uint16_t controls = 0;
    int contr2_up, contr2_left, contr2_right, contr2_down, contr2_fire;
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *green_tank = data->gtank;
    game_tank_data_t *blue_tank = data->btank;
    pthread_t green_bullet_thread;
    green_tank->direction = 0;
    green_tank->visible = 0;

    bool update_pos = 1;
    bool update_dir = 1;
    green_tank->x_pos = 46;
    green_tank->y_pos = 232;

    int rotate_delay = 0;
    // Green tank control loop
    while (!stop_threads)
    {
        // Read controls and update tank positions, directions, and bullet firing
        controls = read_controls();
        contr2_fire = (controls >> 4) & 1;
        contr2_down = (controls >> 3) & 1;
        contr2_right = (controls >> 2) & 1;
        contr2_left = (controls >> 1) & 1;
        contr2_up = (controls >> 0) & 1;

        int speed = 3;
        // Update green tank position
        if (contr2_up == 0 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            switch (green_tank->direction)
            {
            case 0:
                green_tank->x_pos -= speed;
                green_tank->y_pos += speed;
                break;
            case 1:
                green_tank->y_pos += speed;
                break;
            case 2:
                green_tank->x_pos += speed;
                green_tank->y_pos += speed;
                break;
            case 3:
                green_tank->x_pos += speed;
                break;
            case 4:
                green_tank->x_pos += speed;
                green_tank->y_pos -= speed;
                break;
            case 5:
                green_tank->y_pos -= speed;
                break;
            case 6:
                green_tank->x_pos -= speed;
                green_tank->y_pos -= speed;
                break;
            case 7:
                green_tank->x_pos -= speed;
                break;
            default:
                printf("wack shit %d\n", green_tank->direction);
                break;
            }
            pthread_mutex_unlock(&tank_mutex);
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);
            update_pos = 1;
        }

        // Update green tank rotation
        else if (contr2_right == 0 && rotate_delay > 3 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            green_tank->direction--;
            if (green_tank->direction < 0)
                green_tank->direction = 7;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }
        else if (contr2_left == 0 && rotate_delay > 3 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);

            green_tank->direction++;
            if (green_tank->direction > 7)
                green_tank->direction = 0;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }

        // Fire Bullet (make a thread to handle the bullet firing)
        if (contr2_fire == 0 && !green_tank->fired && !data->stun)
        {
            printf("Fire bullet\n");
            pthread_create(&green_bullet_thread, NULL, green_bullet_thread_fcn, (void *)data);
            pthread_detach(green_bullet_thread);
            green_tank->fired = 1;
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 2;
            pthread_mutex_unlock(&audio_mutex);

            green_tank->visible = 1;
            set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
            usleep(1000000);
            green_tank->visible = 0;
            set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
        }

        // Update score and apply spinning knockback if the tank is hit

        // Write to the hardware
        if (update_pos)
        {
            printf("updating pos\n");
            printf("x: %d\n", green_tank->x_pos);
            printf("y: %d\n", green_tank->y_pos);

            tank_loc_t *loc = (tank_loc_t *)malloc(sizeof(loc));
            // pthread_mutex_lock(&tank_mutex);
            //  updates the hitboxes
            update_tank_hitbox(green_tank);
            bool bounce = check_obstacle_collision(green_tank, 5);
            if (bounce)
            {
                green_tank->visible = 1;
                set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
                usleep(500000);
                green_tank->visible = 0;
                set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
            }

            // update with the new values
            loc->x = green_tank->x_pos;
            loc->y = green_tank->y_pos;

            set_gloc(loc);
            // pthread_mutex_unlock(&tank_mutex);
            update_pos = 0;
            free(loc);
        }
        else if (update_dir)
        {
            printf("updating dir\n");
            //  pthread_mutex_lock(&tank_mutex);
            set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
            // pthread_mutex_unlock(&tank_mutex);
            update_dir = 0;
        }
        // Sleep for a short period to control game speed
        if (data->stun)
            update_dir = 1;
        rotate_delay++;
        usleep(100000);
    }
    return NULL;
}

void *invis_blue_tank_thread(void *arg)
{
    uint16_t controls = 0;
    int contr1_up, contr1_left, contr1_right, contr1_down, contr1_fire;
    game_data_t *data = (game_data_t *)arg;
    game_tank_data_t *green_tank = data->gtank;
    game_tank_data_t *blue_tank = data->btank;
    pthread_t blue_bullet_thread;
    blue_tank->direction = 0;
    blue_tank->visible = 0;

    bool update_pos = 1;
    bool update_dir = 1;
    blue_tank->x_pos = 571;
    blue_tank->y_pos = 232;
    blue_tank->fired = 0;
    int rotate_delay = 0;
    // set_tank_data(0, green_tank->direction, blue_tank->visible, blue_tank->direction);
    //  Green tank control loop
    while (!stop_threads)
    {
        // Read controls and update tank positions, directions, and bullet firing
        controls = read_controls();
        contr1_fire = (controls >> 9) & 1;
        contr1_down = (controls >> 8) & 1;
        contr1_right = (controls >> 7) & 1;
        contr1_left = (controls >> 6) & 1;
        contr1_up = (controls >> 5) & 1;

        // printf("contr2_up: %d\n", contr2_up);
        // printf("contr2_left: %d\n", contr2_left);
        // printf("contr2_right: %d\n", contr2_right);
        // printf("contr2_down: %d\n", contr2_down);
        // printf("contr2_fire: %d\n", contr2_fire);

        int speed = 3;

        // Update blue tank position
        if (contr1_up == 0 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            switch (blue_tank->direction)
            {
            case 0:
                blue_tank->x_pos -= speed;
                blue_tank->y_pos += speed;
                break;
            case 1:
                blue_tank->y_pos += speed;
                break;
            case 2:
                blue_tank->x_pos += speed;
                blue_tank->y_pos += speed;
                break;
            case 3:
                blue_tank->x_pos += speed;
                break;
            case 4:
                blue_tank->x_pos += speed;
                blue_tank->y_pos -= speed;
                break;
            case 5:
                blue_tank->y_pos -= speed;
                break;
            case 6:
                blue_tank->x_pos -= speed;
                blue_tank->y_pos -= speed;
                break;
            case 7:
                blue_tank->x_pos -= speed;
                break;
            default:
                printf("wack shit %d\n", blue_tank->direction);
                break;
            }
            pthread_mutex_unlock(&tank_mutex);

            // audio update
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);
            update_pos = 1;
        }
        // 270ms
        //  Update green tank rotation
        else if (contr1_right == 0 && rotate_delay > 3 && !data->stun)
        {
            pthread_mutex_lock(&tank_mutex);
            blue_tank->direction--;
            if (blue_tank->direction < 0)
                blue_tank->direction = 7;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }
        else if (contr1_left == 0 && rotate_delay > 3 && !data->stun)
        {

            pthread_mutex_lock(&tank_mutex);
            blue_tank->direction++;
            if (blue_tank->direction > 7)
                blue_tank->direction = 0;
            pthread_mutex_unlock(&tank_mutex);

            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 3;
            pthread_mutex_unlock(&audio_mutex);

            update_dir = 1;
            rotate_delay = 0;
        }

        // Fire Bullet (make a thread to handle the bullet firing)
        if (contr1_fire == 0 && !blue_tank->fired && !data->stun)
        {
            printf("Fire bullet\n");

            pthread_create(&blue_bullet_thread, NULL, blue_bullet_thread_fcn, (void *)data);
            pthread_detach(blue_bullet_thread);
            blue_tank->fired = 1;
            // audio update
            pthread_mutex_lock(&audio_mutex);
            data->audio_select = 2;
            pthread_mutex_unlock(&audio_mutex);

            blue_tank->visible = 1;
            set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
            usleep(1000000);
            blue_tank->visible = 0;
            set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
        }
        // Update score and apply spinning knockback if the tank is hit

        // Write to the hardware
        if (update_pos)
        {
            printf("updating pos\n");
            printf("x: %d\n", blue_tank->x_pos);
            printf("y: %d\n", blue_tank->y_pos);

            tank_loc_t *loc = (tank_loc_t *)malloc(sizeof(loc));
            // pthread_mutex_lock(&tank_mutex);
            //  updates the hitboxes
            update_tank_hitbox(blue_tank);
            bool bounce = check_obstacle_collision(blue_tank, 5);
            if (bounce)
            {
                blue_tank->visible = 1;
                set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
                usleep(500000);
                blue_tank->visible = 0;
                set_tank_data(green_tank->visible, green_tank->direction, blue_tank->visible, blue_tank->direction);
            }

            // update with the new values
            loc->x = blue_tank->x_pos;
            loc->y = blue_tank->y_pos;

            set_bloc(loc);
            // pthread_mutex_unlock(&tank_mutex);
            update_pos = 0;
            free(loc);
        }
        else if (update_dir)
        {
            // printf("updating dir\n");
            //  pthread_mutex_lock(&tank_mutex);
            set_tank_data(1, green_tank->direction, 1, blue_tank->direction);
            // pthread_mutex_unlock(&tank_mutex);
            update_dir = 0;
        }
        if (data->stun)
            update_dir = 1;
        // Sleep for a short period to control game speed
        rotate_delay++;
        usleep(100000);
    }
    return NULL;
}
