#include <string>
#include <iomanip>
#include <iostream>
using namespace std;

#include <math.h>
#if defined( __APPLE__ )
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include "gljumptable.h"
#include "GL/glu.h"
extern GLJumpTable glJump;
#endif

#include "distortion_opengl.h"


void printMatrix( std::string mt, GLenum mm )
{
    float mat[16];
    glJump.glGetFloatv( mm, mat );
    cout << mt << endl;
    for( int ii=0; ii<4; ii++ )
    {
        for( int jj=0; jj<4; jj++ )
            cout << std::setw( 10 ) << mat[4*ii+jj] << " ";
        cout << endl;
    }
}

const float localPI = 3.1415926f;
float dtor( float degrees )
{
    return( localPI*degrees/180.0f );
}

float rtod( float radians )
{
    return( 180.0f*radians/localPI );
}

void glErrorCheck()
{
    bool done = false;
    while( !done )
    {
        GLenum error = glGetError();
        if ( error == GL_NO_ERROR )
        {
            done = true;
        }
        else
        {
            cout << "error: " << hex << error << endl;
        }

    }
}

void beginDistortionState()
{
    glJump.glPushAttrib( GL_ALL_ATTRIB_BITS );
    glJump.glDisable( GL_LIGHTING );

    glJump.glEnable(GL_BLEND);
    glJump.glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

    glJump.glEnable(GL_TEXTURE_2D);
//  glJump.glActiveTextureARB( GL_TEXTURE0_ARB );
//  glJump.glClientActiveTextureARB( GL_TEXTURE0_ARB );

    glJump.glEnable(GL_TEXTURE_GEN_S);
    glJump.glEnable(GL_TEXTURE_GEN_T);
    glJump.glEnable(GL_TEXTURE_GEN_R);
    glJump.glEnable(GL_TEXTURE_GEN_Q);
    glJump.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
    glJump.glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
    glJump.glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    glJump.glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    glJump.glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    glJump.glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
    GLfloat eyePlaneS[] = {1.0, 0.0, 0.0, 0.0};
    GLfloat eyePlaneT[] = {0.0, 1.0, 0.0, 0.0};
    GLfloat eyePlaneR[] = {0.0, 0.0, 1.0, 0.0};
    GLfloat eyePlaneQ[] = {0.0, 0.0, 0.0, 1.0}; 
    glJump.glTexGenfv( GL_S, GL_EYE_PLANE, eyePlaneS );
    glJump.glTexGenfv( GL_T, GL_EYE_PLANE, eyePlaneT );
    glJump.glTexGenfv( GL_R, GL_EYE_PLANE, eyePlaneR );
    glJump.glTexGenfv( GL_Q, GL_EYE_PLANE, eyePlaneQ );
    glJump.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glJump.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

void endDistortionState()
{
    glJump.glPopAttrib();
}

void drawDome( float fovLeft, float fovRight, float fovBottom, float fovTop, 
                float overlap, float radius )
{
    unsigned int ii, jj;
    float theta, phe, localradius, zz;

    unsigned int nsegs = 15;

    float voffset = ( 2*overlap + fovTop - fovBottom ) / static_cast< float >( nsegs-1 );
    float hoffset = ( 2*overlap + fovRight - fovLeft ) / static_cast< float >( nsegs-1 );
    float toffset = 1.0/(nsegs-1.0);
    for( ii=0; ii<nsegs-1; ii++ )
    {
        glJump.glBegin( GL_QUADS );
        glJump.glActiveTextureARB( GL_TEXTURE0_ARB );
        glJump.glColor3f( 1, 1, 1 );
        for( jj=0; jj<nsegs-1; jj++ )
        {
            // rows, n && n+1, bottom up
            // ii => phe, jj => theta
            phe   = fovBottom-overlap + ii*voffset;
            theta = fovLeft-overlap   + jj*hoffset;
            localradius   = radius * cos( phe );
            zz            = radius * sin( phe );
            glJump.glColor3f( jj*toffset, ii*toffset, 0.0 );
//            glJump.glMultiTexCoord2fARB( GL_TEXTURE1_ARB, jj*toffset, ii*toffset );
            glJump.glVertex3f( localradius * sin( theta ), zz, -localradius * cos( theta ) );

            phe   = fovBottom-overlap + (ii+1)*voffset;
            theta = fovLeft-overlap   + jj*hoffset;
            localradius   = radius * cos( phe );
            zz            = radius * sin( phe );
            glJump.glColor3f( jj*toffset, (ii+1)*toffset, 0.0 );
//            glJump.glMultiTexCoord2fARB( GL_TEXTURE1_ARB, jj*toffset, (ii+1)*toffset );
            glJump.glVertex3f( localradius * sin( theta ), zz, -localradius * cos( theta ) );

            phe   = fovBottom-overlap + (ii+1)*voffset;
            theta = fovLeft-overlap   + (jj+1)*hoffset;
            localradius   = radius * cos( phe );
            zz            = radius * sin( phe );
            glJump.glColor3f( (jj+1)*toffset, (ii+1)*toffset, 0.0 );
//            glJump.glMultiTexCoord2fARB( GL_TEXTURE1_ARB, (jj+1)*toffset, (ii+1)*toffset );
           glJump.glVertex3f( localradius * sin( theta ), zz, -localradius * cos( theta ) );

            phe   = fovBottom-overlap + (ii)*voffset;
            theta = fovLeft-overlap   + (jj+1)*hoffset;
            localradius   = radius * cos( phe );
            zz            = radius * sin( phe );
            glJump.glColor3f( (jj+1)*toffset, ii*toffset, 0.0 );
//            glJump.glMultiTexCoord2fARB( GL_TEXTURE1_ARB, (jj+1)*toffset, ii*toffset );
            glJump.glVertex3f( localradius * sin( theta ), zz, -localradius * cos( theta ) );
        }
        glJump.glActiveTextureARB( GL_TEXTURE0_ARB );
        glJump.glEnd();
        glJump.glColor3f( 1, 1, 1 );
    }
}

void loadTextureProjection( float projectorOffset[ 3 ], float projectorAnglePitch, float projectorAngleYaw,
                            float eyeOffset[ 3 ], float eyeAngle,
                            float fovy, float aspect )
{
    glMatrixMode( GL_TEXTURE );
    glLoadIdentity();
#if 1
    glJump.glTranslatef( 0.5, 0.5, 0.0 );
    glJump.glScalef( 0.5, 0.5, 1.0 );
    gluPerspective( fovy, aspect, .1, 10.0 );
    glJump.glRotatef( -eyeAngle, 1,0,0 ); // eye rot
    glJump.glTranslatef( -eyeOffset[0], -eyeOffset[1], -eyeOffset[2] ); // eye translate
    glJump.glRotatef( projectorAngleYaw,   0,1,0 );  // cam rotate
    glJump.glRotatef( projectorAnglePitch, 1,0,0 );  // cam rotate
    glJump.glTranslatef( projectorOffset[0], projectorOffset[1], projectorOffset[2] ); // cam translate
#endif
}
