#ifndef QRK_URG_CTRL_H
#define QRK_URG_CTRL_H

/*!
  \file
  \brief URG センサ制御

  \author Satofumi KAMIMURA

  $Id: UrgCtrl.h 1300 2009-09-15 06:36:54Z satofumi $
*/

#include "RangeSensor.h"
#include "Coordinate.h"


namespace qrk
{
  class Connection;


  /*!
    \brief URG センサ制御
  */
  class UrgCtrl : public Coordinate, public RangeSensor
  {
    UrgCtrl(const UrgCtrl& rhs);
    UrgCtrl& operator = (const UrgCtrl& rhs);

    struct pImpl;
    std::auto_ptr<pImpl> pimpl;

  protected:
    virtual void captureReceived(void);

  public:
    /*!
      \brief オプション用パラメータ
    */
    enum {
      DefaultBaudrate = 115200, //!< 接続時のデフォルトボーレート
      DefaultRetryTimes = 3,    //!< 通信失敗時のリトライ回数
      Infinity = 0,             //!< 無限回のデータ取得

      Off = 0,                  //!< レーザ Off 用
      On = 1,                   //!< レーザ On 用
    };

    UrgCtrl(void);
    virtual ~UrgCtrl(void);

    const char* what(void) const;

    bool connect(const char* device, long baudrate = DefaultBaudrate);
    void setConnection(Connection* con);
    Connection* connection(void);
    void disconnect(void);
    bool isConnected(void) const;

    long minDistance(void) const;
    long maxDistance(void) const;
    int maxScanLines(void) const;


    /*!
      \brief 通信失敗時のリトライ回数を設定

      通信に失敗したときのリトライ回数を指定する。正常に応答が返された場合、リトライ回数はリセットされる。ここで設定するリトライ回数は、送信と受信で共通となる。

      \param[in] times リトライ回数

      \attention #AutoCapture のときのみ有効
    */
    void setRetryTimes(size_t times);


    /*!
      \brief 内部で保存するスキャンデータ数を設定

      #AutoCapture, #IntensityCapture の各モードでは、UrgCtrl クラス内部でスキャンデータを保持している。このメソッドでは、そのスキャンデータの保持数を指定できる。

      \param[in] size スキャンデータの保持数
    */
    void setCapturesSize(size_t size);


    /*!
      \brief １スキャンにかかる時間

      \return １スキャンにかかる時間 [msec]
    */
    int scanMsec(void) const;


    /*!
      \brief データ取得モードの指定

      \param[in] mode #RangeCaptureMode で定義されているモードを指定できる。

      \see captureMode()
    */
    void setCaptureMode(RangeCaptureMode mode);


    /*!
      \brief データ取得モード

      \return 現在のデータ取得モード

      \see setCaptureMode()
    */
    RangeCaptureMode captureMode(void);


    /*!
      \brief データ取得範囲の指定

      \ref scip_capture_parameter_section を参照のこと

      \param[in] begin_index 測定開始位置
      \param[in] end_index 測定終了位置
    */
    void setCaptureRange(int begin_index, int end_index);


    /*!
      \brief スキャンデータの取得間隔

      データ取得頻度を下げることにより、URG とライブラリ間のデータ通信量を減少できる。\n
      \ref scip_capture_parameter_section を参照のこと

      \param[in] interval 何回に１回だけデータを取得するか
    */
    void setCaptureFrameInterval(size_t interval);


    /*!
      \brief データ取得回数の指定

      \ref scip_capture_parameter_section を参照のこと

      \param[in] times データ取得回数

      \attention データ取得モードが #AutoCapture, #IntensityCapture のときのみ有効
    */
    void setCaptureTimes(size_t times);


    /*!
      \brief 残りデータ取得回数の取得

      \return 残りデータ取得回数

      \attention データ取得モードが #AutoCapture, #IntensityCapture のときのみ有効
    */
    size_t remainCaptureTimes(void);


    /*!
      \brief 取得データのグループ化設定

      複数個の取得データを１つにまとめ、取得データ量を減少させる。

      \param[in] skip_lines １つにまとめる取得データ数

      例えば、まとめる取得データ数が１のときのデータが
      \verbatim
100, 101, 103, 104, 105, 100, ... \endverbatim

      の場合、まとめる取得データ数を２にすると、取得されるデータは
      \verbatim
100, 103, 100, ... \endverbatim

      となる。(一番短い距離データが返される)\n
      これにより、URG とライブラリ間のデータ通信量を減少できる。

      ただし、URG とライブラリ間の通信データは減少するが capture() が返すデータ量は変化せず、
      \verbatim
100, 100, 103, 103, 100, 100, ... \endverbatim

      が返される。\n
      データ量が変化しないのは rad2index(), index2rad() の動作を保証するため。
    */
    void setCaptureSkipLines(size_t skip_lines);


    int capture(std::vector<long>& data, long* timestamp = NULL);


#if defined(USE_INTENSITY)
    /*
      \brief 強度データと距離データの取得

      \attention Top-URG, Classic-URG のみ対応 (2008-12-24 現在)
    */
    int captureWithIntensity(std::vector<long>& data,
                             std::vector<long>& intensity_data,
                             long* timestamp = NULL);
#endif


    /*!
      \brief データ取得の中断

      \attention データ取得モードが #AutoCapture, #IntensityCapture のときのみ有効
    */
    void stop(void);


    bool setTimestamp(int timestamp = 0, int* response_msec = NULL,
                      int* force_delay_msec = NULL);

    long recentTimestamp(void) const;


    /*!
      \brief レーザの点灯/消灯

      \param[in] on true で点灯 / false で消灯
    */
    bool setLaserOutput(bool on);

    double index2rad(const int index) const;
    int rad2index(const double radian) const;

    void setParameter(const RangeSensorParameter& parameter);
    RangeSensorParameter parameter(void) const;


    /*!
      \brief URG パラメータの再読み込み

      \retval true 成功
      \retval false エラー
    */
    bool loadParameter(void);


    /*!
      \brief バージョン情報の取得

      \param[out] lines バージョン文字列の格納先

      \retval true 成功
      \retval false エラー

      <b>Example</b>
      - \ref viewVvSample.cpp
    */
    bool versionLines(std::vector<std::string>& lines);

    bool reset(void);
  };
}

#endif /* !QRK_URG_CTRL_H */
