#ifndef TYPE_UTILS_H
#define TYPE_UTILS_H

/*!
  \file
  \brief 型定義

  \author Saotufmi KAMIMURA

  $Id: typeUtils.h 528 2009-01-30 12:40:15Z satofumi $
*/

#include <math.h>
#include "math_util.h"


/*!
  \brief ビーゴ用の名前空間
*/
namespace VXV {
  /*!
    \brief 位置揃えの定数
  */
  enum Align {
    Top = 	0x01,
    Middle = 	0x02,
    Bottom = 	0x04,
    Left = 	0x08,
    Center = 	0x10,
    Right = 	0x20,
  };

  const double DEG2RAD = 2.0 * M_PI / 360.0; /*!< 積演算にて radian に変換 */
  const double RAD2DEG = 1.0 / DEG2RAD;	/*!< 積演算にて degree に変換 */

  /*!
    \brief 二次元位置
  */
  class Grid {
  public:
    int x;			/*!< X値 */
    int y;			/*!< Y値 */

    Grid(void) : x(0), y(0) {};

    /*!
      \brief 初期値指定のコンストラクタ

      \param x [i] X値
      \param y [i] Y値
    */
    Grid(int x, int y) : x(x), y(y) {};

    /*!
      \brief コピーコンストラクタ
    */
    Grid(const Grid* rhs) : x(rhs->x), y(rhs->y) {}

    virtual ~Grid(void) {}

    /*!
      \brief 演算子定義
    */
    const Grid& operator+=(const Grid& rhs);

    /*!
      \brief 演算子定義
    */
    const Grid operator+(const Grid& rhs) const {
      return Grid(this) += rhs;
    }

    /*!
      \brief 演算子定義
    */
    const Grid& operator-=(const Grid& rhs);

    /*!
      \brief 演算子定義
    */
    Grid operator-(const Grid& rhs) const {
      return Grid(this) -= rhs;
    }

    /*!
      \brief 演算子定義
    */
    bool operator==(const Grid& rhs) const {
      return (this->x == rhs.x) && (this->y == rhs.y);
    }
  };


  /*!
    \brief 三次元位置
  */
  class Grid3D : public Grid {
  public:
    int z;			/*!< Z値 */

    Grid3D(void) : z(0) {};

    /*!
      \brief 初期値指定のコンストラクタ

      \param x [i] X値
      \param y [i] Y値
      \param z [i] Z値
    */
    Grid3D(int x, int y, int z) : Grid(x, y), z(z) {};

    /*!
      \brief 初期値指定のコンストラクタ

      \param x [i] X値
      \param y [i] Y値
    */
    Grid3D(int x, int y) : Grid(x, y), z(0) {};

    /*!
      \brief コピーコンストラクタ
    */
    Grid3D(const Grid3D* rhs) : Grid(rhs->x, rhs->y), z(rhs->z) {}

    /*!
      \brief コピーコンストラクタ
    */
    Grid3D(const Grid& rhs) : Grid(rhs.x, rhs.y), z(0) {}

    virtual ~Grid3D(void) {}

    /*!
      \brief 演算子定義
    */
    const Grid3D& operator+=(const Grid3D& rhs);

    /*!
      \brief 演算子定義
    */
    const Grid3D operator+(const Grid3D& rhs) const {
      return Grid3D(this) += rhs;
    }

    /*!
      \brief 演算子定義
    */
    const Grid3D& operator-=(const Grid3D& rhs);

    /*!
      \brief 演算子定義
    */
    Grid3D operator-(const Grid3D& rhs) const {
      return Grid3D(this) -= rhs;
    }
  };


  /*!
    \brief 向き
  */
  class Direction {
    double inner_radian;
    Direction(const double radian) : inner_radian(radian) {}

  public:
    Direction(void) : inner_radian(0.0) {}

    /*!
      \brief 内部管理の radian 値を返す

      \retval 内部管理の radian 値
    */
    double getInnerRadian(void) const {
      return inner_radian;
    }

    /*!
      \brief コピーコンストラクタ
    */
    Direction(const Direction* rhs) : inner_radian(rhs->inner_radian) {}

    virtual ~Direction(void) {}

    /*!
      \brief 演算子定義
    */
    const Direction& operator+=(const Direction& rhs) {
      inner_radian += rhs.inner_radian;
      return *this;
    }

    /*!
      \brief 演算子定義
    */
    const Direction operator+(const Direction& rhs) const {
      return Direction(this) += rhs;
    }

    /*!
      \brief 演算子定義
    */
    bool operator==(const Direction& rhs) const {
      return (this->to_rad() == rhs.to_rad());
    }

    /*!
      \brief 演算子定義
    */
    const Direction& operator-=(const Direction& rhs) {
      inner_radian -= rhs.inner_radian;
      return *this;
    }

    /*!
      \brief 演算子定義
    */
    Direction operator-(const Direction& rhs) const {
      return Direction(this) -= rhs;
    }


    /*!
      \brief degree値の角度を格納

      \param degree [i] 角度値 [degree]
      \retval Directionオブジェクト
    */
    static Direction deg(int degree) {
      return Direction(2.0 * M_PI * degree / 360.0);
    }

    /*!
      \brief radian値の角度を格納

      \param radian [i] 角度値 [radian]
      \retval Directionオブジェクト
    */
    static Direction rad(double radian) {
      return Direction(radian);
    }

    /*!
      \brief degree単位の値を返す

      返される値は、[+180, -180) となる

      \retval 角度値 [degree]
    */
    int to_deg(void) const;

    /*!
      \brief radian単位の値を返す

      返される値は、[+M_PI, -M_PI) となる

      \retval 角度値 [radian]
    */
    double to_rad(void) const;
  };


  /*!
    \brief 位置と矩形サイズ
  */
  class Rect : public Grid {
  public:
    int w;			/*!< 幅 */
    int h;			/*!< 高さ */

    Rect(void) : Grid(0, 0), w(0), h(0) {}

    /*!
      \brief 初期値指定のコンストラクタ
    */
    Rect(int w, int h) : Grid(0, 0), w(w), h(h) {}

    /*!
      \brief 初期値指定のコンストラクタ
    */
    Rect(int x, int y, int w, int h) : VXV::Grid(x, y), w(w), h(h) {}

    virtual ~Rect(void) {}
  };


  /*!
    \brief 位置と向き
  */
  class Position : public Grid {
  public:
    Direction zt;			/*!< 向き */

    Position(void) {}

    /*!
      \brief 初期値指定のコンストラクタ

      \param x [i] X値
      \param y [i] Y値
      \param zt [i] 向きオブジェクト
    */
    Position(int x, int y, const Direction& zt) : Grid(x, y), zt(zt) {}

    /*!
      \brief コピーコンストラクタ
    */
    Position(const Position* rhs)
      : Grid(rhs->x, rhs->y), zt(rhs->zt) {}

    /*!
      \brief 演算子定義
    */
    bool operator==(const Position& rhs) const {
      return (this->x == rhs.x) && (this->y == rhs.y) && (this->zt == rhs.zt);
    }

    /*!
      \brief 演算子定義
    */
    const Position& operator+=(const Position& rhs);

    /*!
      \brief 演算子定義
    */
    const Position operator+(const Position& rhs) const {
      return Position(this) += rhs;
    }

    /*!
      \brief 演算子定義
    */
    const Position& operator-=(const Position& rhs);

    /*!
      \brief 演算子定義
    */
    const Position operator-(const Position& rhs) const {
      return Position(this) -= rhs;
    }

    virtual ~Position(void) {}
  };


  /*!
    \brief 三次元の位置と向き
  */
  class Position3D : public Position {
  public:
    int z;			/*!< Z値 */
    Direction xt;		/*!< 向き */
    Direction yt;		/*!< 向き */

    Position3D(void) : z(0), xt(Direction()), yt(Direction()) {}

    /*!
      初期値指定のコンストラクタ

      \param x [i] X値
      \param y [i] Y値
      \param z [i] Z値
      \param xt [i] 向きオブジェクト
      \param yt [i] 向きオブジェクト
      \param zt [i] 向きオブジェクト
    */
    Position3D(int x, int y, int z, Direction xt, Direction yt, Direction zt)
      : Position(x, y, zt), z(z), xt(xt), yt(yt) {}

    /*!
      \brief コピーコンストラクタ
    */
    Position3D(const Position3D* rhs)
      : Position(rhs->x, rhs->y, rhs->zt),
	z(rhs->z), xt(rhs->xt), yt(rhs->yt) {}

    /*!
      \brief 代入コンストラクタ
    */
    Position3D(const Position& rhs)
      : Position(rhs),
	z(0), xt(Direction()), yt(Direction()) {}

    /*!
      \brief 演算子定義
    */
    bool operator==(const Position3D& rhs) const {
      return (this->x == rhs.x) && (this->y == rhs.y) && (this->z == rhs.z) &&
	(this->xt == rhs.xt) && (this->yt == rhs.yt) && (this->zt == rhs.zt);
    }

    /*!
      \brief 演算子定義
    */
    const Position3D& operator+=(const Position3D& rhs);

    /*!
      \brief 演算子定義
    */
    const Position3D operator+(const Position3D& rhs) const {
      return Position3D(this) += rhs;
    }

    /*!
      \brief 演算子定義
    */
    const Position3D& operator-=(const Position3D& rhs);

    /*!
      \brief 演算子定義
    */
    const Position3D operator-(const Position3D& rhs) const {
      return Position3D(this) -= rhs;
    }

    virtual ~Position3D(void) {}
  };

  /*!
    \brief 原点との距離を計算する

    \param position [i] 位置
    \retval 距離 [mm]
  */
  extern int length(const Grid3D& position);


  /*!
    \brief 変換行列
  */
  typedef struct {
    double a11;			/*!< 行列要素 */
    double a21;			/*!< 行列要素 */
    double a31;			/*!< 行列要素 */
    double a12;			/*!< 行列要素 */
    double a22;			/*!< 行列要素 */
    double a32;			/*!< 行列要素 */
    double a13;			/*!< 行列要素 */
    double a23;			/*!< 行列要素 */
    double a33;			/*!< 行列要素 */

    int a41;			/*!< 行列要素 */
    int a42;			/*!< 行列要素 */
    int a43;			/*!< 行列要素 */
    int a44;			/*!< 行列要素 */
  } Matrix4D;

  /*!
    \brief 変換行列の作成

    \param pos [i] 変換情報を格納した情報
    \retval 変換行列
  */
  extern Matrix4D createConvertMatrix(const Position3D pos);

  /*!
    \brief 位置変換

    \param converted [o] 変換結果
    \param input [i] 変換元
    \param matrix [i] 変換行列
  */
  extern void convert(Position3D& converted, const Position3D& input,
		      const Matrix4D& matrix);

  /*!
    \brief 変換し、角度情報を加算

    \param converted [o] 変換結果
    \param input [i] 変換元
    \param forMatrix [i] 変換行列
  */
  extern void convertWithAngle(Position3D& converted, const Position3D& input,
			       const Position3D& forMatrix);
}

#endif /* !TYPE_UTILS_H */
