#ifdef _WINDOWS
#include <windows.h>
#endif

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_mixer.h>
#include <iostream>
#include <sstream>
#include <stdio.h>

#include "RUBEScene.h"
#include "DestroyBodyScene.h"
#include "PinballScene.h"
#include "PlanetCuteScene.h"

using namespace std;


// The window dimensions are necessary for functions that convert between
// pixel and physics world coordinates. The window instance is a local
// variable inside the main() function, but we can access like this.
SDL_Window* window = NULL;
SDL_Point getWindowSize() {
    SDL_Point v;
    SDL_GetWindowSize(window, &v.x, &v.y);
    return v;
}

SDL_Renderer* renderer = NULL;
SDL_Renderer* getRenderer() {
    return renderer;
}

// The currently running scene
int currentTestNum = 0;
RUBEScene* scene = NULL;
RUBEScene* getCurrentScene() {
    return scene;
}

// This function sets up the appropriate scene, using currentTestNum as
// an id number to select scenes.
#define NUM_TESTS 5
void changeTest(int incOrDec)
{
    // get rid of the old scene if there was one
    if ( scene )
        delete scene;
    scene = NULL;

    // change the current test number
    currentTestNum = (currentTestNum + NUM_TESTS + incOrDec) % NUM_TESTS;

    // load the appropriate scene type
    if ( currentTestNum == 0 ) {
        scene = new RUBEScene();
        scene->load("jointTypes.json");
        scene->setViewCenter( b2Vec2(0,3) );
        scene->setViewVerticalSpan( 10 );
    }
    else if ( currentTestNum == 1 ) {
        scene = new RUBEScene();
        scene->load("images.json");
        scene->setViewCenter( b2Vec2(0,0) );
        scene->setViewVerticalSpan( 32 );
    }
    else if ( currentTestNum == 2 ) {
        scene = new DestroyBodyScene();
        scene->load("images.json");
        scene->setViewCenter( b2Vec2(0,0) );
        scene->setViewVerticalSpan( 32 );
    }
    else if ( currentTestNum == 3 ) {
        scene = new PinballScene();
        scene->load("pinball.json");
        scene->setViewCenter( b2Vec2(0,24) );
        scene->setViewVerticalSpan( 50 );
    }
    else if ( currentTestNum == 4 ) {
        scene = new PlanetCuteScene();
        scene->load("planetcute.json");
        scene->setViewCenter( b2Vec2(0,0) );
        scene->setViewVerticalSpan( 8 );
    }
}

// a convenience function to display something useful about each scene
string getInfoString()
{
    switch (currentTestNum) {
    case 0: return "Simple scene to check joint types and coordinate conversions (world/pixel).";
    case 1: return "Simple scene to check image loading.";
    case 2: return "This scene demonstrates removing bodies. Click dynamic bodies to remove them.";
    case 3: return "Simple pinball game - use Z and X keys to play.";
    case 4: return "Simple platform game.";
    default:;
    }
    return "";
}


int main(int argc, char* argv[])
{
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
        std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
        return 1;
    }

    TTF_Init();

    window = SDL_CreateWindow("rube SDL2", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
    if (window == NULL){
        std::cout << "SDL_CreateWindow error: " << SDL_GetError() << std::endl;
        return 1;
    }

    renderer = SDL_CreateRenderer(window, -1, 0);

    int frameNumber = 0;
    int lastFPSFrame = 0;

    TTF_Font * font = TTF_OpenFont("UbuntuMono-R.ttf", 16);
    if ( !font ) {
        std::cout << "TTF_Font error: " << SDL_GetError() << std::endl;
        TTF_Quit();
        SDL_Quit();
        exit(1);
    }

    //Initialize SDL_mixer
    if( Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, 1, 735 ) == -1 )
    {
        std::cout << "Mix_OpenAudio error: " << SDL_GetError() << std::endl;
        TTF_Quit();
        SDL_Quit();
        exit(1);
    }

    // load the first scene
    changeTest(0);

    SDL_Surface * surface = NULL;
    SDL_Texture * texture = NULL;
    SDL_Rect dstrect;
    int texW = 0;
    int texH = 0;

    bool quit = false;
    SDL_Event event;
    Uint32 lastPhysicsStepTime = 0;
    Uint32 lastFPSUpdateTime = 0;

    while (!quit) {

        while ( SDL_PollEvent(&event) ) {

            switch (event.type)
            {
            case SDL_MOUSEBUTTONDOWN:
                scene->mouseDown(event.button);
                break;
            case SDL_MOUSEBUTTONUP:
                scene->mouseUp(event.button);
                break;
            case SDL_MOUSEMOTION:
                scene->mouseMove(event.motion);
                break;
            case SDL_MOUSEWHEEL:
                scene->mouseWheel(event.wheel);
                break;
            case SDL_KEYDOWN:
                if ( event.key.keysym.sym == SDLK_ESCAPE )
                    quit = true;
                if ( event.key.keysym.sym == SDLK_LEFT ) {
                    changeTest(-1);
                    lastFPSUpdateTime = 0;
                }
                if ( event.key.keysym.sym == SDLK_RIGHT ) {
                    changeTest(1);
                    lastFPSUpdateTime = 0;
                }
                scene->keyDown(event.key);
                break;
            case SDL_KEYUP:
                scene->keyUp(event.key);
                break;
            case SDL_QUIT:
                quit = true;
                break;
            }
        }

        // check if enough time has passed to do a physics step
        Uint32 now = SDL_GetTicks();
        if ( now - lastPhysicsStepTime > 16 ) { // 60fps (1000 / 60 is approximately 16)
            scene->step();
            lastPhysicsStepTime = now;
        }
        else
            SDL_Delay(1);

        // clear screen with black
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);

        scene->render();

        // update fps counter and text overlay at one second intervals
        if ( now - lastFPSUpdateTime > 1000 || lastFPSUpdateTime == 0 ) {
            char buf[256];
            sprintf(buf, "%s\n%s\nFPS: %d", getInfoString().c_str(), "Use left/right arrow keys to change scenes, R to reset.", frameNumber - lastFPSFrame);

            SDL_DestroyTexture(texture); // inefficient
            SDL_FreeSurface(surface);

            const SDL_Color color = { 0, 192, 0 };
            surface = TTF_RenderText_Blended_Wrapped(font, buf, color, 800);
            texture = SDL_CreateTextureFromSurface(renderer, surface);

            SDL_QueryTexture(texture, NULL, NULL, &texW, &texH);
            dstrect.x = 5;
            dstrect.y = 0;
            dstrect.w = texW;
            dstrect.h = texH;

            lastFPSFrame = frameNumber;
            lastFPSUpdateTime = now;
        }

        if ( texture ) {
            //glEnable(GL_BLEND);
            SDL_RenderCopy(renderer, texture, NULL, &dstrect);

        }

        SDL_RenderPresent(renderer);

        frameNumber++;
    }

    SDL_DestroyTexture(texture);
    SDL_FreeSurface(surface);
    TTF_CloseFont(font);

    if ( scene )
        delete scene;

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    Mix_Quit();
    TTF_Quit();
    SDL_Quit();

    return EXIT_SUCCESS;
}



