/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Dyna class.
 *//*
 * 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).
 *****************************************************************************/

#ifndef OBJECT_DYNA_HH
#define OBJECT_DYNA_HH 1

#include <utility>
#include <vector>
#include "base/timer.hh"

namespace object {

////////////////////////////////////////////////////////////////////////////////
/// @brief Dyna class.
///
/// Optionally, a Dyna-derivative can do animation (or any periodic action)
/// by calling Dyna::RegisterTick() and overriding Dyna::Tick().
///
/// Translate() vs. SetMatrix():
/// ----------------------------
/// Calling Translate() or SetMatrix(m,TRANSLATION) is considered movement (stepping).
/// Calling SetMatrix(void) isn't.
/// The difference affects collision-detection.
///
/// Collision vs. fatal collision:
/// ------------------------------
/// Collision means an intersection -- but a collision isn't necessarily fatal.
/// Two objects could be in "safe contact" (aircraft in contact with runway).
/// Dyna reports a collision, while physics, which knows the velocity,
/// determines whether a collision is fatal or not.
///
/// Collision-detection uses GetHeight() to form a vertical ray.
/// Dyna::GetHeight() can be overridden by derivatives for more accuracy.
///
/// Collision-detection in Object hierarchy:
/// ----------------------------------------
/// Object defines the collision flag (settably by any collision-detect test).
/// Dyna does a collision-detect test using the "collision" module.
/// Craft determines what kind of collision: safe contact or fatal collision.
///
class Dyna : public Object
{
PREVENT_COPYING(Dyna)
typedef Object Parent;
public:     enum { NON_COLLIDABLE, COLLIDABLE };
public:                             Dyna( shptr<Graph> graph, const WorldVertex& pos, const bool collidable = true );
public:     virtual                 ~Dyna();
public:     virtual void            Reset( void );
public:     virtual const string    GetName( void ) { return string("Dyna"); }
public:     virtual bool            IfDyna( void ) { CHECK_TYPESIG(this,TYPESIG_DYNA); return true; }
public:     virtual bool            IfReproducible( void ) { CHECK_TYPESIG(this,TYPESIG_DYNA); return false; }

// Spatial:
public:     virtual fp              GetHeight( void ) { CHECK_TYPESIG(this,TYPESIG_DYNA);
                                                        return GetRadius() * 2.0f; }
// Transformation:
public:     virtual void            SetMatrix( const Matrix& matrix, Object::eMatrixChange change = PLACEMENT );

// Collision-detection:
public:     typedef std::vector<shptr<Object> > Colliders;  ///< container of Objects that are in collision
protected:  virtual void            DetectCollision( void );
protected:  virtual void            HandleCollisions( const Colliders& colliders ) { }  // Template Method
public:     virtual const std::pair<WorldVertex,WorldVertex> GetTranslationStep( void );

// Animation:
protected:  virtual void            Tick( const Milliseconds millisecElapsed );

private:
    /// @brief Reflects tick event to Dyna::Tick().
    /// Indeed, TickFunctor doesn't have a shptr<Dyna>.
    /// It would create a cycle of reference-counted pointers.
    /// ~Dyna() unregisters Dyna::TickFunctor but ~Dyna() would
    /// never be called if TickFunctor had a reference to Dyna!
    ///
    CLASS_TICK_FUNCTOR( Dyna, mDyna )

// Data:
private:    shptr<TickFunctor>  mTickFunctor;       ///< reflects to Dyna::Tick()
protected:  WorldVertex         mTranslationBegin;  ///< begin and end points of last translation
protected:  WorldVertex         mTranslationEnd;    ///< used for collision-detection
public:     DECLARE_TYPESIG(TYPESIG_DYNA)
};

} // namespace object

#endif // OBJECT_DYNA_HH
