// Description:
//   Block View GL smooth rotation
//
// Copyright (C) 2003 Frank Becker
//
// 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
//
#include "SDL.h"
#include <gl++.hpp>

#include <Trace.hpp>
#include <Point.hpp>
#include <Config.hpp>

#include <FPS.hpp>

#include <GameState.hpp>
#include <Constants.hpp>
#include <FontManager.hpp>
#include <ResourceManager.hpp>
#include <zrwops.hpp>

#include "BlockViewGLSmooth.hpp"

const int VIDEO_ORTHO_WIDTH=1000;
const int VIDEO_ORTHO_HEIGHT=750;

const float GAMEOVERROTSPEED = 1.0f;

BlockViewGLSmooth::BlockViewGLSmooth( BlockModel &model):
    BlockViewGL(model),
    _useAALines(false),
    _gameOverAngle(0.0),
    _blockFace(0)
{
    XTRACE();

    ConfigS::instance()->getBoolean( "aalines", _useAALines);

    FontManagerS::instance()->reload();
}

BlockViewGLSmooth::~BlockViewGLSmooth()
{
    XTRACE();
    delete _blockFace;
}

bool BlockViewGLSmooth::init( void)
{
    bool status = BlockViewGL::init();
    
    if( status)
    {
	_smallFont = FontManagerS::instance()->getFont( "bitmaps/arial-small");
	if( !_smallFont)
	{
	    LOG_ERROR << "Unable to get font... (arial-small)" << endl;
	    SDL_QuitSubSystem( SDL_INIT_VIDEO);
	    status = false;
	}

	if( !ResourceManagerS::instance()->selectResource(
//		    string("bitmaps/Biohazard3.png")))
		    string("bitmaps/biohazard.png")))
	{
	    LOG_WARNING << "blockPic1.png not found." << endl;
	    return false;
	}
	ziStream &bminfile1 = ResourceManagerS::instance()->getInputStream();
	SDL_RWops *src = RWops_from_ziStream( bminfile1);
	SDL_Surface *img1 = IMG_LoadPNG_RW( src);
	_blockFace = new GLTexture( GL_TEXTURE_2D, img1, false);
	SDL_FreeRW(src);
    }

    return status;
}

void BlockViewGLSmooth::close( void)
{
    FontManagerS::cleanup();
    BlockViewGL::close();
}

void BlockViewGLSmooth::update( void)
{
    if( GameState::isAlive)
    {
	BlockViewSmooth::update();
    }
    else
    {
	_gameOverAngle += GAMEOVERROTSPEED;
    }
}

void BlockViewGLSmooth::draw( void)
{
    int blockView = min(_width, _height);
    glViewport(0,0, blockView, blockView);

    glEnable( GL_DEPTH_TEST);
    glEnable( GL_LIGHTING);
    glEnable( GL_LIGHT0);
    glShadeModel(GL_SMOOTH);

    glEnable( GL_NORMALIZE);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable( GL_COLOR_MATERIAL );

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    const float fov = 53.13;
    glFrustum(
	    (3.0/3.0)*(-2.0*tan(fov * M_PI / 360.0)),	//xmin
	    (3.0/3.0)*( 2.0*tan(fov * M_PI / 360.0)),	//xmax
	    -2.0*tan(fov * M_PI / 360.0),		//ymin
	    2.0*tan(fov * M_PI / 360.0),		//ymax
	    2.0,					//znear
	    2000.0);					//zfar

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    //    glTranslatef( -_cameraX, -_cameraY, -_cameraZ );
    glTranslatef( 0, 0,-100);

    GLfloat light_position[] = { -100.0, 100.0, 100.0, 1.0 };
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

#if 0
    //show light position
    glColor4f( 1.0, 1.0, 1.0, 1.0 );
    glBegin(GL_QUADS);
    glNormal3f( 0,0,1); 
    glVertex3f(  40.0, 40.0, -150.0);
    glVertex3f(  40.0, 60.0, -150.0);
    glVertex3f(  60.0, 60.0, -150.0);
    glVertex3f(  60.0, 40.0, -150.0);
    glEnd();
#endif

    int w = _model.getWidth();
    int h = _model.getHeight();
    int d = _model.getDepth();

    _squaresize = 200.0f / (float)(max(w,h));
    float bottom = -120.0f -((float)d *_squaresize);

    if( _useAALines)
    {
	//AA lines
	glHint(GL_NICEST,GL_LINE_SMOOTH_HINT);
	glEnable( GL_LINE_SMOOTH);
    }

    // -- Draw the shaft
    glDisable(GL_DEPTH_TEST);
    drawShaft();
    glEnable(GL_DEPTH_TEST);

    // -- Draw all the locked elements
    glPushMatrix();

    if( !GameState::isAlive)
    {
	float gf = GameState::frameFraction;
	float interpAngle = _gameOverAngle + (GAMEOVERROTSPEED*gf);
	glRotatef( interpAngle, 0,0,1);
    }

    ElementList::iterator i;

    glTranslatef( 
	    0-_squaresize*(w-1)/2.0f, 
	    0-_squaresize*(h-1)/2.0f, 
	    0+bottom + _squaresize/2.0f);

    ElementList &lockedElementList = _model.getLockedElementList();
    for( i=lockedElementList.begin(); i!=lockedElementList.end(); i++)
    {
	Point3Di *p = *i;
	drawElement( p, true);
    }
    glPopMatrix();

    if( GameState::isAlive)
    {
	// -- Draw the block
	glPushMatrix();
    //    glDisable( GL_LIGHTING);

	Point3D &interpOffset = getInterpolatedOffset();
	glTranslatef( 
		interpOffset.x*_squaresize-_squaresize*(w-1)/2.0f, 
		interpOffset.y*_squaresize-_squaresize*(h-1)/2.0f, 
		interpOffset.z*_squaresize+bottom + _squaresize/2.0f);

	Point3D &currentAxis = getCurrentAxis();
	Point3D &prevAxis = getPrevAxis();
	glRotatef( getInterpolatedAngle(), currentAxis.x, currentAxis.y, currentAxis.z);
	glRotatef( getPrevAngle(), prevAxis.x, prevAxis.y, prevAxis.z);

	ElementList &elementList = _model.getElementListNorm();
	for( i=elementList.begin(); i!=elementList.end(); i++)
	{
	    Point3Di *p = *i;
	    drawElement( p, false);
	}
    //    glEnable( GL_LIGHTING);
	glPopMatrix();
    }
    else
    {
	float gf = GameState::frameFraction;
	float interpAngle = _gameOverAngle + (GAMEOVERROTSPEED*gf);
	glRotatef( interpAngle, 0,1,0);

        glDisable( GL_LIGHTING);
	glColor4f(1.0,1.0,1.0,1.0);
	string gameOver = "Game Over!";
	_smallFont->DrawString( gameOver.c_str() , -40, 10.0, 0.4, 0.4);
	string getOut = "Press ESC to exit";
	_smallFont->DrawString( getOut.c_str() , -14, 0.0, 0.1, 0.1);
        glEnable( GL_LIGHTING);
    }

    //---
    glViewport(blockView,0, _width-blockView, blockView);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(
	    (1.0/4.0)*(-2.0*tan(fov * M_PI / 360.0)),	//xmin
	    (1.0/4.0)*( 2.0*tan(fov * M_PI / 360.0)),	//xmax
	    -2.0*tan(fov * M_PI / 360.0),		//ymin
	    2.0*tan(fov * M_PI / 360.0),		//ymax
	    2.0,					//znear
	    2000.0);					//zfar
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    drawIndicator();
    //---

    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-0.5,VIDEO_ORTHO_WIDTH/4+0.5,-0.5,VIDEO_ORTHO_HEIGHT+0.5, -1000.0, 1000.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    FPS::Update();
    glColor4f(1.0,1.0,1.0,1.0);
    _smallFont->DrawString( FPS::GetFPSString(), 0, 20, 1.0, 1.0);

    char num[30];
    sprintf( num, "Score: %d", _model.getScore());
    _smallFont->DrawString( num, 0, 70, 0.6, 1.0);

    sprintf( num, "Blocks: %d", _model.getElementCount());
    _smallFont->DrawString( num, 0, 120, 0.6, 1.0);

    sprintf( num, "Level: %d", _model.getLevel());
    _smallFont->DrawString( num, 0, 170, 0.6, 1.0);

    glColor4f(1.0,1.0,1.0,0.5);
    string cr = "Copyright (C) 2004 by Frank Becker";
    _smallFont->DrawString( cr.c_str() , 0, VIDEO_ORTHO_HEIGHT-20.0, 0.4, 0.4);

    glColor4f(1.0,1.0,1.0,0.5);
    string gVersion = "v"+GAMEVERSION;
    float width = _smallFont->GetWidth( gVersion.c_str(), 0.6);
    _smallFont->DrawString( gVersion.c_str() , (VIDEO_ORTHO_WIDTH/4)-width, 5.0, 0.6, 0.4);

    SDL_GL_SwapBuffers( );
}

void BlockViewGLSmooth::drawIndicator( void)
{
#if 0
    static float r=0;
    r+= 0.1;
    glTranslatef( -10, -10,-100);
    glRotatef( r, 0,1,0);
#else
    glTranslatef( -5, -7,-50);
#endif

    for( int i=0; i<_model.getDepth(); i++)
    {
	int count = _model.numBlocksInPlane( i);

	if( !count) glColor4f( 0.2, 0.2, 0.2, 1.0 );
	else setColor( i);

	glBegin(GL_QUADS);
	glNormal3f(-1, 1, 1); glVertex3f(-1, (float)i*2.5+1.0, 1);
	glNormal3f( 1, 1,-1); glVertex3f( 1, (float)i*2.5+1.0, 1);
	glNormal3f( 1,-1,-1); glVertex3f( 1, (float)i*2.5-1.0, 1);
	glNormal3f(-1,-1, 1); glVertex3f(-1, (float)i*2.5-1.0, 1);
#if 0
	glNormal3f(-1, 1, 1); glVertex3f(-1, i*3+1, 1);
	glNormal3f( 1, 1, 1); glVertex3f( 1, i*3+1, 1);
	glNormal3f( 1,-1, 1); glVertex3f( 1, i*3-1, 1);
	glNormal3f(-1,-1, 1); glVertex3f(-1, i*3-1, 1);

	glNormal3f( 1, 1, 1); glVertex3f( 1, i*3+1, 1);
	glNormal3f( 1,-1, 1); glVertex3f( 1, i*3-1, 1);
	glNormal3f( 1,-1,-1); glVertex3f( 1, i*3-1,-1);
	glNormal3f( 1, 1,-1); glVertex3f( 1, i*3+1,-1);

	glNormal3f( 1, 1,-1); glVertex3f( 1, i*3+1,-1);
	glNormal3f(-1, 1,-1); glVertex3f(-1, i*3+1,-1);
	glNormal3f(-1,-1,-1); glVertex3f(-1, i*3-1,-1);
	glNormal3f( 1,-1,-1); glVertex3f( 1, i*3-1,-1);

	glNormal3f(-1, 1,-1); glVertex3f(-1, i*3+1,-1);
	glNormal3f(-1, 1, 1); glVertex3f(-1, i*3+1, 1);
	glNormal3f(-1,-1, 1); glVertex3f(-1, i*3-1, 1);
	glNormal3f(-1,-1,-1); glVertex3f(-1, i*3-1,-1);
#endif
	glEnd();
    }
}

void BlockViewGLSmooth::drawShaft( void)
{
    int w = _model.getWidth();
    int h = _model.getHeight();
    int d = _model.getDepth();

    float tilesize = _squaresize*0.80f;
    float halfgapsize = (_squaresize-tilesize)/2.0f;
    float bottom = -120.0f -((float)d *_squaresize);

    glPushMatrix();

    if( !GameState::isAlive)
    {
	float gf = GameState::frameFraction;
	float interpAngle = _gameOverAngle + (GAMEOVERROTSPEED*gf);
	glRotatef( interpAngle, 0,0,1);
    }

    glTranslatef( 0,0,bottom); 

    glBegin(GL_QUADS);
    glColor4f( 0.0, 1.0, 0.0, 0.5 );
    for( int x=0; x<w; x++)
    {
        float xp = x*_squaresize - _squaresize*w/2.0f + halfgapsize;
        for( int y=0; y<h; y++)
        {
            float yp = y*_squaresize - _squaresize*h/2.0f + halfgapsize;
            glNormal3f( 2,1,1);
            glVertex3f(  xp         , yp         , 0);
            glNormal3f( 1,2,1);
            glVertex3f(  xp+tilesize, yp         , 0);
            glNormal3f( 1,2,1);
            glVertex3f(  xp+tilesize, yp+tilesize, 0);
            glNormal3f( 2,1,1);
            glVertex3f(  xp         , yp+tilesize, 0);
        }
    }

    glColor4f( 0.0, 0.0, 1.0, 0.5 );
    for( int x=0; x<w; x++)
    {
        float xp = x*_squaresize - _squaresize*w/2.0f + halfgapsize;
        float yp = _squaresize*h/2.0f;
        for( int z=0; z<d; z++)
        {
            float zp = z*_squaresize;
            glNormal3f( 1, 1,2);
            glVertex3f(  xp+tilesize,-yp, zp);
            glNormal3f( 2, 1,1);
            glVertex3f(  xp         ,-yp, zp);
            glNormal3f( 2, 1,1);
            glVertex3f(  xp         ,-yp, zp+tilesize);
            glNormal3f( 1, 1,2);
            glVertex3f(  xp+tilesize,-yp, zp+tilesize);

            glNormal3f( 1,-1,2);
            glVertex3f(  xp+tilesize, yp, zp+tilesize);
            glNormal3f( 2,-1,1);
            glVertex3f(  xp         , yp, zp+tilesize);
            glNormal3f( 2,-1,1);
            glVertex3f(  xp         , yp, zp);
            glNormal3f( 1,-1,2);
            glVertex3f(  xp+tilesize, yp, zp);
        }
    }

    glColor4f( 1.0, 0.0, 0.0, 0.5 );
    for( int y=0; y<h; y++)
    {
        float xp = _squaresize*w/2.0f;
        float yp = y*_squaresize - _squaresize*h/2.0f + halfgapsize;
        for( int z=0; z<d; z++)
        {
            float zp = z*_squaresize;
            glNormal3f( 1,0,0);
            glVertex3f(-xp, yp         , zp);
            glVertex3f(-xp, yp+tilesize, zp);
            glVertex3f(-xp, yp+tilesize, zp+tilesize);
            glVertex3f(-xp, yp         , zp+tilesize);

            glNormal3f(-1,0,0);
            glVertex3f( xp, yp         , zp+tilesize);
            glVertex3f( xp, yp+tilesize, zp+tilesize);
            glVertex3f( xp, yp+tilesize, zp);
            glVertex3f( xp, yp         , zp);
        }
    }
    glEnd();

    glPopMatrix();
}

void BlockViewGLSmooth::setColor( int p)
{
    switch( p % 7)
    {
	case 0:
	    glColor4f( 0.0, 0.0, 1.0, 1.0 );
	    break;
	case 1:
	    glColor4f( 0.0, 1.0, 0.0, 1.0 );
	    break;
	case 2:
	    glColor4f( 0.0, 1.0, 1.0, 1.0 );
	    break;
	case 3:
	    glColor4f( 1.0, 0.0, 0.0, 1.0 );
	    break;
	case 4:
	    glColor4f( 1.0, 0.0, 1.0, 1.0 );
	    break;
	case 5:
	    glColor4f( 1.0, 0.7, 0.0, 1.0 );
	    break;
	case 6:
	    glColor4f( 0.5, 0.5, 0.5, 1.0 );
	    break;
    }
}

void BlockViewGLSmooth::drawElement( Point3Di *p, bool isLocked)
{
    float xp = (p->x)*_squaresize;
    float yp = (p->y)*_squaresize;
    float zp = (p->z)*_squaresize;

    float halftilesize = (_squaresize*0.80f)/2.0f;
    float xpm = xp-halftilesize;
    float xpp = xp+halftilesize;
    float ypm = yp-halftilesize;
    float ypp = yp+halftilesize;
    float zpm = zp-halftilesize;
    float zpp = zp+halftilesize;

    float halfmtilesize = (_squaresize*0.60f)/2.0f;
    float xpmm = xp-halfmtilesize;
    float xpmp = xp+halfmtilesize;
    float ypmm = yp-halfmtilesize;
    float ypmp = yp+halfmtilesize;
    float zpmm = zp-halfmtilesize;
    float zpmp = zp+halfmtilesize;

    if( isLocked)
    {
	setColor( p->z);
    }

    if( isLocked)
    {
	glBegin(GL_QUADS);
	glNormal3f(-1,-1,1); 
	glVertex3f( xpm, ypm, zpm);
	glNormal3f( 1,-1,1); 
	glVertex3f( xpp, ypm, zpm);
	glNormal3f( 1, 1,1); 
	glVertex3f( xpp, ypp, zpm);
	glNormal3f(-1, 1,1); 
	glVertex3f( xpm, ypp, zpm);

	glNormal3f(-1, 1,-1); 
	glVertex3f( xpm, ypp, zpp);
	glNormal3f( 1, 1,-1); 
	glVertex3f( xpp, ypp, zpp);
	glNormal3f( 1,-1,-1); 
	glVertex3f( xpp, ypm, zpp);
	glNormal3f(-1,-1,-1); 
	glVertex3f( xpm, ypm, zpp);

	glEnd();
    }
    else
    {
//	glDisable( GL_DEPTH_TEST);
	glColor4f( 1.0, 1.0, 0.3, 0.8 );

	float dx= 1.0/512.0;
	_blockFace->bind();

	glEnable(GL_TEXTURE_2D);

	glBegin(GL_QUADS);
	glNormal3f( 0,0,1); 
	glTexCoord2f( dx    ,dx );    glVertex3f( xpmm, ypmm, zpmm);
	glTexCoord2f( 1.0-dx,dx );    glVertex3f( xpmp, ypmm, zpmm);
	glTexCoord2f( 1.0-dx,1.0-dx );glVertex3f( xpmp, ypmp, zpmm);
	glTexCoord2f( dx    ,1.0-dx );glVertex3f( xpmm, ypmp, zpmm);

	glNormal3f( 0,0,-1); 
	glTexCoord2f( dx    ,dx );    glVertex3f( xpmm, ypmp, zpmp);
	glTexCoord2f( 1.0-dx,dx );    glVertex3f( xpmp, ypmp, zpmp);
	glTexCoord2f( 1.0-dx,1.0-dx );glVertex3f( xpmp, ypmm, zpmp);
	glTexCoord2f( dx    ,1.0-dx );glVertex3f( xpmm, ypmm, zpmp);

	glNormal3f( 0, 1,0); 
	glTexCoord2f( dx    ,dx );    glVertex3f( xpmp, ypmm, zpmm);
	glTexCoord2f( 1.0-dx,dx );    glVertex3f( xpmm, ypmm, zpmm);
	glTexCoord2f( 1.0-dx,1.0-dx );glVertex3f( xpmm, ypmm, zpmp);
	glTexCoord2f( dx    ,1.0-dx );glVertex3f( xpmp, ypmm, zpmp);

	glNormal3f( 0,-1,0); 
	glTexCoord2f( dx    ,dx );    glVertex3f( xpmp, ypmp, zpmp);
	glTexCoord2f( 1.0-dx,dx );    glVertex3f( xpmm, ypmp, zpmp);
	glTexCoord2f( 1.0-dx,1.0-dx );glVertex3f( xpmm, ypmp, zpmm);
	glTexCoord2f( dx    ,1.0-dx );glVertex3f( xpmp, ypmp, zpmm);

	glNormal3f( 1,0,0); 
	glTexCoord2f( dx    ,dx );    glVertex3f( xpmm, ypmm, zpmm); 
	glTexCoord2f( 1.0-dx,dx );    glVertex3f( xpmm, ypmp, zpmm); 
	glTexCoord2f( 1.0-dx,1.0-dx );glVertex3f( xpmm, ypmp, zpmp); 
	glTexCoord2f( dx    ,1.0-dx );glVertex3f( xpmm, ypmm, zpmp);

	glNormal3f(-1,0,0); 
	glTexCoord2f( dx    ,dx );    glVertex3f( xpmp, ypmm, zpmp); 
	glTexCoord2f( 1.0-dx,dx );    glVertex3f( xpmp, ypmp, zpmp); 
	glTexCoord2f( 1.0-dx,1.0-dx );glVertex3f( xpmp, ypmp, zpmm); 
	glTexCoord2f( dx    ,1.0-dx );glVertex3f( xpmp, ypmm, zpmm);
	glEnd();

	glDisable(GL_TEXTURE_2D);

//	glEnable( GL_DEPTH_TEST);
    }

    if( !isLocked)
    {
	glDisable( GL_LIGHTING);

//	glLineWidth(2.0f);
	glColor4f( 1.0, 1.0, 0.0, 1.0 );

	glBegin(GL_LINE_LOOP);
	glNormal3f( 0,0,1); 
	glVertex3f( xpm, ypm, zpm);
	glVertex3f( xpp, ypm, zpm);
	glVertex3f( xpp, ypp, zpm);
	glVertex3f( xpm, ypp, zpm);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glNormal3f( 0,0,-1); 
	glVertex3f( xpm, ypm, zpp);
	glVertex3f( xpp, ypm, zpp);
	glVertex3f( xpp, ypp, zpp);
	glVertex3f( xpm, ypp, zpp);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glNormal3f( 0, 1,0); 
	glVertex3f( xpp, ypm, zpm);
	glVertex3f( xpm, ypm, zpm);
	glVertex3f( xpm, ypm, zpp);
	glVertex3f( xpp, ypm, zpp);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glNormal3f( 0,-1,0); 
	glVertex3f( xpp, ypp, zpp);
	glVertex3f( xpm, ypp, zpp);
	glVertex3f( xpm, ypp, zpm);
	glVertex3f( xpp, ypp, zpm);
	glEnd();

	glEnable( GL_LIGHTING);
    }
}
