/*
 * Graphics.cc:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.com
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

#include <cstdlib>
#include <fstream>

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

#include <SDL/SDL_mixer.h>
#include <SDL/SDL.h>

#include "DG.h"

#include "widgets/ScrollpaneWidget.h"

#include "Graphics.h"

#include "widgets/EventListeners.h"
#include "widgets/Widget.h"
#include "widgets/ConsoleWidget.h"
#include "widgets/ButtonWidget.h"
#include "widgets/LabelWidget.h"
#include "widgets/SliderWidget.h"
#include "widgets/Scrollable.h"
#include "widgets/WidgetManager.h"

#include "widgets/TabbedpaneWidget.h"
#include "widgets/ConsoleWidget.h"

#include "util/Texload.h"
#include "util/TextureFont.h"
#include "util/TextureObject.h"
#include "util/TextureManager.h"

#include "client/Client.h"
#include "client/ClientState.h"

#include "gl_stubs.h"
#include "GraphicsState.h"

const int Graphics::_preferredBpp[] = { 16, 32, 8, 0 };
GLuint Graphics::FONT_DISPLAY_LIST_IDX = 0;
static const char * _start_map = 0;

Graphics::Graphics( DG *dg, int argc, char **argv ) {

        ok = false;

        FONT_DISPLAY_LIST_IDX = 0;
        CUBE_DISPLAY_LIST_IDX = 0;
        PTR_DEFAULT_DISPLAY_LIST_IDX = 0;

        // windows users get fullscreen default
        
#ifdef WIN32
        _fullscreen = true;
        _videoMode._flags |= SDL_FULLSCREEN;
#else
        _fullscreen = false;
#endif
     
        _dg = dg;

        _loopStarted = false;
        _loopRunning = false;
        
        //
        // parse arguments
        //

	for ( int i=1; argv[i]; ++i ) {

                if ( strcmp( argv[i], "-nograb" ) == 0 ) {
                        g_state.grab_input = false;
                        continue;
                }

                if ( strcmp( argv[i], "-gamma" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                
                                g_state.gamma = atof( argv[i] );
                                
                                if ( g_state.gamma <= 0 ||
                                     g_state.gamma > 2 ) {
                                        cout << " :: gamma " << g_state.gamma 
                                             << " is out of range." << endl;
                                        exit(1);
                                }
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

                if ( strcmp( argv[i], "-multitex" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                g_state.use_multitex = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                if ( strcmp( argv[i], "-use_cva" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                g_state.use_cva = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                if ( strcmp( argv[i], "-swap_rb" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                g_state.swap_RB = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

                if ( strcmp( argv[i], "-stats" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                g_state.show_stats = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                if ( strcmp( argv[i], "-textures" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.use_textures = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
  
                if ( strcmp( argv[i], "-lightmaps" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.use_lightmaps = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

                if ( strcmp( argv[i], "-dynamic_light" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.use_dynlights = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

                if ( strcmp( argv[i], "-wireframe" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.use_wireframe = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

                if ( strcmp( argv[i], "-use_pvs" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.use_pvs = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                if ( strcmp( argv[i], "-reverse_mouse_y" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.reverse_mouse_y = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

                   
                if ( strcmp( argv[i], "-draw_walls" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.draw_walls = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                if ( strcmp( argv[i], "-draw_floors" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                cl_state.draw_floors = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

		if ( strcmp(argv[i], "-fullscreen" ) == 0 ) {
                        _fullscreen = true;
			_videoMode._flags |= SDL_FULLSCREEN;
#ifndef WIN32
                        putenv( "MESA_GLX_FX=f" );
#endif
                        continue;
		}
                
		if ( strcmp(argv[i], "-windowed" ) == 0 ) {
                        _fullscreen = false;
			_videoMode._flags &= ~SDL_FULLSCREEN;
                        continue;
		}
                
                if ( strcmp( argv[i], "-width" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                _videoMode._width = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }

                if ( strcmp( argv[i], "-height" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                _videoMode._height = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                if ( strcmp( argv[i], "-bpp" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                _videoMode._bpp = atoi( argv[i] );
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                if ( strcmp( argv[i], "-map" ) == 0 ) {
                        if ( argv[++i] != 0 ) {
                                _start_map = argv[i];
                                continue;
                        }
                        else {
                                goto no_argument;
                        }
                }
                
                continue;
        no_argument:
                cout << " :: option " << argv[i-1] << " requires an argument." << endl;
                exit( 1 );
	}

#ifdef WIN32
        // disable DirectX by default; it causes problems with some boards.
        if ( ! getenv( "SDL_VIDEODRIVER" ) ) {
                putenv( "SDL_VIDEODRIVER=windib" );
        }
#endif
        //
        // attempt to initialize SDL
        //
        
        if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) {
		fprintf(stderr,"Couldn't initialize SDL: %s\n",SDL_GetError());
		exit( 1 );
	}

        cout << " opening audio " << endl;
        
        if ( Mix_OpenAudio( 11025, AUDIO_S16, 2, 512 ) < 0 ) {
                cout << "can't open audio: " << SDL_GetError() << endl;
                SDL_Quit();
                exit( 1 );
        }

        Mix_AllocateChannels( 32 );

	SDL_WM_SetCaption( "SDL-DG", "sdl-dg" );

        //
        // setup the display attributes
        //
        
	SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
        SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

        /*
           TODO: find out why SDL_ListModes returns 0xffffffff
        SDL_Rect ** availableModes = SDL_ListModes( NULL, _videoMode._flags );

        if ( availableModes == 0 ) {
                cout << " :: No available modes for video flags." << endl;
        }

        for ( SDL_Rect *r = *availableModes; r != 0; ++r ) {
                cout << " :: AVAILABLE MODE: " << r->w << "x" << r->h << "; " << r->x << "," << r->y << endl;
        }
        */

        bool vidModeFound = false;
        
        if ( ! setVideoMode() ) {
                cout << " :: Unable to set default vid mode " << _videoMode << ", trying other preferred." << endl;
                
                for ( int i = 0; _preferredBpp[i] != 0; ++i ) {
                        _videoMode._bpp = _preferredBpp[i];
                        if ( setVideoMode() ) {
                                vidModeFound = true;
                                break;
                        }
                        else {
                                cout << " :: Failed to set preferred vidmode " << _videoMode << endl;
                        }
                }
                
                if ( vidModeFound == false ) {
                        cout << "unable to set video mode" << endl;
                        return;
                }
        }
        
        SDL_ShowCursor( 0 );

        setGrab( g_state.grab_input );

        ok = true;
}

Graphics::~Graphics() {
        cout << " ++ deleting Graphics" << endl;

        Client::shutdown();
        TextureManager::purge();

        delete[] _screenshotCache;
        
        Mix_CloseAudio();
        SDL_CloseAudio();
        SDL_Quit();
        
}

void Graphics::setGrab( bool flag ) {
        g_state.grab_input = flag;

        SDL_WM_GrabInput( flag ? SDL_GRAB_ON : SDL_GRAB_OFF );

        if ( flag )
                SDL_WarpMouse( _videoMode._width / 2, _videoMode._height / 2 );
}

void Graphics::updateGraphics() {

        //
        // if loop is not running (e.g. window is minimized, don't draw)
        //
        if ( _loopRunning == false ) {
                //
                // sleep as a failsafe to prevent runaway display loops
                //
                SDL_Delay( 25 );
                return;
        }

	double color = 0.0;
        GLenum gl_error;
        char* sdl_error;

        glDisable( GL_DITHER );
        
        //        glClearDepth( 1 );
        //        glClearColor( 1.0, 0.0, 1.0, 1.0 );
        glClearColor( 0, 0, 0, 1 );

        glDepthMask( 1 );
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // TODO: optimize widget drawing code to make high level clear unnecessary
        
        //        glClear( GL_DEPTH_BUFFER_BIT);
        glDepthMask( 0 );

        //
        // setup the 2d Orthographic projection
        //

        glMatrixMode( GL_PROJECTION );
        
        glLoadIdentity();
        
        glOrtho( 0, _videoMode._width, 
                 0, _videoMode._height,
                 -1, 1);
        
        glCullFace( GL_BACK );
        glEnable( GL_CULL_FACE );
        //
        // draw the 2d stuff
        //

        glMatrixMode( GL_MODELVIEW );
                
        glLoadIdentity();
        
        glDisable( GL_DEPTH_TEST );
        
        //
        // workaround for wierd mesa FX bug
        //

        glTranslatef( 0.375, 0.375, 0 );

        //
        // display the widget tree
        //

        _wm->display();

        //
        // display the statistics text
        //

        if ( g_state.show_stats ) {
                glColor3f( 1, 1, 1 );

                const class TextureFont * font = Widget::_font;
        
                // texture font only
        
                glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

                font->startStringDraw();
                //        Widget::_font->drawStringFragment( s.c_str(), 0, s.size() );

                glPushMatrix();
                glPushMatrix();
                glPushMatrix();

                glTranslatef( 0, _videoMode._height - 16, 0 );
                
                char buf[1024];
                sprintf( buf, " %10s: %3d", "total fps", g_state.total_fps );
                font->drawStringFragment( buf, 0, strlen( buf ) );
                
                glPopMatrix();
                glTranslatef( 0, _videoMode._height - 32, 0 );
                sprintf( buf, " %10s: %3d ms : %3d fps : %3d surfs : %3d ents", 
                         "graphics", g_state.graphics_time, g_state.graphics_fps,
                         g_state.num_surfs, g_state.num_ents );
                font->drawStringFragment( buf, 0, strlen( buf ) );
                
                glPopMatrix();
                glTranslatef( 0, _videoMode._height - 48, 0 );
                sprintf( buf, " %10s: %3d ms: %4d fps, %04d deficit", "physics", 
                         g_state.physics_time,
                         g_state.physics_fps,
                         g_state.physics_deficit );
                font->drawStringFragment( buf, 0, strlen( buf ) );
                
                font->endStringDraw();

                glPopMatrix();
        }
        
        //
        // draw the mouse pointer cursor
        //
        
        if ( _dg->hasInputFocus() && g_state.show_cursor ) {
                
                int x, y;
                SDL_GetMouseState( &x, &y );
                y = _videoMode._height - y;
                
                glTranslatef( x, y, 0 );
                        
                const class Draggable * d = _wm->getDraggable();
                        
                if ( d && d->getIcon() ) {
                        glEnable( GL_TEXTURE_2D );
                        
                        glBindTexture( GL_TEXTURE_2D, d->getIcon()->getIndex() );
                        glEnable( GL_BLEND );
                        glEnable( GL_ALPHA_TEST );
                        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
                        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
                        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

                        int coords[4] = { -d->getIcon()->getSize()[0]/2,
                                          -d->getIcon()->getSize()[1]/2,
                                          d->getIcon()->getSize()[0]/2,
                                          d->getIcon()->getSize()[1]/2
                        };

                        glColor3f( 1, 1, 1 );
                        glBegin( GL_QUADS );
                        {
                                glTexCoord2f( 0, 0 );
                                glVertex2f(  coords[0], coords[1] );
                                glTexCoord2f( 1, 0 );
                                glVertex2f(  coords[2], coords[1] );
                                glTexCoord2f( 1, 1 );
                                glVertex2f(  coords[2], coords[3] );
                                glTexCoord2f( 0, 1 );
                                glVertex2f(  coords[0], coords[3] );
                        }
                        glEnd();
                        glDisable( GL_TEXTURE_2D );
                        glDisable( GL_BLEND );
                        glDisable( GL_ALPHA_TEST );
                }

                glCallList( PTR_DEFAULT_DISPLAY_LIST_IDX );

        }
        
        glMatrixMode( GL_MODELVIEW );
        glEnable( GL_DEPTH_TEST );

        SDL_GL_SwapBuffers( );

        color += 0.01;

        if( color >= 1.0 ) {
                color = 0.0;
        }

        /* Check for error conditions. */
        if( ( gl_error = glGetError() ) != GL_NO_ERROR ) {
                cout << " :: GL error: Graphics::updateGraphics(): " << (long) gl_error << endl;
        }

        sdl_error = SDL_GetError( );

        if( sdl_error[0] != '\0' ) {
                cout <<  " :: SDL error: Graphics::updateGraphics(): " << sdl_error << endl;
                SDL_ClearError();
        }

}

void Graphics::setLoopRunning( bool running ) {
        _loopStarted = true;
        _loopRunning = running;
}


bool Graphics::setVideoMode() {

        cout <<  "setVideoMode: " << _videoMode << endl;

        int actualBits = SDL_VideoModeOK( _videoMode._width, 
                                          _videoMode._height, 
                                          _videoMode._bpp, 
                                          _videoMode._flags );

        if ( actualBits == 0 ) {
                cout << " :: Requested video mode " << _videoMode << " not OK." << endl;
                return false;
        }

        if ( actualBits != _videoMode._bpp ) {
                cout << " :: Requested video mode " << _videoMode << " only available at bpp " << actualBits << endl;
                return false;
        }
        

        if ( SDL_SetVideoMode( _videoMode._width, 
                               _videoMode._height, 
                               _videoMode._bpp, 
                               _videoMode._flags ) == NULL ) {
                fprintf(stderr, "Couldn't set GL mode: %s\n", SDL_GetError());
                SDL_Quit();
                exit(1);
        }
        
        initGLState();

        return true;
}

/**
 * initialize the GL state based on the screen size
 */

void Graphics::initGLState() {

        cout << "GL_VERSION: "  << glGetString( GL_VERSION ) << endl;
        const char * renderer = (char *) glGetString( GL_RENDERER );
        cout << "GL_RENDERER: " << renderer << endl;
        const char * vendor   = (char *) glGetString( GL_VENDOR );
        cout << "GL_VENDOR: " << vendor << endl;
        
        const char * extensions = (char *) glGetString( GL_EXTENSIONS );

        cout << "GL_EXTENSIONS: " << extensions << endl;
        
        if ( strstr( renderer, "GeForce" ) && strstr( vendor, "NVIDIA" ) ) {
                cout << " :: GL_POLYGON_SMOOTH broken for this GL driver, disabling." << endl;
                g_state.polygon_smooth_ok = false;
                glDisable( GL_POLYGON_SMOOTH );
        }
        
        setup_gl_stubs();
        
        if ( g_state.use_multitex ) {
                if ( g_state.has_multitex == false || ! strstr( extensions, "GL_ARB_multitexture" ) ) {
                        cout << " :: You GL library does not support GL_ARB_multitexture." << endl;
                        g_state.has_multitex = g_state.use_multitex = false;
                }
                else {
                        int max_tex_units = 0;
                        glGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &max_tex_units );
                        cout << "GL_MAX_TEXTURE_UNITS_ARB: " << max_tex_units << endl;
                }
        }
        
        if ( g_state.use_cva ) {
                if ( g_state.has_cva == false || ! strstr( extensions, "GL_EXT_compiled_vertex_array" ) ) {
                        cout << " :: You GL library does not support GL_EXT_compiled_vertex_array." << endl;
                        g_state.has_cva = g_state.use_cva = false;
                }
        }

        cout << " :: Initializing viewport." << endl;
        
        glViewport( 0, 0, _videoMode._width, _videoMode._height );
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );

        glShadeModel( GL_FLAT );

        glOrtho( -2.0, 2.0, -2.0, 2.0, -20.0, 20.0 );
        
        //        glFrustum( -1, 1, -1, 1, 5, 45 );
        //        gluPerspective( 90, 1.25, 0.5, 50 );

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity( );

        //        glTranslatef( 0, 0, -10 );

	glEnable( GL_DEPTH_TEST );

	glDepthFunc( GL_LESS );

        //        glEnable( GL_POINT_SMOOTH );


        initGLDisplayLists();
        

        //
        // ugly setup of primary game components
        //

        TextureManager::init();

        _wm = WidgetManager::getInstance( _videoMode._width, _videoMode._height );

        Client::createInstance( _wm, _dg, _start_map );
        
        const char * sdl_error = SDL_GetError( );
        
        if( *sdl_error ) {
                cout <<  " :: SDL error: Graphics::initGLState(): " << sdl_error << endl;
                SDL_ClearError();
        }
}

/**
 * GL Display List init function
 */
void Graphics::initGLDisplayLists() {

        static GLubyte cursor_body[32] = { 
                0x00, 0x00,
                0x03, 0x00,
                0x03, 0x00,
                0x06, 0x00,
                0x46, 0x00,
                0x6c, 0x00,
                0x7c, 0x00,
                0x7f, 0x80,
                0x7f, 0x00,
                0x7e, 0x00,
                0x7c, 0x00,
                0x78, 0x00,
                0x70, 0x00,
                0x60, 0x00,
                0x40, 0x00
        };

        static GLubyte cursor_outline[32] = { 
                0x07, 0x80,
                0x04, 0x80,
                0x0c, 0x80,
                0xe9, 0x80,
                0xb9, 0x00,
                0x93, 0x00,
                0x83, 0xc0,
                0x80, 0x40,
                0x80, 0xc0,
                0x81, 0x80,
                0x83, 0x00,
                0x86, 0x00,
                0x8c, 0x00,
                0x98, 0x00,
                0xb0, 0x00,
                0xe0, 0x00
        };
                                      
        
        //
        // create the default pointer
        //
        
        PTR_DEFAULT_DISPLAY_LIST_IDX = glGenLists( 1 );
        
        glNewList( PTR_DEFAULT_DISPLAY_LIST_IDX, GL_COMPILE );
        {
                glPixelStorei( GL_UNPACK_ROW_LENGTH, 16 );

                glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
        
                glColor3f( 0, 0, 0 );
                glRasterPos2i( 0, -16 );

                glBitmap( 16, 16,
                          0,   0,
                          0,   0,
                          cursor_body );

                glColor3f( 1, 1, 0.7 );
                glRasterPos2i( 0, -16 );

                glBitmap( 16, 16,
                          0,   0,
                          0,   0,
                          cursor_outline );
        }
        glEndList();

        //
        // create the cube displayer
        //

        CUBE_DISPLAY_LIST_IDX = glGenLists( 1 );
        
        glNewList( CUBE_DISPLAY_LIST_IDX, GL_COMPILE );
        {
                float cube[8][3]= {{ 0.5,  0.5, -0.5}, 
                                   { 0.5, -0.5, -0.5},
                                   {-0.5, -0.5, -0.5},
                                   {-0.5,  0.5, -0.5},
                                   {-0.5,  0.5,  0.5},
                                   { 0.5,  0.5,  0.5},
                                   { 0.5, -0.5,  0.5},
                                   {-0.5, -0.5,  0.5}};


                glBegin( GL_QUADS );
                {
                        glColor3f(1.0, 0.0, 0.0);
                        glVertex3fv(cube[0]);
                        glVertex3fv(cube[1]);
                        glVertex3fv(cube[2]);
                        glVertex3fv(cube[3]);
			
                        glColor3f(0.0, 1.0, 0.0);
                        glVertex3fv(cube[3]);
                        glVertex3fv(cube[4]);
                        glVertex3fv(cube[7]);
                        glVertex3fv(cube[2]);
			
                        glColor3f(0.0, 0.0, 1.0);
                        glVertex3fv(cube[0]);
                        glVertex3fv(cube[5]);
                        glVertex3fv(cube[6]);
                        glVertex3fv(cube[1]);
			
                        glColor3f(0.0, 1.0, 1.0);
                        glVertex3fv(cube[5]);
                        glVertex3fv(cube[4]);
                        glVertex3fv(cube[7]);
                        glVertex3fv(cube[6]);

                        glColor3f(1.0, 1.0, 0.0);
                        glVertex3fv(cube[5]);
                        glVertex3fv(cube[0]);
                        glVertex3fv(cube[3]);
                        glVertex3fv(cube[4]);

                        glColor3f(1.0, 0.0, 1.0);
                        glVertex3fv(cube[6]);
                        glVertex3fv(cube[1]);
                        glVertex3fv(cube[2]);
                        glVertex3fv(cube[7]);
                }
                glEnd( );

        }
        glEndList();

        const char * sdl_error = SDL_GetError( );
        
        if( *sdl_error ) {
                cout <<  " :: SDL Error: Graphics::initGLDisplayLists() : " << sdl_error << endl;
                SDL_ClearError();
        }

}

void Graphics::setScreenshotCacheSize( int size ) {

        if ( _screenshotCacheSize && _screenshotCacheUsed > 0 ) {
                flushScreenshotCache();
        }
        
        if ( _screenshotCacheSize != size ) {
                delete[] _screenshotCache;
                
                if ( size <= 0 ) {
                        _screenshotCache = 0;
                }
                else {
                        _screenshotCache = new GLubyte * [ size ];
                }
                
                _screenshotCacheUsed = 0;
                _screenshotCacheSize = size;
        }
}

GLubyte * Graphics::getScreenshot() {

        int num_bytes =  _videoMode._width * _videoMode._height * 3;

        GLubyte * pixels = new GLubyte[ num_bytes ];
        
        glReadBuffer( GL_BACK );
        
        glReadPixels( 0, 0, _videoMode._width, _videoMode._height, 
                      g_state.swap_RB ? GL_BGR :GL_RGB,
                      GL_UNSIGNED_BYTE, pixels );
        
        Texload::flipImage( pixels, _videoMode._width, _videoMode._height, 3 );
        return pixels;       
}

void Graphics::flushScreenshotCache() {

        for ( int i = 0; i < _screenshotCacheUsed; ++i ) {
                dumpScreenshot( 0, _screenshotCache[i] );
                delete[] _screenshotCache[i];
                _screenshotCache[i] = 0;
        }

        _screenshotCacheUsed = 0;
}

bool Graphics::takeScreenshot() {
        if ( _screenshotCache ) {
                _screenshotCache[ _screenshotCacheUsed++ ] = getScreenshot();
                
                if ( _screenshotCacheUsed >= _screenshotCacheSize ) {
                        flushScreenshotCache();
                }
        }
        else {
                dumpScreenshot();
        }

        return true;
}

/**
 * default filename = 0, pixels = 0
 */

bool Graphics::dumpScreenshot( const char * filename, const GLubyte * pixels ) {
        
        static int last_screenshot = 0;
        char my_filename[64];
        
        int num_bytes =  _videoMode._width * _videoMode._height * 3;
        
        if ( pixels == 0 ) {
                pixels = getScreenshot();
        }

        if ( filename == 0 ) {
                sprintf( my_filename, "screenshot_%03d.ppm", last_screenshot++ );
                filename = my_filename;
        }
        
        ofstream ofs( filename );

        ofs << "P6" << endl
            << _videoMode._width << " " << _videoMode._height << endl
            << "255" << endl;

        ofs.write( pixels, num_bytes );
        ofs.close();

        cout << " :: Screenshot [" << filename << "] written." << endl;
        
        return true;
}

