/*
* Takes an object of a class that implements the ICallbacks interface
* and registers it handle GLUT generated events, then enters the GLUT main
* loop.
*/
void GLUTBaseRunApplication(OpenGLApplicationBase* pCallbacks)
{
	// Complete initialization of the application before rendering begins.
	pCallbacks->initialize();
	// Check to make sure an appropriate Object has been supplied.
	// The object must be a member of a class that implments the
	// ICallbacks interface.
	if (!pCallbacks)
	{
		fprintf(stderr, "%s : callbacks not specified!\n", __FUNCTION__);
		return;
	}
	// Save a reference to the ICallbacks object
	s_pOpenGLAppBase = pCallbacks;
	// Register the ICallbacks to receive events generated by GLUT.
	registerCallBacks();
	// Enter the GLUT main loop. Control will not return until the
	// window is closed.
	glutMainLoop();
	// Keeps the console window open after the main loop has been exited
	// Allows console output to be viewed after the program ends
	system( "pause" );

} // end GLUTBaseRun
/**
 * Initialisiert das Programm (inkl. I/O und OpenGL) und startet die
 * Ereignisbehandlung.
 * @param title Beschriftung des Fensters
 * @param width Breite des Fensters
 * @param height Hoehe des Fensters
 * @return ID des erzeugten Fensters, 0 im Fehlerfall
 */
int initAndStartIO (char *title, int width, int height)
{
    int windowID = 0;

    /* Kommandozeile immitieren */
    int argc = 1;
    char *argv = "cmd";

    G_Width = width;
    G_Height = height;
    G_NearPlane = 0.1;
    G_FarPlane  = 1000;

    /* Glut initialisieren */
    glutInit (&argc, &argv);

    /* FensterInitialisierung */
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    /* FensterGröße */
    glutInitWindowSize (G_Width, G_Height);
    /* FensterPosition */
    glutInitWindowPosition (0, 0);

    windowID = glutCreateWindow (title);

    if (windowID)
    {
        /* Hintergrund und so werden initialisiert (Farben) */
        if (initScene ())
        {
            G_ShaderID = loadShaders("waterVertexShader.vert", "waterFragmentShader.frag");
            G_ShaderTerrain = loadShaders("textureVertexShader.vert", "textureFragmentShader.frag");
            G_ShaderColor = loadShaders("colorVertexShader.vert", "colorFragmentShader.frag");
            G_ShaderDepth = loadShaders("textureVertexShader.vert", "textureDepthFragmentShader2.frag");

            registerCallBacks ();

            allocateMemoryStuffDepth(&G_TexCamera, &G_TexCameraDepth, &G_fboCam);

            G_sampler2dLoc          = glGetUniformLocation(G_ShaderID, "texsampler");
            G_samplerDepth2dLoc     = glGetUniformLocation(G_ShaderID, "texsamplerDepth");

            Image * imageSand, * imageGrass, * imageRocks, * imageSnow, * imageForest, * imageTundra;
            imageSand  = malloc(sizeof(Image));
            imageGrass = malloc(sizeof(Image));
            imageRocks = malloc(sizeof(Image));
            imageSnow = malloc(sizeof(Image));
            imageForest = malloc(sizeof(Image));
            imageTundra = malloc(sizeof(Image));

            loadTextureImage(imageSand,  "sand1.bmp", &G_TexImageSand );
            loadTextureImage(imageGrass, "gras2.bmp", &G_TexImageGrass);
            loadTextureImage(imageRocks, "rocks.bmp", &G_TexImageRocks);
            loadTextureImage(imageSnow, "snow.bmp", &G_TexImageSnow);
            loadTextureImage(imageForest, "forest.bmp", &G_TexImageForest);
            loadTextureImage(imageTundra, "tundra.bmp", &G_TexImageTundra);

            /* Wasser */
            glGenBuffers (1, &G_WaterBuffer);
            glBindBuffer (GL_ARRAY_BUFFER, G_WaterBuffer);
            glBufferData (GL_ARRAY_BUFFER, sizeof(*getWaterList())*WORLD_SIZE*WORLD_SIZE, getWaterList(), GL_STREAM_DRAW);
            glBindBuffer (GL_ARRAY_BUFFER, 0);

            glGenBuffers (1, &G_WaterBufferIndex);
            glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, G_WaterBufferIndex);
            glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof(*getWaterIndices())*WORLD_SIZE*WORLD_SIZE*3*2, getWaterIndices(), GL_STREAM_DRAW);
            glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);

            /* Terrain */
            glGenBuffers (1, &G_TerrainBuffer);
            glBindBuffer (GL_ARRAY_BUFFER, G_TerrainBuffer);
            glBufferData (GL_ARRAY_BUFFER, sizeof(*getTerrainList())*TERRAIN_SIZE*TERRAIN_SIZE, getTerrainList(), GL_STATIC_DRAW);
            glBindBuffer (GL_ARRAY_BUFFER, 0);

            glGenBuffers (1, &G_TerrainBufferIndex);
            glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, G_TerrainBufferIndex);
            glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof(*getTerrainIndices())*TERRAIN_SIZE*TERRAIN_SIZE*3*2, getTerrainIndices(), GL_STATIC_DRAW);
            glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);

            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glClearColor(0.0, 1.0, 1.0, 0.0);
            glClearDepth(1.0f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            /* Die Endlosschleife wird angestoßen */
            glutMainLoop ();
            windowID = 0;

            glDeleteFramebuffers(1, &G_fboCam);
            glDeleteTextures(1, &G_TexCamera);
            glDeleteTextures(1, &G_TexCameraDepth);

        }
        else
        {
            glutDestroyWindow (windowID);
            windowID = 0;
        }
    }
    return windowID;
}
/**
 * Initializes the program (including io and OpenGL) and starts the
 * event handling.
 */
int initAndStartIO (char *title, int width, int height)
{
    int windowID = 0;

    G_Width = width;
    G_Height = height;
    G_WindowTitle = title;
    int argc = 1;
    char *argv = "cmd";

    G_NearPlane = 0.5;
    G_FarPlane  = 50.0;

    /* initialize GLUT */
    glutInit (&argc, &argv);

    /* initialize window */
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    /* window size */
    glutInitWindowSize (G_Width, G_Height);
    /* window position */
    glutInitWindowPosition (0, 0);

    windowID = glutCreateWindow (G_WindowTitle);

    if (windowID)
    {

        /* Background color and other stuff initialization */
        if (initScene ())
        {
            printf ("--> Load shaders...\n"); fflush(stdout);

            registerCallBacks ();

            /*
             * Load shaders from file
             */
            G_ShaderTexture = loadShaders("textureVertexShader.vert", "textureFragmentShader.frag");
            G_ShaderColor = loadShaders("colorVertexShader.vert", "colorFragmentShader.frag");
            G_ShaderPosColor = loadShaders("posColorVertexShader.vert", "colorFragmentShader.frag");
            G_ShaderDepthTexture = loadShaders("textureVertexShader.vert", "textureDepthFragmentShader.frag");

            printf ("--> Shaders are loaded.\n"); fflush(stdout);

            /*
             * Load texture from file
             */
            Image * imageRocks;
            imageRocks = malloc(sizeof(Image));
            loadTextureImage(imageRocks, "sunset-red.bmp", &G_TexImageRocks);

            /*
             * Create buffer for the object we want to draw.
             */
            glGenBuffers(1, &G_ObjectsBuffer);
            glBindBuffer(GL_ARRAY_BUFFER, G_ObjectsBuffer);
            glBufferData(GL_ARRAY_BUFFER, sizeof(G_Objects)*sizeof(GLfloat), G_Objects, GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);

            allocateMemoryStuffDepth(&G_TexCamera, &G_TexCameraDepth, &G_fboCam);

            printf ("--> Finished Initialization.\n"); fflush(stdout);

            /* Endless loop is started. */
            glutMainLoop ();
            windowID = 0;


        } else {
            glutDestroyWindow (windowID);
            return 0;
        }
    } else {
        return 0;
    }
    glutDestroyWindow (windowID);

    return 1;
}