/*!
  \file
  \brief Coordinate のテスト

  \author Satofumi KAMIMURA

  $Id: TestCoordinate.cpp 1117 2009-07-07 17:30:45Z satofumi $
*/

#include "TestCoordinate.h"
#include "Coordinate.h"

using namespace qrk;
using namespace std;


CPPUNIT_TEST_SUITE_REGISTRATION(TestCoordinate);
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TestCoordinate, "TestCoordinate");


// 座標系の初期化テスト
void TestCoordinate::initializeTest(void)
{
  // デフォルトの親がグローバル座標かのテスト
  Coordinate* global = Coordinate::global();
  Coordinate coordinate;
  Coordinate* parent = coordinate.parent();
  CPPUNIT_ASSERT_EQUAL(global, parent);

  // 子座標系が存在しないかのテスト
  set<Coordinate*> children = coordinate.children();
  CPPUNIT_ASSERT_EQUAL(true, children.empty());

  // 原点位置が親座標系の (0, 0, deg(0)) かをテスト
  Position<long> offset = global->offset(&coordinate);
  CPPUNIT_ASSERT_EQUAL(Position<long>(0, 0, deg(0)), offset);
}


void TestCoordinate::setOriginToTest(void)
{
  // 原点位置を設定し、取得できるかのテスト
  Coordinate* global = Coordinate::global();
  Coordinate coordinate;

  coordinate.setOriginTo(Position<long>(1, 2, deg(3)), global);
  Position<long> offset = global->offset(&coordinate);
  CPPUNIT_ASSERT_EQUAL(Position<long>(1, 2, deg(3)), offset);

  // 再設定できるかのテスト
  coordinate.setOriginTo(Position<long>(4, 5, deg(6)), global);
  offset = global->offset(&coordinate);
  CPPUNIT_ASSERT_EQUAL(Position<long>(4, 5, deg(6)), offset);
}


void TestCoordinate::treeTest(void)
{
  // ２階層のツリー構造を表現できるかのテスト
  Coordinate parent;
  Coordinate child;
  Coordinate grandchild;

  grandchild.setOriginTo(Position<long>(0, 0, deg(0)), &child);
  child.setOriginTo(Position<long>(0, 0, deg(0)), &parent);
  CPPUNIT_ASSERT_EQUAL(&child, grandchild.parent());
  CPPUNIT_ASSERT_EQUAL(&parent, child.parent());

  // 設定した子座標系を取得できるかのテスト
  set<Coordinate*> children = parent.children();
  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), children.size());
  CPPUNIT_ASSERT(children.find(&child) != children.end());

  // 複数の子座標系を設定して取得できるかのテスト
  Coordinate* child_2nd = new Coordinate;
  child_2nd->setOriginTo(Position<long>(0, 0, deg(0)), &parent);
  children = parent.children();
  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), children.size());
  CPPUNIT_ASSERT(children.find(child_2nd) != children.end());

  // 削除したときに、子座標系の登録も削除されるかのテスト
  delete child_2nd;
  children = parent.children();
  CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), children.size());
  CPPUNIT_ASSERT(children.find(&child) != children.end());

  // 親を再設定したときに、子座標系の登録が削除されるかのテスト
  grandchild.setOriginTo(Position<long>(0, 0, deg(0)), &parent);
  children = child.children();
  CPPUNIT_ASSERT_EQUAL(true, children.empty());
}


// 簡単な位置変換のテスト
void TestCoordinate::basicConvertTest(void)
{
  Coordinate* global = Coordinate::global();
  Coordinate coordinate;

  // (100, 100, 0) に配置した子の座標系の (100, 0, 90) の点の位置を親座標系から取得
  coordinate.setOriginTo(Position<long>(100, 100, deg(0)), global);
  Position<long> offset =
    global->pointPosition(Position<long>(100, 0, deg(90)), &coordinate);
  CPPUNIT_ASSERT_EQUAL(Position<long>(200, 100, deg(90)), offset);

  // (100, 100, 90) に配置した子の座標系の (100, 0, 90) の点の位置を親座標系から取得
  coordinate.setOriginTo(Position<long>(100, 100, deg(90)), global);
  offset = global->pointPosition(Position<long>(100, 0, deg(90)), &coordinate);
  CPPUNIT_ASSERT_EQUAL(Position<long>(100, 200, deg(180)), offset);

  // (100, 100, 0) に配置した子の座標系で、親座標系の (100, 0, 90) の点がどこかを取得
  coordinate.setOriginTo(Position<long>(100, 100, deg(0)), global);
  offset = coordinate.pointPosition(Position<long>(100, 0, deg(90)), global);
  CPPUNIT_ASSERT_EQUAL(Position<long>(0, -100, deg(90)), offset);

  // (100, 100, 90) に配置した子の座標系で、親座標系の (100, 0, 90) の点がどこかを取得
  coordinate.setOriginTo(Position<long>(100, 100, deg(90)), global);
  offset = coordinate.pointPosition(Position<long>(100, 0, deg(90)), global);
  CPPUNIT_ASSERT_EQUAL(Position<long>(-100, 0, deg(0)), offset);
}


void TestCoordinate::pointPositionTest(void)
{
  // １階層はなれた座標系位置の計算テスト
  Coordinate* global = Coordinate::global();
  Coordinate coordinate_1st;
  coordinate_1st.setOriginTo(Position<long>(10, 100, deg(90)), global);
  Position<long> offset =
    coordinate_1st.pointPosition(Position<long>(20, 30, deg(180)), global);
  CPPUNIT_ASSERT_EQUAL(Position<long>(-70, -10, deg(90)), offset);

  // ２階層はなれた座標系位置の計算テスト
  Coordinate coordinate_2nd;
  coordinate_2nd.setOriginTo(Position<long>(20, 200, deg(90)), &coordinate_1st);
  offset = coordinate_2nd.pointPosition(Position<long>(30, 40, deg(270)),
                                        global);
  CPPUNIT_ASSERT_EQUAL(Position<long>(-220, 80, deg(90)), offset);

  // グローバル座標系をまたいだ座標系での計算テスト
  Coordinate coordinate_3rd;
  coordinate_3rd.setOriginTo(Position<long>(-10, -20, deg(-90)), global);
  offset = coordinate_3rd.pointPosition(Position<long>(0, 110, deg(90)),
                                        &coordinate_2nd);
  CPPUNIT_ASSERT_EQUAL(Position<long>(-30, -180, deg(360)), offset);
}
