/*
  座標系クラス
  Satofumi KAMIMURA
  $Id: coordinateCtrl.cpp 286 2008-10-20 09:40:22Z satofumi $
*/

#include "coordinateCtrl.h"
#include "objCoordinateCtrl.h"

using namespace VXV;

std::list<CoordinateCtrl*> CoordinateCtrl::nortify_crd;

CoordinateCtrl::CoordinateCtrl(void)
  : parent_crd(NULL), parent_offset(Position3D()), crd_ticks(0),
    crd_position(VXV::Position3D()) {
  sendCrdNortify(Create);
}


CoordinateCtrl::~CoordinateCtrl(void) {
  // 子座標系を親座標系に張り付ける
  while (!child_crd.empty()) {
    CoordinateCtrl*& p = child_crd.front();
    p->setOwnCrdToCrd(getParentCrd(), p->getCrdPosition(this));
  }
  if (parent_crd) {
    parent_crd->remove_crd(this);
  }
  unregisterCrdNortifyClient();
  sendCrdNortify(Remove);
}


void CoordinateCtrl::remove_crd(CoordinateCtrl* crd) {
  child_crd.remove(crd);
}


void CoordinateCtrl::registerCrdNortifyClient(void) {
  nortify_crd.push_back(this);
}


void CoordinateCtrl::unregisterCrdNortifyClient(void) {
  nortify_crd.remove(this);
}


void CoordinateCtrl::sendCrdNortify(int event) {
  for (std::list<CoordinateCtrl*>::iterator it = nortify_crd.begin();
       it != nortify_crd.end(); ++it) {
    (*it)->crdNortify(event, this);
  }
}


VXV::Position3D CoordinateCtrl::getCrdPosition(const CoordinateCtrl* crd,
					       const VXV::Position3D&
					       position) const {
  Position3D pos = position;
  for (const CoordinateCtrl* p = this; p != NULL; p = p->parent_crd) {
    if (p->isObjectCrdMode(this)) {
      convertWithAngle(pos, pos,
		       const_cast<CoordinateCtrl*>(p)->getLocalPosition());
    }
    convertWithAngle(pos, pos, p->parent_offset);
  }

  if (crd) {
    Position gl_pos = crd->getCrdPosition(NULL);
    pos -= gl_pos;

    Position3D base;
    base -= gl_pos;
    base.x = 0;
    base.y = 0;
    base.z = 0;
    VXV::convert(pos, pos, createConvertMatrix(base));
  }
  return pos;
}


CoordinateCtrl* CoordinateCtrl::getParentCrd(void) {
  return parent_crd;
}


CoordinateCtrl* CoordinateCtrl::getRootCrd(void) {
  if (this == NULL) {
    throw CrdCtrl_Exception("Invalid CoorinateCtrl object: in getRootCrd()");
  }

  CoordinateCtrl* p = this;
  while (p->parent_crd != NULL) {
    p = p->parent_crd;
  }
  return p;
}


void CoordinateCtrl::setOwnCrdToCrd(CoordinateCtrl* parent,
				    const VXV::Position3D& offset) {

  for (CoordinateCtrl* p = parent; p != NULL; p = p->parent_crd) {
    if (p == this) {
      // ループになる張り付け
      throw CrdCtrl_Exception("Loop: in setParentCrd()");
    }
  }

  if (parent_crd) {
    parent_crd->remove_crd(this);
  }

  parent_crd = parent;
  if (parent_crd) {
    parent->child_crd.push_back(this);
  }
  updateParentCrdOffset(offset);
}


VXV::Position3D CoordinateCtrl::getParentCrdOffset(void) {
  return parent_offset;
}


void CoordinateCtrl::updateParentCrdOffset(const VXV::Position3D& offset) {

  if (getParentCrd()) {
    parent_offset = offset;
  }
  sendCrdNortify(Update);
}


std::list<CoordinateCtrl*>& CoordinateCtrl::getChildCrd(void) {
  return child_crd;
}


void CoordinateCtrl::adjustCrdPosition(const VXV::Position3D& position,
				       const CoordinateCtrl* crd) {
  for (const CoordinateCtrl* p = crd; p != NULL; p = p->parent_crd) {
    if (p == this) {
      // 自分以下の座標系に対する操作
      throw CrdCtrl_Exception("Loop adjust : in adjustCrdPosition()");
    }
  }

  // 指定座標系の位置を取得し、差分を補正
  parent_offset = Position3D();
  Position3D actual = getCrdPosition(crd);
  parent_offset = position - actual;

  sendCrdNortify(Update);
}


void CoordinateCtrl::setOwnCrdToObject(ObjCoordinateCtrl* crd,
				       const VXV::Position3D& offset) {
  crd->registerChildAddress(this);
  setOwnCrdToCrd(crd, offset);
}
