//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "gui/core/guiCanvas.h"
#include "game/guiPlayerView.h"
#include "console/consoleTypes.h"

static const F32 MaxOrbitDist = 5.0f;
static const S32 MaxAnimations = 6;

IMPLEMENT_CONOBJECT( GuiPlayerView );

//------------------------------------------------------------------------------
GuiPlayerView::GuiPlayerView() : GuiTSCtrl()
{
   mActive = true;
   mMouseState = None;
   mModel = NULL;
   mWeapon = NULL;
   mLastMousePoint.set( 0, 0 );
   lastRenderTime = 0;
   runThread = 0;
   wNode = -1;
   pNode = -1;
   mAnimationSeq = 0;
}


//------------------------------------------------------------------------------
GuiPlayerView::~GuiPlayerView()
{
   if ( mModel )
   {
      delete mModel;
      mModel = NULL;
   }
   if ( mWeapon )
   {
      delete mWeapon;
      mWeapon = NULL;
   }
}


//------------------------------------------------------------------------------
ConsoleMethod( GuiPlayerView, setModel, void, 4, 4, "playerView.setModel( raceGender, skin )" )
{
   argc;
   object->setPlayerModel( argv[2], argv[3] );
}

ConsoleMethod( GuiPlayerView, setSeq, void, 3, 3, "playerView.setSeq( index )" )
{
   argc;
   object->setPlayerSeq( dAtoi(argv[2]) );
}

//------------------------------------------------------------------------------
bool GuiPlayerView::onWake()
{
   if ( !Parent::onWake() )
      return( false );

   mCameraMatrix.identity();
   mCameraRot.set( 0, 0, 3.9 );
   mCameraPos.set( 0, 1.75, 1.25 );
   mCameraMatrix.setColumn( 3, mCameraPos );
   mOrbitPos.set( 0, 0, 0 );
   mOrbitDist = 3.5f;

   return( true );
}

//------------------------------------------------------------------------------
void GuiPlayerView::onMouseDown( const GuiEvent &event )
{
   if ( !mActive || !mVisible || !mAwake )
      return;

   mMouseState = Rotating;
   mLastMousePoint = event.mousePoint;
   mouseLock();
}


//------------------------------------------------------------------------------
void GuiPlayerView::onMouseUp( const GuiEvent &/*event*/ )
{
   mouseUnlock();
   mMouseState = None;
}


//------------------------------------------------------------------------------
void GuiPlayerView::onMouseDragged( const GuiEvent &event )
{
   if ( mMouseState != Rotating )
      return;

   Point2I delta = event.mousePoint - mLastMousePoint;
   mLastMousePoint = event.mousePoint;

   mCameraRot.x += ( delta.y * 0.01 );
   mCameraRot.z += ( delta.x * 0.01 );
}


//------------------------------------------------------------------------------
void GuiPlayerView::onRightMouseDown( const GuiEvent &event )
{
   mMouseState = Zooming;
   mLastMousePoint = event.mousePoint;
   mouseLock();
}


//------------------------------------------------------------------------------
void GuiPlayerView::onRightMouseUp( const GuiEvent &/*event*/ )
{
   mouseUnlock();
   mMouseState = None;
}


//------------------------------------------------------------------------------
void GuiPlayerView::onRightMouseDragged( const GuiEvent &event )
{
   if ( mMouseState != Zooming )
      return;

   S32 delta = event.mousePoint.y - mLastMousePoint.y;
   mLastMousePoint = event.mousePoint;

   mOrbitDist += ( delta * 0.01 );
}

void GuiPlayerView::setPlayerSeq( S32 index )
{
   if( index > MaxAnimations || index < 0 )
      return;

   mAnimationSeq = index;
}

//------------------------------------------------------------------------------
void GuiPlayerView::setPlayerModel( const char* shape, const char* skin )
{
   // Stuff random rotation values in...
   mCameraRot.z = gRandGen.randF(-4.14, -1);

   if ( mModel )
   {
      delete mModel;
      mModel = NULL;
   }

   if ( mWeapon )
   {
      delete mWeapon;
      mWeapon = NULL;
   }

   runThread = 0;

   Resource<TSShape> hShape = ResourceManager->load( shape);
   if ( !bool( hShape ) )
      return;

   mModel = new TSShapeInstance( hShape, true );
   AssertFatal( mModel, "ERROR!  Failed to load warrior model!" );

   // Set the skin:
   if ( !mModel->ownMaterialList() )
      mModel->cloneMaterialList();

   TSMaterialList* materialList = mModel->getMaterialList();
   for ( U32 i = 0; i < materialList->mMaterialNames.size(); i++ )
   {
      const char* name = materialList->mMaterialNames[i];
      if ( !name )
         continue;

      const U32 len = dStrlen( name );
      AssertISV( len < 200, "GuiPlayerView::setPlayerModel - Skin name exceeds maximum length!" );
      if ( len < 6 )
         continue;

      const char* replace = dStrstr( name, "base." );
      if ( !replace )
         continue;

      char newName[256];
      dStrncpy( newName, name, replace - name );
      newName[replace - name] = 0;
      dStrcat( newName, skin );
      dStrcat( newName, "." );
      dStrcat( newName, replace + 5 );

      TextureHandle test = TextureHandle( newName, MeshTexture, false );
      if ( test.getGLName() )
         materialList->mMaterials[i] = test;
      else
         materialList->mMaterials[i] = TextureHandle( name, MeshTexture, false );
   }

   // Initialize camera values:
   mOrbitPos = mModel->getShape()->center;
   mMinOrbitDist = mModel->getShape()->radius;

//   // initialize run thread
//   S32 sequence = hShape->findSequence("dummyRun");
//
//   if( sequence != -1 )
//   {
//      runThread = mModel->addThread();
//      mModel->setPos( runThread, 0 );
//      mModel->setTimeScale( runThread, 1 );
//      mModel->setSequence( runThread, sequence, 0 );
//   }

   // create a weapon for this dude
   Resource<TSShape> wShape;
   if( dStrcmp( shape, "heavy_male") == 0 || dStrcmp( shape, "bioderm_heavy") == 0 || dStrcmp( shape, "heavy_female") == 0 )
   {
      wShape = ResourceManager->load( "shapes/weapon_mortar.dts" );
   }
   else if( dStrcmp( shape, "medium_male") == 0 || dStrcmp( shape, "bioderm_medium") == 0 || dStrcmp( shape, "medium_female") == 0 )
   {
      wShape = ResourceManager->load( "shapes/weapon_plasma.dts" );
   }
   else
   {
      wShape = ResourceManager->load( "shapes/weapon_disc.dts" );
   }

   if ( !bool( wShape ) )
      return;

   pNode = hShape->findNode("mount0");
   wNode = wShape->findNode("mountPoint");

   mWeapon = new TSShapeInstance( wShape, true );
   AssertFatal( mWeapon, "ERROR!  Failed to load Weapon Model!" );

   // the first time recording
   lastRenderTime = Platform::getVirtualMilliseconds();
}

void GuiPlayerView::getWeaponTransform( MatrixF *mat )
{
   MatrixF weapTrans = mWeapon->mNodeTransforms[wNode];
   Point3F weapOffset = -weapTrans.getPosition();
   MatrixF modelTrans = mModel->mNodeTransforms[pNode];
   modelTrans.mulP( weapOffset );
   modelTrans.setPosition( weapOffset );
   *mat = modelTrans;
}

//------------------------------------------------------------------------------
bool GuiPlayerView::processCameraQuery( CameraQuery* query )
{
   // Make sure the orbit distance is within the acceptable range:
   mOrbitDist = ( mOrbitDist < mMinOrbitDist ) ? mMinOrbitDist : ( ( mOrbitDist > MaxOrbitDist ) ? MaxOrbitDist : mOrbitDist );

   // Adjust the camera so that we are still facing the model:
   Point3F vec;
   MatrixF xRot, zRot;
   xRot.set( EulerF( mCameraRot.x, 0, 0 ) );
   zRot.set( EulerF( 0, 0, mCameraRot.z ) );

   mCameraMatrix.mul( zRot, xRot );
   mCameraMatrix.getColumn( 1, &vec );
   vec *= mOrbitDist;
   mCameraPos = mOrbitPos - vec;

   query->nearPlane = 0.1;
   query->farPlane = 2100.0;
   query->fov = 3.1415 / 3.5;
   mCameraMatrix.setColumn( 3, mCameraPos );
   query->cameraMatrix = mCameraMatrix;

   return( true );
}


//------------------------------------------------------------------------------
void GuiPlayerView::renderWorld( const RectI &updateRect )
{
   if ( !(bool)mModel /* || !(bool)mWeapon */)
      return;

   S32 time = Platform::getVirtualMilliseconds();
   S32 dt = time - lastRenderTime;
   lastRenderTime = time;

   glClear( GL_DEPTH_BUFFER_BIT );
   glMatrixMode( GL_MODELVIEW );

   glEnable( GL_DEPTH_TEST );
   glDepthFunc( GL_LEQUAL );

   // animate and render in a run pose
   F32 fdt = dt;
//   mModel->advanceTime( fdt/1000.f, runThread );
   mModel->animate();
   mModel->render();

   // render a weapon, if we have one
   if(mWeapon)
   {
      MatrixF mat;
      getWeaponTransform( &mat );
      glPushMatrix();
      dglMultMatrix( &mat );

      mWeapon->render();

      glPopMatrix();
   }

   glDisable( GL_DEPTH_TEST );
   dglSetClipRect( updateRect );
   dglSetCanonicalState();
}

void GuiPlayerView::onMouseEnter(const GuiEvent & event)
{
   Con::executef(this, 1, "onMouseEnter");
}

void GuiPlayerView::onMouseLeave(const GuiEvent & event)
{
   Con::executef(this, 1, "onMouseLeave");
}
