/***************************************************************************
 *   Copyright (C) 2007 by A.J. Tavakoli                                   *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   This program is distributed in the hope that it will be useful,       *
 *   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.             *
 ***************************************************************************/

// temporary
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <GL/gl.h>

#include <cstddef>
#include "MD5Entity.h"
#include "BSP.h"


Quat MD5Entity::md5Rot = Quat::fromEulerAngles(-90.0f, 0.0f, 0.0f);


MD5Entity::MD5Entity(MD5Model *md5, bool _cleanUp):
     Entity(_cleanUp) {
  if ( NULL == md5 )
    throw std::runtime_error("MD5Entity::MD5Entity(): md5 pointer is NULL");

  AABB aabb = md5->getBoundingBox();
  aabbExt = Vector3( aabb.getExt()[0], aabb.getExt()[2], aabb.getExt()[1] );

  mass = 1.0f; // mass should be set somewhere else...
  model = md5;
  currAnim = 0;
  currFrame = 0;
  animTime = 0.0f;
}


void MD5Entity::render() {
  // if there is no model data, then there is nothing to render
  if ( NULL == model || currAnim < 0 || currFrame < 0 )
    return;

  // set md5 model to current animation and frame
  model->setAnim(currAnim, currFrame, animTime);
    
  // extract bounding box from MD5 model
  // (note that changing the bounding box of the entity is not changed
  //  since this causes the entity to tunnel)
  AABB md5AABB = model->getBoundingBox();

  // get offset of model's bounding box from origin, this will be used
  // to offset the model 
  //Vector3 transOffset(md5AABB.getCenter()[0],
  //                    md5AABB.getCenter()[2],
  //                    md5AABB.getCenter()[1]);

  // only offset in vertical direction for this demo
  Vector3 transOffset = Vector3(0.0f, md5AABB.getCenter()[2], 0.0f);

  Point3 translation(pos[0], pos[1], pos[2]);
  Video::setModelTranslation(translation - transOffset);
  Video::setModelRotation( md5Rot*getRot() );

  Video::applyTransforms();

  for( int i=0; i < model->getNumMeshes(); i++ ) {
    const MD5Model::Mesh *mesh = model->getMesh(i);
    Video::useTexture(mesh->texID);
    Video::useNormalMap(mesh->nmID);
    Video::renderIndexedTris( &mesh->verts[0], &mesh->tris[0].v[0], (int)mesh->tris.size() );
  }

  //Video::setModelTranslation(translation);
  //Video::applyTransforms();
  //renderAxis();
}


void MD5Entity::update(float dt, BSP &world) {
  updateAnim(dt);
}


void MD5Entity::setAnim(int animIndex, int frameIndex, float t) {
  currAnim  = animIndex;
  currFrame = frameIndex;
  animTime  = t;
}


void MD5Entity::updateAnim(float dt) {
  if ( NULL == model || currAnim < 0 )
    return;

  // get number of frames in current animation
  int numFrames = model->getNumAnimFrames(currAnim);

  // do not continue if animation does not have frames for some reason
  if ( numFrames <= 0 )
    return;

  // make sure frame index is valid
  if ( currFrame >= numFrames )
    return;

  // update animation timer
  animTime += float(dt)*model->getAnimFrameRate(currAnim);

  // next frame?
  if ( animTime > 1.0f ) {
    // step or loop animation
    if ( currFrame+1 >= numFrames )
      currFrame = 0;
    else
      currFrame++;

    animTime = 0.0f;
  }
}
