// Includes
#include "SDL/SDL.h"
#include "time.h"

// Required Variables
const int FRAMES_PER_SECOND = 60;
const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;
const int SCREEN_BPP = 32;

SDL_Surface *screen = NULL;
SDL_Event event;

// Program Variables

struct snake {
    int x, y;
};

const int field_width = 25;
const int field_height = 25;
const int body_size = 10;
const int max_body_length = 200;
const int start_speed = 7;
const int min_speed = 2;
const int speed_divisor = 1000;

bool game_started = false;
bool game_paused = false;

snake snake_body[max_body_length+1];
snake food;
int body_length = -1;
int xvel, yvel, txvel, tyvel;
int speed;
int speed_count = 0;
int score = 0;

/* Init everything */
bool init() {

    if (SDL_Init( SDL_INIT_EVERYTHING ) == -1) {
        return false;
    }
    
    screen = SDL_SetVideoMode((field_width*body_size), (field_height*body_size), SCREEN_BPP, SDL_SWSURFACE);
    if (screen == NULL) {
        return false;    
    }
    
    SDL_WM_SetCaption("snake", NULL);

    return true;
}

/* Load the files required */
bool load_files() {

    return true;
}

/* Free everything */
void clean_up() {

    SDL_Quit();
}

void reset_snake() {
    body_length = 2;
    snake_body[0].x = (int)(field_width/2)+2;
    snake_body[0].y = (int)(field_height/2);
    
    snake_body[1].x = snake_body[0].x - 1;
    snake_body[1].y = snake_body[0].y;
    
    snake_body[2].x = snake_body[0].x - 2;
    snake_body[2].y = snake_body[0].y;
    
    xvel = 1;
    yvel = 0;
    txvel = 1;
    tyvel = 0;
    
    speed = start_speed;
    
    food.x = -1;
    food.y = -1;
    
    srand(time(NULL));
}

void draw_snake() {
    SDL_Rect drect;
    
    if (food.x != -1) {
        drect.x = food.x * body_size;
        drect.y = food.y * body_size;
        
        drect.w = body_size - 1;
        drect.h = body_size - 1;

        SDL_FillRect(screen, &drect, SDL_MapRGB(screen->format, 142, 107, 35));
    }
    
    for (int i = body_length; i >= 0; i--) {
        drect.x = snake_body[i].x * body_size;
        drect.y = snake_body[i].y * body_size;
        
        drect.w = body_size - 1;
        drect.h = body_size - 1;

        SDL_FillRect(screen, &drect, SDL_MapRGB(screen->format, 0, 255, 0));
    }
}

bool snake_check_collision() {
    // Check outside of area collision
    if (snake_body[0].x == -1 || snake_body[0].x == (field_width) ||
        snake_body[0].y == -1 || snake_body[0].y == (field_height)) {
            return true;
    }
    // Check collision with itself
    for (int i = 1; i <= body_length; i++) {
        if (snake_body[i].x == snake_body[0].x && snake_body[i].y == snake_body[0].y) {
            return true;
        }
    }
    return false;
}

void add_snake_body() {
    if (game_started && !game_paused) {
        if (body_length != max_body_length) {
            snake_body[body_length+1].x = snake_body[body_length].x;
            snake_body[body_length+1].y = snake_body[body_length].y;
            body_length++;
        }
    }
}

void add_food() {
    if (game_started && !game_paused) {
        if (food.x == -1) {
            food.x = (int)((field_width + 0.0) * (rand() / (RAND_MAX + 1.0)));
            food.y = (int)((field_height + 0.0) * (rand() / (RAND_MAX + 1.0)));
        }
    }
}

bool check_food_collision() {
    if (snake_body[0].x == food.x && snake_body[0].y == food.y) {
        return true;
    }
    return false;
}
    
void update_snake() {
    if (game_started && !game_paused) {
        // Updating movement
        if (speed_count == speed) {
            
            if (!(txvel == -(xvel))) {
                xvel = txvel;
            }
            
            if (!(tyvel == -(yvel))) {
                yvel = tyvel;
            }
            
            for (int i = body_length; i > 0; i--) {
                snake_body[i].x = snake_body[i-1].x;
                snake_body[i].y = snake_body[i-1].y;
            }
            
            snake_body[0].x += xvel;
            snake_body[0].y += yvel;
            
            if (snake_check_collision() == true) {
                body_length = -1;
                food.x = -1;
                score = 0;
                game_started = false;
                
            }

            speed_count = 0;
        }
        else {
            speed_count++;
        }
        
        // Updating dot replacement/collision/length addition
        if (check_food_collision() == true) {
            food.x = -1;
            add_snake_body();
            score = score + 100;
                
            speed = start_speed - (score/speed_divisor);
                    
            if (speed < min_speed) {
                speed = min_speed;
            }
        }
        
        if (food.x == -1) {
            add_food();
        }
        
    }
    
}
      
int main( int argc, char* args[] ) {
    bool game_is_running = true;
    int next_game_tick = int(SDL_GetTicks());
    int sleep_time = 0;
    
    int frame = 0;
    int s_ticks;
    int fps = 0;
    char buffer[50] = "FPS: 0";
    
    int tspeed;
    
    if (init() == false) {
        return 1;    
    }

    if (load_files() == false) {
        return 1;    
    }
    
    reset_snake();
    body_length = -1;
    
    while (game_is_running) {
        SDL_FillRect( screen, &screen->clip_rect, SDL_MapRGB( screen->format, 0x00, 0x00, 0x00 ) ); 
        
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                game_is_running = false;
            }
            
            if (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_RETURN) {
                if (game_started == false) {
                    game_started = true;
                    reset_snake();
                }
                else if (game_started == true) {
                    if (game_paused == false) {
                        game_paused = true;
                    }
                    else if (game_paused == true) {
                        game_paused = false;
                    }
                }
            }
            
            if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_UP) {
                txvel = 0;
                tyvel = -1;
            }
            else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_DOWN) {
                txvel = 0;
                tyvel = 1;
            }
            else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_LEFT) {
                txvel = -1;
                tyvel = 0;
            }
            else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_RIGHT) {
                txvel = 1;
                tyvel = 0;
            }
            
            if (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_SPACE) {
                add_snake_body();
            }
        }
        
        update_snake();
        draw_snake();
    
        if (SDL_Flip(screen) == -1) {
            fprintf(stderr, "Unable to put buffer to screen.");
            return 1;
        }

        // FPS Contraint
        next_game_tick += SKIP_TICKS;
        sleep_time = next_game_tick - int(SDL_GetTicks());
        
        if (sleep_time >= 0) {
            SDL_Delay(sleep_time);
        }
        
        // FPS Counter
        frame++;
        if( SDL_GetTicks() - s_ticks > 1000 ) {
            
            fps = frame;
            
            frame = 0;
            s_ticks = SDL_GetTicks();
        }
        
        tspeed = start_speed - speed + 1;
        
        if (!game_started) {
            sprintf(buffer, "snake [ENTER TO START]", fps);
        }
        else if (game_paused) {
            sprintf(buffer, "snake [PAUSED]", fps);
        }
        else if (game_started && !game_paused) {
            sprintf(buffer, "snake [%i] [%i speed]", score, tspeed);
        }
        
        SDL_WM_SetCaption( buffer, NULL );
    }
    
    clean_up(); 
    return 0;
}
