#pragma comment(lib, "SDL.lib")
#pragma comment(lib, "SDLmain.lib")
#pragma comment(lib, "SDL_TTF.lib")

#include <stack>		// using the STL stack for function pointers
#include "SDL.h"
#include "SDL_TTF.h"
#include "Defines.h"

using namespace std;

struct StateStruct
{
	void (*StatePointer)();
};

stack<StateStruct> g_StateStack;
SDL_Surface* g_Bitmap = NULL;  // Our background image
SDL_Surface* g_Window = NULL;  // Our back buffer
SDL_Event g_Event;             // An SDL event structure for input
int g_Timer;                   // Our timer is just an integer

// Functions to handle the three states of the game 
void Menu();
void Game();
void Exit();

// Init and Shutdown functions
void Init();
void Shutdown();

// Helper functions for the game state functions
void DrawBackground();
void ClearScreen();
void DisplayText(string text, int x, int y, int size,
            int fR, int fG, int fB, int bR, int bG, int bB);
void HandleMenuInput();
void HandleGameInput();
void HandleExitInput();

int main(int argc, char **argv)
{
    Init();

    // Our game loop is just a while loop that breaks when our state stack is empty.
    while (!g_StateStack.empty())
    {
        g_StateStack.top().StatePointer(); 
    }

    Shutdown();

    return 0;
}
// This function initializes our game. 
void Init()
{
    // Initiliaze SDL video and our timer. 
    SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER);
    // Setup our window's dimensions, bits-per-pixel (0 tells SDL to choose for us),       
    // and video format (SDL_ANYFORMAT leaves the decision to SDL). This function
    // returns a pointer to our window which we assign to g_Window. 
    g_Window = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_ANYFORMAT); 
    // Set the title of our window. 
    SDL_WM_SetCaption(WINDOW_CAPTION, 0);
    // Get the number of ticks since SDL was initialized. 
    g_Timer = SDL_GetTicks();

    // Fill our bitmap structure with information. 
    g_Bitmap = SDL_LoadBMP("data/background.bmp"); 

    // We start by adding a pointer to our exit state, this way
    // it will be the last thing the player sees of the game. 
    StateStruct state;
    state.StatePointer = Exit;
    g_StateStack.push(state);

    // Then we add a pointer to our menu state, this will 
    // be the first thing the player sees of our game. 
    state.StatePointer = Menu;
    g_StateStack.push(state);

    // Initialize the true type font library. 
    TTF_Init();
}

// This function shuts down our game. 
void Shutdown()
{
    // Shutdown the true type font library.
    TTF_Quit();

    // Free our surfaces. 
    SDL_FreeSurface(g_Bitmap);
    SDL_FreeSurface(g_Window);

    // Tell SDL to shutdown and free any resources it was using. 
    SDL_Quit();
}
// This function handles the game's main menu. From here 
// the player can select to enter the game, or quit. 
void Menu()
{
    // Here we compare the difference between the current time and the last time we 
    // handled a frame. If FRAME_RATE amount of time has, it's time for a new frame. 
    if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
    {
        HandleMenuInput();

        // Make sure nothing from the last frame is still drawn.
        ClearScreen();

        DisplayText("Start (G)ame", 350, 250, 12, 255, 255, 255, 0, 0, 0);
        DisplayText("(Q)uit Game", 350, 270, 12, 255, 255, 255, 0, 0, 0);

        // Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen. 
        SDL_UpdateRect(g_Window, 0, 0, 0, 0);

        // We've processed a frame so we now need to record the time at which we did it.
        // This way we can compare this time with the next time our function gets called and 
        // see if enough time has passed between calls.
        g_Timer = SDL_GetTicks();
    } 
}

// This function handles the main game. We'll control the
// drawing of the game as well as any necessary game logic. 
void Game()
{ 
    // Here we compare the difference between the current time and the last time we
    // handled a frame. If FRAME_RATE amount of time has, it's time for a new frame. 
    if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
    {
        HandleGameInput();

        // Make sure nothing from the last frame is still drawn. 
        ClearScreen();

        // Draw the background of our 'game'.
        DrawBackground();

        // Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen. 
        SDL_UpdateRect(g_Window, 0, 0, 0, 0);

        // We've processed a frame so we now need to record the time at which we did it.
        // This way we can compare this time with the next time our function gets called and 
        // see if enough time has passed between calls.
        g_Timer = SDL_GetTicks();
    } 
}

// This function handles the game's exit screen. It will display 
// a message asking if the player really wants to quit.
void Exit()
{     
    // Here we compare the difference between the current time and the last time we
    // handled a frame. If FRAME_RATE amount of time has, it's time for a new frame.
    if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
    {
        HandleExitInput();
        // Make sure nothing from the last frame is still drawn.
        ClearScreen();

        DisplayText("Quit Game (Y or N)?", 350, 250, 12, 255, 255, 255, 0, 0, 0);

        // Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen. 
        SDL_UpdateRect(g_Window, 0, 0, 0, 0);

        // We've processed a frame so we now need to record the time at which we did it. 
        // This way we can compare this time with the next time our function gets called and 
        // see if enough time has passed between calls. 
        g_Timer = SDL_GetTicks();
    } 
}
// This function draws the background 
void DrawBackground() 
{
    // These structures tell SDL_BlitSurface() the location of what
    // we want to blit and the destination we want it blitted to. 
    // Presently, we blit the entire surface to the entire screen.
    SDL_Rect source = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT }; 
    SDL_Rect destination = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };

    // This just 'block-image transfers' our bitmap to our window.
    SDL_BlitSurface(g_Bitmap, &source, g_Window, &destination);
}
// This function simply clears the back buffer to black. 
void ClearScreen()
{
    // This function just fills a surface with a given color. The
    // first 0 tells SDL to fill the whole surface. The second 0 
    // is for black. 
    SDL_FillRect(g_Window, 0, 0);
}
// This function displays text to the screen. It takes the text 
// to be displayed, the location to display it, the size of the 
// text, and the color of the text and background. 
void DisplayText(string text, int x, int y, int size, int fR,
                 int fG, int fB, int bR, int bG, int bB) 
{
    // Open our font and set its size to the given parameter. 
    TTF_Font* font = TTF_OpenFont("arial.ttf", size);

    SDL_Color foreground = { fR, fG, fB, NULL};   // Text color. 
    SDL_Color background = { bR, bG, bB, NULL};   // Color of what's behind the text.

    // This renders our text to a temporary surface. There 
    // are other text functions, but this one looks nice. 
    SDL_Surface* temp = TTF_RenderText_Shaded(font, text.c_str(), foreground, background);

    // A structure storing the destination of our text. 
    SDL_Rect destination = { x, y, 0, 0 };

    // Blit the text surface to our window surface, the NULL specifies the whole surface. 
    SDL_BlitSurface(temp, NULL, g_Window, &destination);

    // Always free memory! 
    SDL_FreeSurface(temp);

    // Close the font.
    TTF_CloseFont(font);
}
// This function receives player input and 
// handles it for the game's menu screen. 
void HandleMenuInput() 
{
    // Fill our event structure with event information. 
    if ( SDL_PollEvent(&g_Event) )
    {
        // Handle user manually closing game window 
        if (g_Event.type == SDL_QUIT)
        { 
            // While state stack isn't empty, pop 
            while (!g_StateStack.empty())
            {
                g_StateStack.pop();
            }

            return; // game is over, exit the function
        }

        // Handle keyboard input here 
        if (g_Event.type == SDL_KEYDOWN)
        {
            if (g_Event.key.keysym.sym == SDLK_ESCAPE)
            {
                g_StateStack.pop();
                return; // this state is done, exit the function 
            }
            // Quit 
            if (g_Event.key.keysym.sym == SDLK_q)
            {
                g_StateStack.pop();
                return; // game is over, exit the function 
            }
            // Start Game 
            if (g_Event.key.keysym.sym == SDLK_g)
            {
                StateStruct temp;
                temp.StatePointer = Game;
                g_StateStack.push(temp);
                return; // this state is done, exit the function 
            }
        }
    }
}

// This function receives player input and 
// handles it for the main game state. 
void HandleGameInput() 
{
    // Fill our event structure with event information.
    if ( SDL_PollEvent(&g_Event) )
    {
        // Handle user manually closing game window
        if (g_Event.type == SDL_QUIT)
        { 
            // While state stack isn't empty, pop 
            while (!g_StateStack.empty())
            {
                g_StateStack.pop();
            }

            return; // game is over, exit the function
        }

        // Handle keyboard input here 
        if (g_Event.type == SDL_KEYDOWN)
        {
            if (g_Event.key.keysym.sym == SDLK_ESCAPE)
            {
                g_StateStack.pop();

                return; // this state is done, exit the function 
            } 
        }
    }
}

// This function receives player input and 
// handles it for the game's exit screen.
void HandleExitInput() 
{
    // Fill our event structure with event information. 
    if ( SDL_PollEvent(&g_Event) )
    {
        // Handle user manually closing game window
        if (g_Event.type == SDL_QUIT)
        { 
            // While state stack isn't empty, pop
            while (!g_StateStack.empty())
            {
                g_StateStack.pop();
            }

            return; // game is over, exit the function
        }

        // Handle keyboard input here 
        if (g_Event.type == SDL_KEYDOWN)
        {
            if (g_Event.key.keysym.sym == SDLK_ESCAPE)
            {
                g_StateStack.pop();

                return; // this state is done, exit the function 
            }
            // Yes 
            if (g_Event.key.keysym.sym == SDLK_y)
            {
                g_StateStack.pop();
                return; // game is over, exit the function 
            }
            // No 
            if (g_Event.key.keysym.sym == SDLK_n)
            {
                StateStruct temp;
                temp.StatePointer = Menu;
                g_StateStack.push(temp);
                return; // this state is done, exit the function 
            }
        }
    }
}