/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Commands to control an Aircraft.
 *//*
 * 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_COMMAND_HH
#define CONTROL_COMMAND_HH 1

#include "base/stream.hh"
using namespace base;
#include "math/module.hh"
#include "math/quaternion.hh"
using namespace math;
#include "object/module.hh"
#include "object/aircraft.hh"
using namespace object;

namespace control {

class Command;

// Asymmetrically, operator<<() (write) is defined, but operator>>() (read) is N/A,
// since reading a derivative from stream into base is illogical.
// Rather, use the factory method Command::Read(istream&).

std::ostream& operator<<( std::ostream& strm, /*const*/ Command& command );

////////////////////////////////////////////////////////////////////////////
/// @brief Defines a command to control an Aircraft.
///
/// This is both an abstract class and a factory class.
/// Read(istream&) can construct a derivative from a stream.
///
class Command : public Shared
{
public:
    ////////////////////////////////////////////////////////////////////////////
    /// @brief Supports graphical or physical replay modes.
    ///
    /// EXECUTE_MODE_PHYSICS is the most realistic but vulnerable
    /// to malfunctioning if system is running too slow or
    /// player selects different aircraft type.
    /// EXECUTE_MODE_GRAPHICS_PHYSICS is a compromise which ensures
    /// every point is replayed while replaying control (gears, brakes, etc).
    ///
    enum ExecuteMode
    {
        // -- update replay.lua --
        EXECUTE_MODE_GRAPHICS           = 1<<0,
        EXECUTE_MODE_PHYSICS            = 1<<1,
        EXECUTE_MODE_GRAPHICS_PHYSICS   = EXECUTE_MODE_GRAPHICS | EXECUTE_MODE_PHYSICS,
        EXECUTE_MODE_ALWAYS             = EXECUTE_MODE_GRAPHICS | EXECUTE_MODE_PHYSICS,
        EXECUTE_MODE_DEFAULT            = EXECUTE_MODE_GRAPHICS_PHYSICS,
    };
                        Command( void ) { SET_TYPESIG(this,TYPESIG_COMMAND); }
    virtual             ~Command()      { INVALIDATE_TYPESIG(this,TYPESIG_COMMAND); }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode ) = 0;
    virtual void        Write( std::ostream& strm ) = 0;
    CLASS_METHOD shptr<Command> Read( std::istream& strm );  // factory method

    // Unavoidable type codes.
    enum TypeNum { TYPE_NOP, TYPE_POSITION, TYPE_QUATERNION, TYPE_PHYSICAL_ROTATE,
           TYPE_SPEED, TYPE_THROTTLE, TYPE_LANDING_GEAR, TYPE_BRAKES };
    virtual TypeNum     GetType( void ) = 0;

public:
    DECLARE_TYPESIG(TYPESIG_COMMAND)
};

////////////////////////////////////////////////////////////////////////////
/// @brief NOP command.
///
/// For Replay::Step default ctor.
///
class CommandNop : public Command
{
public:
                        CommandNop( void ) { }
    virtual TypeNum     GetType( void ) { return TYPE_NOP; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode ) { }
    virtual void        Write( std::ostream& strm ) { }
};

////////////////////////////////////////////////////////////////////////////
/// @brief Set position command.
///
class CommandPosition : public Command
{
public:
                        CommandPosition( const GeoVertex& geoPos );
    virtual TypeNum     GetType( void ) { return TYPE_POSITION; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode );
    virtual void        Write( std::ostream& strm );
    WorldVertex         GetWorldVertex( void );

private:
    const GeoVertex     mGeoPos;
};

////////////////////////////////////////////////////////////////////////////
/// @brief Set graphical rotation (quaternion) command.
///
class CommandQuaternion : public Command
{
public:
                        CommandQuaternion( const Quaternion& quat );
    virtual TypeNum     GetType( void ) { return TYPE_QUATERNION; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode );
    virtual void        Write( std::ostream& strm );
    Quaternion          GetQuaternion( void ) { CHECK_TYPESIG(this,TYPESIG_COMMAND);
                                                return mQuat; }

private:
    const Quaternion    mQuat;
};

////////////////////////////////////////////////////////////////////////////
/// @brief Rotation according to physics model (for physical replay).
/// Correlates to Aircraft::PhysicalRotate().
///
class CommandPhysicalRotate : public Command
{
public:
                        CommandPhysicalRotate( uint axis, fp controlFraction, Milliseconds controlFreq );
    virtual TypeNum     GetType( void ) { return TYPE_PHYSICAL_ROTATE; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode );
    virtual void        Write( std::ostream& strm );

private:
    const uint          mAxis;
    const fp            mControlFraction;
    const Milliseconds  mControlFreq;
};

////////////////////////////////////////////////////////////////////////////
/// @brief Set speed.
///
class CommandSpeed : public Command
{
public:
                        CommandSpeed( const Speed speed );
    virtual TypeNum     GetType( void ) { return TYPE_SPEED; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode );
    virtual void        Write( std::ostream& strm );
    Speed               GetSpeed( void ) { CHECK_TYPESIG(this,TYPESIG_COMMAND);
                                           return mSpeed; }
private:
    const Speed mSpeed;
};

////////////////////////////////////////////////////////////////////////////
/// @brief Set throttle command.
///
class CommandThrottle : public Command
{
public:
                        CommandThrottle( const fp throttle );
    virtual TypeNum     GetType( void ) { return TYPE_THROTTLE; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode );
    virtual void        Write( std::ostream& strm );

private:
    const fp mThrottle;
};

////////////////////////////////////////////////////////////////////////////
/// @brief Set landing gear command.
///
class CommandLandingGear : public Command
{
public:
                        CommandLandingGear( const bool down );
    virtual TypeNum     GetType( void ) { return TYPE_LANDING_GEAR; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode );
    virtual void        Write( std::ostream& strm );

private:
    const bool mDown;
};

////////////////////////////////////////////////////////////////////////////
/// @brief Set brakes command.
///
class CommandBrakes : public Command
{
public:
                        CommandBrakes( const bool brakes );
    virtual TypeNum     GetType( void ) { return TYPE_BRAKES; }
    virtual void        Execute( shptr<Aircraft> aircraft, const ExecuteMode mode );
    virtual void        Write( std::ostream& strm );

private:
    const bool mBrakes;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief Exception in Command.
///
class ExceptionCommand : public base::Exception
{
public:
    ExceptionCommand( const string& text ) : Exception(text) { }
};

} // namespace control

#endif // CONTROL_COMMAND_HH
