//===============================================================================
// @ Player.cpp
// ------------------------------------------------------------------------------
// Player
//
// Copyright (C) 2004 by Elsevier, Inc. All rights reserved.
//
//
//
// This example demonstrates a spot light and its effect upon a tesselated plane
// The controls move the spotlight and adjust its cone angle, etc.  A red arrow
// icon shows the position and orientation of the spotlight.  By moving and
// rotating the spotlight, the expected lighting patterns (circles, ellipses and
// parabolas) may be seen
//
// The key commands are:
//
// i, k - translate light in x
// j, l - translate light in y
// u, o - rotate light around z axis
// Space - reset transforms
// q, w - increase, decrease spotlight cone angle
// w, s - increase, decrease the spotlight attenuation exponent
//
//===============================================================================

//-------------------------------------------------------------------------------
//-- Dependencies ---------------------------------------------------------------
//-------------------------------------------------------------------------------

#include <IvEventHandler.h>
#include <IvGLHelp.h>
#include <IvMath.h>
#include <IvMatrix44.h>

#include "Player.h"
#include "Game.h"

//-------------------------------------------------------------------------------
//-- Static Members -------------------------------------------------------------
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
//-- Methods --------------------------------------------------------------------
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
// @ Player::Player()
//-------------------------------------------------------------------------------
// Constructor
//-------------------------------------------------------------------------------
Player::Player()
{
    mRotate.Identity();
    mTranslate.Set(-10.0f, 0.0f, 0.0f);

    mGridVerts = 0;
    mGridIndices = 0;

    mSpotAngle = 12.5f;
    mSpotFalloff = 0.0f;

    CreateGridVertexArrays();
}   // End of Player::Player()


//-------------------------------------------------------------------------------
// @ Player::~Player()
//-------------------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------------------
Player::~Player()
{
    delete[] mGridVerts;
    delete[] mGridIndices;
}   // End of Player::~Player()


//-------------------------------------------------------------------------------
// @ Player::Update()
//-------------------------------------------------------------------------------
// Main update loop
//-------------------------------------------------------------------------------
void
Player::Update( float dt )
{
    // get scale, rotate, translate for this frame
    float r = 0.0f;
        
    // set up rotate
    if (IvGame::mGame->mEventHandler->IsKeyDown('o'))
    {
        r -= kPI*0.25f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('u'))
    {
        r += kPI*0.25f*dt;
    }
    IvMatrix33 rotate;
    rotate.RotationZ(r);
    mRotate = rotate*mRotate;
    
    // set up translation
    IvVector3 xlate;
    xlate.Zero();
    if (IvGame::mGame->mEventHandler->IsKeyDown('k'))
    {
        xlate.x -= 3.0f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('i'))
    {
        xlate.x += 3.0f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('l'))
    {
        xlate.y -= 3.0f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('j'))
    {
        xlate.y += 3.0f*dt;
    }
    mTranslate += xlate;
    
    // clear transform
    if (IvGame::mGame->mEventHandler->IsKeyDown(' '))
    {
        mRotate.Identity();
        mTranslate.Set(-10.0f, 0.0f, 0.0f);
    }

    // set up spot angle
    if (IvGame::mGame->mEventHandler->IsKeyDown('q'))
    {
        mSpotAngle += 5.0f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('a'))
    {
        mSpotAngle -= 5.0f*dt;
        mSpotAngle = (mSpotAngle >= 0.0) ? mSpotAngle : 0.0f;
    }

    // set up spot falloff
    if (IvGame::mGame->mEventHandler->IsKeyDown('w'))
    {
        mSpotFalloff += 0.5f*dt;
    }
    if (IvGame::mGame->mEventHandler->IsKeyDown('s'))
    {
        mSpotFalloff -= 0.5f*dt;
        mSpotFalloff = (mSpotFalloff >= 0.0) ? mSpotFalloff : 0.0f;
    }

}   // End of Player::Update()


//-------------------------------------------------------------------------------
// @ Player::Render()
//-------------------------------------------------------------------------------
// Render stuff
//-------------------------------------------------------------------------------
void 
Player::Render()                                    
{   
    // build 4x4 matrix for the light
    IvMatrix44 transform(mRotate);
    transform(0,3) = mTranslate.x;
    transform(1,3) = mTranslate.y;
    transform(2,3) = mTranslate.z;
    
    // transform the _light_, not the grid
    
    // store current matrix
    glPushMatrix();

    // multiply by transform
    glMultMatrixf(transform);

    // turn on the lights
    glDisable( GL_LIGHTING );

    // Draw a visual representation of the light
    DrawLightIcon();

    // restore original matrix          
    glPopMatrix();

    // turn on the lights
    glEnable( GL_LIGHTING );
    glEnable( GL_NORMALIZE );
    glDisable(GL_COLOR_MATERIAL);

    glEnable(GL_LIGHT0);
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, mSpotAngle);
    glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, mSpotFalloff);

    // position and aim the light in world space
    IvVector4 lightDir = transform * IvVector4::xAxis;
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, (float *)&lightDir);

    IvVector4 lightPos(mTranslate.x, mTranslate.y, mTranslate.z, 1.0f);
    glLightfv(GL_LIGHT0, GL_POSITION, (float *)&lightPos);

    // draw grid geometry
    glColor3f(1.0f, 1.0f, 1.0f);
    DrawGridVertexArrays();

    glDisable( GL_LIGHTING );

}   // End of Player::Render()


//-------------------------------------------------------------------------------
// @ Player::DrawGridVertexArrays()
//-------------------------------------------------------------------------------
// Draw vertex arrays for a grid centered around the origin
//-------------------------------------------------------------------------------
void
Player::DrawGridVertexArrays()
{
    glEnableClientState(GL_VERTEX_ARRAY);

    glVertexPointer(3, GL_FLOAT, 0, mGridVerts); 

    // plane has a constant normal
    glNormal3f(-1.0f, 0.0f, 0.0f);
    
    glDrawElements(GL_TRIANGLE_STRIP, mGridIndexCount, GL_UNSIGNED_SHORT, 
        mGridIndices);

    glDisableClientState(GL_VERTEX_ARRAY);
} // End of Player::DrawGridVertexArrays()
    
//-------------------------------------------------------------------------------
// @ Player::CreateGridVertexArrays()
//-------------------------------------------------------------------------------
// Create vertex arrays for a grid centered around the origin
//-------------------------------------------------------------------------------
void 
Player::CreateGridVertexArrays()                                    
{
    // Creates a grid of points
    const unsigned int steps = 32;
    mGridVerts = new IvVector3[steps * steps];

    // temporary pointer that can be stepped along the array
    IvVector3* tempVerts = mGridVerts;

    float size = 15.0f;
    float increment = size / steps;

    IvVector3 origin(0.0f, -size * 0.5f, -size * 0.5f);
    
    // A double loop, walking along and down the grid
    unsigned int j;
    for (j = 0; j < steps; j++)
    {
        IvVector3 column = origin;

        unsigned int i;
        for (i = 0; i < steps; i++)
        {
            *tempVerts = column;

            column.y += increment;

            tempVerts++;
        }

        origin.z += increment;
    }

    // Create index arrays - just a 32x31-quad mesh of triangles
    // Each of the 32 strips has 31 * 2 triangles plus two dummy indices
    // This means that there are 31 * 2 + 2 + 2 (two extra to start the
    // strip, and two extra to end the previous strip) indices in each
    // strip, although we can avoid two indices in the first strip, as
    // there is no previous strip to be ended in that case.  Thus,
    // 64 + 66 * 31 indices for the entire grid
    mGridIndexCount = steps * 2 + (steps - 1) * (steps * 2 + 2);

    mGridIndices = new unsigned short[mGridIndexCount];

    unsigned short* tempIndices = mGridIndices;

    for (j = 0; j < steps; j++)
    {
        unsigned int baseIndex0 = steps * j;
        unsigned int baseIndex1 = steps * ((j + 1) % steps);

        // restart the strip by doubling the last and next indices
        if (j != 0)
        {
            *(tempIndices++) = tempIndices[-1];
            *(tempIndices++) = baseIndex0;
        }

        unsigned int i;
        for (i = 0; i < steps; i++)
        {
            *(tempIndices++) = baseIndex0;
            *(tempIndices++) = baseIndex1;

            baseIndex0++;
            baseIndex1++;
        }
    }
}   // End of Player::CreateGridVertexArrays()

//-------------------------------------------------------------------------------
// @ DrawLightIcon()
//-------------------------------------------------------------------------------
// Draw icon for light source
//-------------------------------------------------------------------------------
void 
Player::DrawLightIcon()
{
    glBegin(GL_LINES);

    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f(0.5f, 0.0f, 0.0f);

    glVertex3f(0.5f, 0.0f, 0.0f);
    glVertex3f(0.375f, 0.125f, 0.125f);

    glVertex3f(0.5f, 0.0f, 0.0f);
    glVertex3f(0.375f, -0.125f, 0.125f);

    glVertex3f(0.5f, 0.0f, 0.0f);
    glVertex3f(0.375f, 0.125f, -0.125f);

    glVertex3f(0.5f, 0.0f, 0.0f);
    glVertex3f(0.375f, -0.125f, -0.125f);

    glEnd();
    
}   // End of DrawLightIcon()


