/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Replay data class (animation path).
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef CONTROL_REPLAY_PATH_HH
#define CONTROL_REPLAY_PATH_HH 1

#include <utility>
#include <list>
#include "base/stream.hh"

namespace control {

////////////////////////////////////////////////////////////////////////////////
/// @brief Replay data (animation path).
///
/// ReplayPath is responsible for discarding redundant consecutive Commands.
///
class ReplayPath : public Shared
{
PREVENT_COPYING(ReplayPath)
public:
    ////////////////////////////////////////////////////////////////////////////
    /// @brief A step in the animation path (Craft command with time-stamp).
    ///
    struct Step
    {
        Step( void );
        Step( const Milliseconds relativeTime, shptr<Command> command );
        ~Step();
        Milliseconds    mMilliseconds;      ///< time relative to beginning of recording/playing
        shptr<Command>  mCommand;           ///< command to control Craft
        DECLARE_TYPESIG(TYPESIG_REPLAY_STEP)
    };
    friend struct Step;

public:
    typedef std::list<Step> Path;

public:
    ////////////////////////////////////////////////////////////////////////////
    /// @brief Cursor tracks a locus of steps in the animation path for interpolation.
    ///
    /// Cursor facilitates interpolating 3D positions and rotations.
    /// ReplayPath::Advance() calls Cursor::Advance() which tracks
    /// pairs of Steps having CommandPosition and CommandQuaternion.
    ///
    /// Cursor natually tracks the current Step.
    /// Additionally, because of interpolation, it also has references to
    /// position/rotation commands to execute in the future (lookahead).
    ///
    class Cursor
    {
    PREVENT_COPYING(Cursor);
    public:
        typedef std::pair<Step,Step> PositionSteps;
        typedef std::pair<Step,Step> QuaternionSteps;

    public:
                        Cursor( ReplayPath::Path& path );
                        ~Cursor();
        bool            IfEnd( void ) { return mPathIter == mPath.end(); }
        void            Rewind( void );
        void            Advance( void );
        Step&           GetCurrentStep( void ) { ASSERT(not IfEnd()); return *mPathIter; }
        bool            IfHaveQuaternionSteps( void );
        bool            IfHavePositionSteps( void );
        QuaternionSteps GetQuaternionSteps( void );
        PositionSteps   GetPositionSteps( void );

    private:
        void            Advance_FindNextStep( ReplayPath::Step& prevStep,
                                              ReplayPath::Step& nextStep,
                                              const ReplayPath::Path::iterator prevIter,
                                              const Command::TypeNum commandType );
    private:
        // Invalid is indicated by Step pointing to CommandNop.
        ReplayPath::Path&           mPath;
        ReplayPath::Path::iterator  mPathIter;
        ReplayPath::Step            mPrevQuaternionStep;
        ReplayPath::Step            mNextQuaternionStep;
        ReplayPath::Step            mPrevPositionStep;
        ReplayPath::Step            mNextPositionStep;
    };

// Interface:
public:
                        ReplayPath( void );
                        ~ReplayPath();
    bool                IfEmpty( void ) const { return mPath.empty(); }
    void                Clear( void );
    void                Rewind( void );
    void                Append( const Step& step );
    Cursor&             GetCursor( void ) { return mCursor; }

    // Emulate STL container:
    Path::iterator      begin( void ) { return mPath.begin(); }
    Path::iterator      end( void ) { return mPath.end(); }
    bool                empty( void ) const { return IfEmpty(); }
    void                clear( void ) { Clear(); }
    void                push_back( const Step& step ) { Append(step); }
    uint                size( void ) const { return mPath.size(); }

    // Pathnames:
    CLASS_METHOD string GetPathnameForLoading( const string& filename );
    CLASS_METHOD string GetPathnameForSaving( const string& filename );

private:
    bool                IfSameAsPrevCommand( Command& command );

private:
    Path            mPath;      ///< replay data consists of steps in animation path
    Cursor          mCursor;    ///< defines current set of steps in animation path

friend std::ostream& operator<<( std::ostream& strm, const Step& step );
friend std::istream& operator>>( std::istream& strm, Step& step );
};

std::ostream& operator<<( std::ostream& strm, const ReplayPath::Step& step );
std::istream& operator>>( std::istream& strm, ReplayPath::Step& step );

} // namespace control

#endif // CONTROL_REPLAY_PATH_HH
