#ifndef QRK_RUNNING_H
#define QRK_RUNNING_H

/*!
  \file
  \brief 走行モジュールのインターフェース定義

  \author Satofumi KAMIMURA

  $Id: Running.h 1584 2009-12-25 23:25:11Z satofumi $
*/

#include "Position.h"
#include "Point.h"


namespace qrk
{
  class Connection;
  class Coordinate;


  //! 走行モジュールのインターフェース
  class Running
  {
  public:
    virtual ~Running(void)
    {
    }


    /*!
      \brief 内部状態を返す

      \return 内部状態を示す文字列

      <strong>Example</strong>
\code
DifferentialDrive run;
if (! run.connect(device, baudrate)) {
  // 接続に失敗したときに、エラーメッセージを表示する
  printf("connect: %s\n", run.what());
  exit(1);
} \endcode
    */
    virtual const char* what(void) const = 0;


    /*!
      \brief 接続

      \param[in] device 接続デバイス名
      \param[in] baudrate 接続ボーレート

      \retval true 成功
      \retval false 失敗

      <strong>Example</strong>
\code
const char device[] = "/dev/usb/ttyUSB0";
const long baudrate = 115200;
DifferentialDrive run;

// 指定デバイスに接続する
if (! run.connect(device, baudrate)) {
  printf("connect: %s\n", run.what());
  exit(1);
} \endcode
    */
    virtual bool connect(const char* device, long baudrate) = 0;


    /*!
      \brief 切断
    */
    virtual void disconnect(void) = 0;


    /*!
      \brief 接続状態を返す

      \retval true 接続中
      \retval false 切断中
    */
    virtual bool isConnected(void) const = 0;


    /*!
      \brief 接続オブジェクトの指定

      \param[in] connection 接続オブジェクト

      \attention 内部で保持している接続オブジェクトの解放はユーザ側で保証すること
    */
    virtual void setConnection(Connection* connection) = 0;


    /*!
      \brief 接続オブジェクトの取得

      \return 接続オブジェクト
    */
    virtual Connection* connection(void) = 0;


    /*!
      \brief 直線追従

      !!! 図

      \param[in] line 追従する直線
      \param[in] coordinate 直線を定義する座標系
    */
    virtual void followLine(const Position<long>& line,
                            const Coordinate* coordinate) = 0;

    //! \overload
    virtual void followLine(long x, long y, const Angle& angle,
                            const Coordinate* coordinate = NULL) = 0;


    /*!
      \brief 直線追従して停止

      直線追従を行い、直線の指定点に垂直な線上で停止する。

      !!! 図

      \param[in] line 追従する直線
      \param[in] coordinate 直線を定義する座標系
    */
    virtual void stopLine(const Position<long>& line,
                          const Coordinate* coordinate) = 0;

    //! \overload
    virtual void stopLine(long x, long y, const Angle& angle,
                          const Coordinate* coordinate = NULL) = 0;


    /*!
      \brief 円弧追従

      radius が正の値のときに、反時計回りに走行する。

      !!! 図

      \param[in] center 円の中心
      \param[in] radius 半径
      \param[in] coordinate 円を定義する座標系
    */
    virtual void followCircle(const Point<long>& center, long radius,
                      const Coordinate* coordinate) = 0;


    /*!
      \brief 円弧追従

      円弧追従を行い、指定された角度に垂直な線上で停止する。

      !!! 図

      \param[in] center 円の中心
      \param[in] radius 半径
      \param[in] angle 停止させる位置
      \param[in] coordinate 円を定義する座標系
    */
    virtual void stopCircle(const Point<long>& center, long radius,
                            const Angle& angle,
                            const Coordinate* coordinate) = 0;


    /*!
      \brief 指定角度だけ回転する

      angle_per_sec が正の値のときに、反時計回りに回転する。

      !!! 図

      \param[in] angle 回転角度 [Angle]
    */
    virtual void spin(const Angle& angle) = 0;


    /*!
      \brief 向きの制御

      向きを制御する。指定された向きまでの回転方向は実装依存とする。

      !!! 図

      angle に 360 [deg] 以上が指定された場合 coordinate がロボット座標系の場合は、その角度だけ回転する。coordinate がロボット座標系でない場合は、360 [deg] の剰余となる位置を向くように回転する。

      \param[in] angle 停止させる向き
      \param[in] coordinate 向きを定義する座標系
    */
    virtual void rotate(const Angle& angle, const Coordinate* coordinate) = 0;


    /*!
      \brief 停止

      等速度で停止する
    */
    virtual void stop(void) = 0;


    virtual void freeServo(void) = 0;


    /*!
      \brief 推定自己位置の取得

      \return 推定自己位置
    */
    virtual Position<long> position(const Coordinate* coordinate) const = 0;


    /*!
      \brief 並進の最高速度の取得

      \return 並進の最高速度 [mm / sec]
    */
    virtual long maxStraightVelocity(void) const = 0;


    /*!
      \brief 並進速度の取得

      \return 並進速度 [mm / sec]
    */
    virtual long straightVelocity(void) const = 0;


    /*!
      \brief 回転速度の取得

      \return 回転速度 [angle / sec]
    */
    virtual Angle rotateVelocity(void) const = 0;


#if 0
    /*!
      \brief 位置の設定

      \param[in] coordinate 位置を指定する座標系
      \param[in] position 位置

      \attention 位置を設定するために position() を内部で呼び出しているため、ロボットが停止しているときに呼び出さないと修正が適切に動作しない可能性がある。
    */
    virtual void setRobotPosition(const Position<long>& position,
                                  const Coordinate* coordinate) = 0;


    virtual void setRobotPositionOffset(const Position<long>& offset) = 0;
#endif


    /*!
      \brief ロボット位置の修正

      \param[in] from 変更前のロボット位置
      \param[in] to 変更後のロボット位置
    */
    virtual void changeRobotPosition(const Position<long>& from,
                                     const Position<long>& to) = 0;


    /*!
      \brief 並進速度の設定

      \param[in] mm_per_sec 並進速度 [mm / sec]
    */
    virtual void setStraightVelocity(long mm_per_sec) = 0;


    /*!
      \brief 回転速度の設定

      \param[in] angle_per_sec 回転加速度 [mm / sec]
    */
    virtual void setRotateVelocity(const Angle& angle_per_sec) = 0;


    /*!
      \brief 並進加速度の設定

      \param[in] mm_per_sec2 並進加速度 [mm / sec^2]
    */
    virtual void setStraightAcceleration(long mm_per_sec2) = 0;


    /*!
      \brief 回転加速度の設定

      \param[in] angle_per_sec2 回転加速度 [mm / sec^2]
    */
    virtual void setRotateAcceleration(const Angle& angle_per_sec2) = 0;


    /*!
      \brief 移動コマンドが安定しているかの取得

      以下の場合に true を返す。
      - 直線に追従し続ける場合に、目標直線に追従中
      - 線上で停止する場合に、線上で停止している
      - 回転し続ける場合に、目標速度に達している
      - 向きの制御中に、その方向を向いて停止している

      \retval true 制御が安定している
      \retval false 制御の途中
    */
    virtual bool isStable(void) const = 0;
  };
}

#endif /* !QRK_RUNNING_H */
