/*!
  \file
  \brief カメラ視点の制御クラス

  \author Satofumi KAMIMURA

  $Id: CameraViewManager.cpp 1402 2009-10-11 12:45:16Z satofumi $
*/

#include "CameraViewManager.h"
#include "Angle.h"
#include <memory>

using namespace qrk;
using namespace std;


const double DefaultHeight = 5.0; //!< カメラ初期位置 [m]


struct CameraViewManager::pImpl
{
  bool updated_;

  double initial_scale_;
  double initial_x_;
  double initial_y_;
  double initial_z_;
  Angle initial_x_axis_;
  Angle initial_y_axis_;
  Angle initial_z_axis_;

  double base_x_;
  double base_y_;
  double base_z_;
  Angle base_x_axis_;
  Angle base_y_axis_;
  Angle base_z_axis_;

  double scale_;
  double x_;
  double y_;
  double z_;
  Angle x_axis_;
  Angle y_axis_;
  Angle z_axis_;

  pImpl(void)
    : updated_(true),
      initial_scale_(1.0),
      initial_x_(0.0), initial_y_(0.0), initial_z_(DefaultHeight),
      base_x_(0.0), base_y_(0.0), base_z_(0.0),
      scale_(1.0), x_(0.0), y_(0.0), z_(DefaultHeight)
  {
  }


  static pImpl* object(void)
  {
    static pImpl singleton_object;
    return &singleton_object;
  }
};


CameraViewManager::CameraViewManager(void) : pimpl(pImpl::object())
{
}


CameraViewManager::~CameraViewManager(void)
{
}


bool CameraViewManager::updated(void) const
{
  bool ret = pimpl->updated_;
  pimpl->updated_ = false;
  return ret;
}


void CameraViewManager::setInitialView(double x, double y, double z,
                                       const Angle& x_axis,
                                       const Angle& y_axis,
                                       const Angle& z_axis)
{
  pimpl->updated_ = true;

  pimpl->initial_x_ = x;
  pimpl->initial_y_ = y;
  pimpl->initial_z_ = z;

  pimpl->initial_x_axis_ = x_axis;
  pimpl->initial_y_axis_ = y_axis;
  pimpl->initial_z_axis_ = z_axis;

  initializeView();
}


void CameraViewManager::initializeView(void)
{
  pimpl->updated_ = true;

  pimpl->x_ = pimpl->initial_x_;
  pimpl->y_ = pimpl->initial_y_;
  pimpl->z_ = pimpl->initial_z_;

  pimpl->x_axis_ = pimpl->initial_x_axis_;
  pimpl->y_axis_ = pimpl->initial_y_axis_;
  pimpl->z_axis_ = pimpl->initial_z_axis_;
}


void CameraViewManager::setBaseView(double x, double y, double z,
                                    const Angle& x_axis,
                                    const Angle& y_axis,
                                    const Angle& z_axis)
{
  pimpl->updated_ = true;

  pimpl->base_x_ = x;
  pimpl->base_y_ = y;
  pimpl->base_z_ = z;

  pimpl->base_x_axis_ = x_axis;
  pimpl->base_y_axis_ = y_axis;
  pimpl->base_z_axis_ = z_axis;
}


void CameraViewManager::setView(double x, double y, double z,
                                const Angle& x_axis,
                                const Angle& y_axis,
                                const Angle& z_axis)
{
  pimpl->updated_ = true;

  pimpl->x_ = x;
  pimpl->y_ = y;
  pimpl->z_ = z;

  pimpl->x_axis_ = x_axis;
  pimpl->y_axis_ = y_axis;
  pimpl->z_axis_ = z_axis;
}


void CameraViewManager::view(double& x, double& y, double& z,
                             Angle& x_axis, Angle& y_axis, Angle& z_axis)
{
  x = pimpl->x_ + pimpl->base_x_;
  y = pimpl->y_ + pimpl->base_y_;
  z = pimpl->z_ + pimpl->base_z_;

  x_axis = pimpl->x_axis_ + pimpl->base_x_axis_;
  y_axis = pimpl->y_axis_ + pimpl->base_y_axis_;
  z_axis = pimpl->z_axis_ + pimpl->base_z_axis_;
}


void CameraViewManager::setScale(double scale)
{
  pimpl->updated_ = true;

  scale = min(max(scale, 0.005), 25.0);
  pimpl->scale_ = scale;
}


double CameraViewManager::scale(void) const
{
  return pimpl->scale_;
}
