// Preprocessing:
#include <list>
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <stdio.h>
#include <windows.h>
using namespace std;

// Global constants:
const int  WIDTH   =  30;           // Board width
const int  HEIGHT  =  20;           // Board height
const int  MILISEC_PER_FRAME = 100; // 30 ms/frame = approx 33 fps

// "Graphics":
const char P_SNAKE = 'o';  // Picture for snake body
const char P_FOOD  = '~';  // Picture for food
const char P_WALL  = 'X';  // Picture for wall
const char P_BLANK = ' ';  // Picture for empty square

// Buttons:
const int  B_UP    = 'W';
const int  B_DOWN  = 'S';
const int  B_LEFT  = 'A';
const int  B_RIGHT = 'D';
const int  B_QUIT  = 'M';

// Directions:
const int  D_UP    = 0;
const int  D_DOWN  = 1;
const int  D_LEFT  = 2;
const int  D_RIGHT = 3;
const int  DX[4]   = { 0, 0, -1, 1};
const int  DY[4]   = {-1, 1,  0, 0};
const int  Opp[4]  = {D_DOWN, D_UP, D_RIGHT, D_LEFT};
const int  KeyCode[4] = {B_UP, B_DOWN, B_LEFT, B_RIGHT};

// A single point:
class ZnakePoint
{
public:
    int x;
    int y;
};

// Global functions:
void GameInit();
void StartClock();
void GameProcess();
void PauseClock();
void GameQuit();

void BuildBoard();
void DrawBoard();

void LoadLevel(int lev);

void AddPointToBody(int col, int row);

// Global variables:
bool GameRunning;  // Stores whether the game is running

list<ZnakePoint> g_TheSnake;   // The snake itself

int              g_direc;      // The direction of travel
int              g_score;      // The player's score
int              g_length;     // The target length of the snake

clock_t          g_timer;      // The game timer

int              g_LastButton; // The last button pressed
char             g_LevelBoard[WIDTH][HEIGHT]; // column - row, or (x,y)
char             g_ToBeDrawn [WIDTH][HEIGHT]; // column - row, or (x,y)

int main()
{
    // Initialise game:
    GameInit();

    // While the game is running:
    while(GameRunning)
    {
        StartClock();   // Start the clock
        GameProcess();  // Do necessary processing and output
        PauseClock();   // Wait necessary time, reading input
    }

    // Unload the game:
    GameQuit();

    // And finish:
    return 0;
}

void GameInit()
{
    GameRunning = true;   // Starts the Game running
    g_score     = 0;      // Start at zero points
    g_length    = 1;
    LoadLevel(1);         // Tells which level to load initially
}

void GameQuit()
{
    // Ouput score
    // Have hi-scores list
}

void StartClock()
{
    // Set the game timer to the current time:
    g_timer = clock();
}

void GameProcess()
{
    // Button-dependant movement:
    int new_x = DX[g_LastButton] + g_TheSnake.front().x;
    int new_y = DY[g_LastButton] + g_TheSnake.front().y;

    // Check for wall:
    if(g_LevelBoard[new_x][new_y] == P_WALL)
    {
        GameRunning = false; // Game no longer working
        return;              // Do not continue any more
    }

    // Check for food:
    if(g_LevelBoard[new_x][new_y] == P_FOOD)
    {
        g_score++;
        // Plant new food
    }

    // Move Snake
    ZnakePoint NewFront;
    NewFront.x = new_x;
    NewFront.y = new_y;

    // Move Snake:
    g_TheSnake.push_front(NewFront);
    while(g_TheSnake.size() > g_length)
    {
        g_TheSnake.pop_back();
    }

    // Build board
    BuildBoard();
    DrawBoard();

}

void PauseClock()
{
    if(!GameRunning)
    {
        return; // Escape if not running
    }

    // To save the next direciton
    int temp_direc = g_direc;

    // While the frame hasn't been running long enough:
    while(clock() < g_timer + MILISEC_PER_FRAME)
    {
        for(int c=0; c<4; c++)
        {
            if(GetAsyncKeyState(KeyCode[c]))
            {
                if(g_direc != Opp[c])
                    {temp_direc = c;}
            }
        }
    }
    g_direc = temp_direc;
}

void LoadLevel(int lev)
{
    // Clear board:
    for(int q = 0; q <  WIDTH; q++){
        for(int w = 0; w < HEIGHT; w++){
        g_LevelBoard[q][w] = P_BLANK;
        }
    }

    // Set initial boundaries:
    for(int e = 0; e <  WIDTH; e++){
        g_LevelBoard[e][HEIGHT-1] = P_WALL;
        g_LevelBoard[e][0]        = P_WALL;
    }
    for(int e = 0; e < HEIGHT; e++){
        g_LevelBoard[WIDTH-1][e]  = P_WALL;
        g_LevelBoard[0][e]        = P_WALL;
    }

    switch (lev) // set values depending on which board
    {
    case 1:
        g_direc = D_UP;
        // Add initial point:
        ZnakePoint init;
        init.x = 15;
        init.y = 10;
        g_TheSnake.push_front(init);
        break;
    case 2:
        // fill in some code
        break;
    default:
        break;
    }
}

void BuildBoard()
{
    // Copy level to drawn board
    for(int p = 0; p < WIDTH; p++){
        for(int q = 0; q < HEIGHT; q++){
            g_ToBeDrawn[p][q] = g_LevelBoard[p][q];
        }
    }

    // Copy Snake to board:
    list<ZnakePoint>::iterator Itr;
    for(Itr = g_TheSnake.begin(); Itr!=g_TheSnake.end(); Itr++)
    {
        g_ToBeDrawn[(*Itr).x][(*Itr).y] = P_SNAKE;
    }

    // Copy Food to board:

}

void DrawBoard()
{
     system("CLS");             // Clear the screen
     int p,q;
     for(p = 0; p < HEIGHT; p++){
        for(q = 0; q < WIDTH; q++){
            cout<<g_ToBeDrawn[q][p];  // Output info in board
        }
        cout<<endl;             // Go to next line
     }
}
