#include <raylib.h>
#include <stdlib.h>
#include <stdio.h>

/* typedef struct map_t { */

/* } Map; */

int screenHeight = 800;
int screenWidth = 800;
int fps = 60;
int rows = 10;
int cols = 10;
int size;
int food_x;
int food_y;
int score = 0;
char score_text[16] = "0";

typedef enum directions {
    UP,
    DOWN,
    LEFT,
    RIGHT
} Direction;

typedef struct snake_body {
    int x;
    int y;
} SnakeBody;

typedef struct snake_t {
    SnakeBody *body;
    int max_length;
    int length;
    int direction;
    int frame_duration;
    bool alive;
} Snake;

Snake make_snake(int max_len, int speed){
    Snake snek;
    if (max_len < 3){
        snek.alive = false;
        snek.length = 0;
        return snek;
    }

    snek.body = (SnakeBody*) calloc(max_len, sizeof(SnakeBody));
    snek.max_length = max_len;
    snek.length = 3;
    snek.body[0].x = GetRandomValue(0, cols - 1);
    snek.body[0].y = GetRandomValue(0, rows - 1);
    snek.frame_duration = fps / speed;
    snek.direction = snek.body[0].x < cols / 2 ? RIGHT : LEFT;
    snek.alive = true;
    snek.body[1] = snek.body[2] = snek.body[0];
    snek.body[1].x += snek.direction == LEFT ? 1 : -1;
    snek.body[2].x += snek.direction == LEFT ? 2 : -2;
    return snek;
}

void free_snake(Snake* s){
    free(s->body);
}

void grow_snake(Snake* s){
    int i = s->length;
    if (i < s->max_length){
        s->body[i] = (SnakeBody) {
            2 * s->body[i - 1].x - s->body[i - 2].x,
            2 * s->body[i - 1].y - s->body[i - 2].y
        };

        s->length++;
    }
}

void draw_snake(Snake* s){
    for (int i = 0; i < s->length; i++){
        DrawRectangle(size * s->body[i].x, size * s->body[i].y, size, size, BLACK);
    }
    return ;
}

bool collides_with_snake(Snake* s, int x, int y){
    for (int i = 0; i < s->length; i++){
        if (s->body[i].x == x && s->body[i].y == y){
            return true;
        }
    }
    return false;
}

void place_food(Snake* s){
    do {
        food_x = GetRandomValue(0, cols - 1);
        food_y = GetRandomValue(0, rows - 1);
    } while (collides_with_snake(s, food_x, food_y));
}

void move_snake(Snake* s){
    SnakeBody temp1 = s->body[0];
    SnakeBody temp2;
    switch (s->direction){
    case LEFT:
        s->body[0].x--;
        if (s->body[0].x < 0 || s->body[0].x >= cols){
            s->alive = false;
        }
        break;
    case RIGHT:
        s->body[0].x++;
        if (s->body[0].x < 0 || s->body[0].x >= cols){
            s->alive = false;
        }
        break;
    case UP:
        s->body[0].y--;
        if (s->body[0].y < 0 || s->body[0].y >= rows){
            s->alive = false;
        }
        break;
    case DOWN:
        s->body[0].y++;
        if (s->body[0].x < 0 || s->body[0].y >= rows){
            s->alive = false;
        }
        break;
    }
    for (int i = 1; i < s->length; i++){
        if (s->body[0].x == s->body[i].x &&
            s->body[0].y == s->body[i].y){
            s->alive = false;
            break;
        }
        temp2 = s->body[i];
        s->body[i] = temp1;
        temp1 = temp2;
    }
}


/* int frame = 0; */
void update_snake(Snake* s){
    static int frame = 0;
    if (frame++ > s->frame_duration){
        move_snake(s);
        frame = 0;
    }
    int key;
    while ((key = GetKeyPressed()) != 0){
        switch (key){
        case KEY_W:
            s->direction = UP;
            break;
        case KEY_A:
            s->direction = LEFT;
            break;
        case KEY_S:
            s->direction = DOWN;
            break;
        case KEY_D:
            s->direction = RIGHT;
            break;
        case KEY_G:
            grow_snake(s);
        }
    }
    if (collides_with_snake(s, food_x, food_y)){
        food_x = food_y = -1;
        score++;
        snprintf(score_text, 16, "%d", score);
        grow_snake(s);
        place_food(s);
    }
}

void draw_grid(int size){
    /* DrawGrid(10, screenWidth / 10); */

    for (int i = 0; i < rows; i++){
        DrawLine(0, i * size, screenWidth, i * size, BLACK);
    }
    for (int j = 0; j < cols; j++){
        DrawLine(j * size, 0, j * size, screenHeight, BLACK);
    }
}

void calculate_tile_size(){
    int width = screenWidth / cols;
    int height = screenHeight / rows;
    size = height > width ? width : height;
}

void init_game(){
    InitWindow(screenHeight, screenHeight, "Snek");
    SetTargetFPS(fps);
    calculate_tile_size();
}

int main(void){

    init_game();
    Snake snek = make_snake(100, 5);
    int b;
    while (!WindowShouldClose()){

        if (snek.alive){
            update_snake(&snek);
        } else {
            DrawText("Died!", screenWidth / 2, screenHeight / 2, 20, BLACK);
        }
        if (food_x >= 0){
            DrawRectangle(size * food_x, size * food_y, size, size, RED);
        }

        if (IsKeyPressed(KEY_R)){
            if (b){
                screenWidth = screenHeight = 500;
                /* MinimizeWindow(); */
            } else {
                screenWidth = screenHeight = 800;
                /* MaximizeWindow(); */
            }
            SetWindowSize(screenWidth, screenHeight);
            calculate_tile_size();
            b = !b;
        }

        BeginDrawing();

        ClearBackground(WHITE);
        draw_grid(size);
        draw_snake(&snek);
        DrawText(score_text, 0, 0, 20, BLACK);

        EndDrawing();
    }

    free_snake(&snek);

    CloseWindow();

    return 0;
}