/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   RotationList is a sequence of matrix rotations.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define OBJECT_ROTATION_LIST_CC 1
#include <list>
#include <fstream>
#include "base/module.hh"
using namespace base;
#include "math/module.hh"
#include "math/types_trig.hh"
#include "math/funcs_trig.hh"
#include "math/matrix.hh"
#include "math/matrix_funcs.hh"
using namespace math;
#include "physics/module.hh"
#include "physics/physics_control.hh"
using namespace physics;
#include "object/module.hh"
#include "object/object.hh"
#include "object/rotation_list.hh"
#include <fstream>

namespace object {

INTERN const uint ROTATION_LIST_MAX_ROTATIONS = 100;

////////////////////////////////////////////////////////////////////////////////
///////////////////////  RotationList::Rotation  ///////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor.
 *****************************************************************************/
RotationList::Rotation::Rotation( const uint axis, const Degree degree )
:   mAxis(axis),
    mRadian(Deg2Rad(degree))
{
ASSERT_AXIS3(axis);
}

////////////////////////////////////////////////////////////////////////////////
//////////////////////////////  RotationList  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
RotationList::RotationList( void )
:   mRotationList()
{
    // NOP
}

RotationList::~RotationList()
{
    // NOP
}

/*****************************************************************************
 * Append a rotation (around an axis) to list.
 *****************************************************************************/
void
RotationList::AddRotation( const Rotation& rotation )
{
    mRotationList.push_back( rotation );
}

/*****************************************************************************
 * Parse a rotation list from a line in a stream in this format:
 * { axis degree axis degree ... }
 * This method is used to parse .conf files.
 *****************************************************************************/
void
RotationList::Parse( std::ifstream& strm )
{
    string w; strm >> w;  // parse begin brace '{'

    while ( strm.good() )
    {
        // Parse symbolic axis XX,YY,ZZ or end brace
        string axisStr;
        strm >> axisStr;

        // End brace?
        if ( axisStr == "}" )
            break;

        // Convert axis from string to uint.
        uint axis = AXIS_INVALID;
        if ( axisStr == "XX" )
            axis = XX;
        else if ( axisStr == "YY" )
            axis = YY;
        else if ( axisStr == "ZZ" )
            axis = ZZ;

        // Did the user mistype an axis?
        if ( UX( axis == AXIS_INVALID ) )
            throw Exception( "ERROR: misparsed RotationList axis" );

        // Parse degree.
        fp degree = 0.0f;
        strm >> degree;

        // Add rotation (degree around one axis) to list.
        AddRotation( Rotation( axis, degree ) );
    }
}

/*****************************************************************************
 * Apply a series of rotations to a Matrix.
 *****************************************************************************/
void
RotationList::Apply( Matrix& m ) const
{
    DEBUG_CODE( uint i = 0; )

    for ( std::list<Rotation>::const_iterator iter = mRotationList.begin();
          iter != mRotationList.end();
          ++iter )
    {
        const Rotation& rotation = *iter;
        ASSERT_AXIS3( rotation.mAxis );
        MatrixRotateLocal<>( m, rotation.mAxis, rotation.mRadian );

        ASSERT( ++i < ROTATION_LIST_MAX_ROTATIONS );  // overrun?
    }
}

/*****************************************************************************
 * Apply a series of rotations to a 3D Object.
 * Apply(Matrix&) cannot be reused since it uses MatrixRotateLocal<>()
 * which may be inappropriate for some Object-derivatives.
 *****************************************************************************/
void
RotationList::Apply( Object& object ) const
{
CHECK_TYPESIG(&object,TYPESIG_OBJECT);

    DEBUG_CODE( uint i = 0; )

// Disable physics in case this is a Dyna as the rotations
// could cause an unintentional collision.
const bool physics = PhysicsControl::IfEnabled();
PhysicsControl::Enable( false );

    for ( std::list<Rotation>::const_iterator iter = mRotationList.begin();
          iter != mRotationList.end();
          ++iter )
    {
        const Rotation& rotation = *iter;
        ASSERT_AXIS3( rotation.mAxis );
        object.Rotate( rotation.mAxis, rotation.mRadian );

        ASSERT( ++i < ROTATION_LIST_MAX_ROTATIONS );  // overrun?
    }

PhysicsControl::Enable( physics );

}

} // namespace object
