#ifndef QRK_RANGE_SENSOR_H
#define QRK_RANGE_SENSOR_H

/*!
  \file
  \brief レンジセンサのインターフェース定義

  \author Satofumi KAMIMURA

  $Id: RangeSensor.h 1053 2009-06-20 22:52:51Z satofumi $
*/

#if !defined(NO_INTENSITY)
#define USE_INTENSITY
#endif

#include "RangeCaptureMode.h"
#include "MathUtils.h"
#include <vector>
#include <string>


namespace qrk
{
  class Connection;
  class RangeSensorParameter;


  /*!
    \brief 測域センサ・インターフェース
  */
  class RangeSensor
  {
  public:
    virtual ~RangeSensor(void) {}


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

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

      <strong>Example</strong>
\code
UrgCtrl sensor;
if (! sensor.connect(device, baudrate)) {
  // 接続に失敗したときに、エラーメッセージを表示する
  printf("connect: %s\n", sensor.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/ttyACM0";
const long baudrate = 115200;
UrgCtrl sensor;

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


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

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

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


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

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


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


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

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


    /*!
      \brief 有効距離の最小値を取得

      \return 有効距離の最小値
    */
    virtual long minDistance(void) const = 0;


    /*!
      \brief 有効距離の最大値を取得

      \return 有効距離の最大値
    */
    virtual long maxDistance(void) const = 0;


    /*!
      \brief １周の最大スキャン数

      \return １周の最大スキャン数
    */
    virtual int maxScanLines(void) const = 0;

    virtual int scanMsec(void) const = 0;

    virtual void setCaptureMode(RangeCaptureMode mode) = 0;
    virtual RangeCaptureMode captureMode(void) = 0;


    /*!
      \brief データ取得

      データ取得を行い、バッファに格納する

      \param[out] data 取得データ用バッファ
      \param[out] timestamp タイムスタンプ

      \return 取得データ数
      \retval <0 取得失敗
    */
    virtual int capture(std::vector<long>& data, long* timestamp) = 0;


#if defined(USE_INTENSITY)
    virtual int captureWithIntensity(std::vector<long>& data,
                                     std::vector<long>& intensity_data,
                                     long* timestamp) = 0;
#endif


    /*!
      \brief センサが返すタイムスタンプ値の設定

      capture() で取得できるタイムスタンプを設定する

      \param[in] timestamp その時刻のタイムスタンプの設定値
      \param[out] response_msec メッセージの送受信に要した時間 [msec]
      \param[in] force_delay_msec 強制的に指定する遅延時間

      \retval true 成功
      \retval false 失敗
    */
    virtual bool setTimestamp(int timestamp = 0, int* response_msec = NULL,
                              int* force_delay_msec = NULL) = 0;


    /*!
      \brief 最新のタイムスタンプ値

      \return タイムスタンプ値
    */
    virtual long recentTimestamp(void) const
    {
      return 0;
    }


    virtual bool setLaserOutput(bool on) = 0;


    /*!
      \brief データインデックスの radian 角度変換

      センサ前面が、0.0 [rad] に相当する。

      \param[in] index データインデックス

      \return 角度 [radian]

      \image html sensor_radian.png センサ上面からの角度対応図

      \see index2deg(), rad2index(), deg2index()
    */
    virtual double index2rad(const int index) const = 0;


    /*!
      \brief データインデックスの degree 角度変換

      センサ前面が、0.0 [rad] に相当する。

      \param[in] index データインデックス

      \return 角度 [degree]

      \image html sensor_radian.png センサ上面からの角度対応図

      \see index2rad(), rad2index(), deg2index()
    */
    int index2deg(const int index) const
    {
      return static_cast<int>(floor((180.0 * index2rad(index) / M_PI) + 0.5));
    }


    /*!
      \brief radian 角度のデータインデックス変換

      センサ前面が、0.0 [rad] に相当する。

      \param[in] radian 角度 [radian]

      \return データインデックス

      \see deg2index(), index2rad(), index2deg()
    */
    virtual int rad2index(const double radian) const = 0;


    /*!
      \brief データインデックスの角度変換

      センサ前面が、0.0 [rad] に相当する。

      \param[in] degree 角度 [degree]

      \return インデックス

      \see rad2index(), index2rad(), index2deg()
    */
    int deg2index(const int degree) const
    {
      return rad2index(degree * M_PI / 180.0);
    }


    /*!
      \brief URG パラメータの更新

      \param[in] parameter 更新する URG パラメータ
    */
    virtual void setParameter(const RangeSensorParameter& parameter) = 0;


    /*!
      \brief URG パラメータの取得

      \return URG パラメータ
    */
    virtual RangeSensorParameter parameter(void) const = 0;
  };
}

#endif /* !QRK_RANGE_SENSOR_H */
