/*
 * Copyright (C) 2012-2016 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/
/* Desc: External interfaces for Gazebo
 * Author: Nate Koenig
 * Date: 03 Apr 2007
 */

#include "gazebo/math/Pose.hh"

using namespace gazebo;
using namespace math;

const Pose Pose::Zero = math::Pose(0, 0, 0, 0, 0, 0);

//////////////////////////////////////////////////
Pose::Pose()
  : pos(0, 0, 0), rot(1, 0, 0, 0)
{
}

//////////////////////////////////////////////////
Pose::Pose(const Vector3 &_pos, const Quaternion &_rot)
  : pos(_pos), rot(_rot)
{
}

//////////////////////////////////////////////////
Pose::Pose(double _x, double _y, double _z,
           double _roll, double _pitch, double _yaw)
: pos(_x, _y, _z), rot(_roll, _pitch, _yaw)
{
}

//////////////////////////////////////////////////
Pose::Pose(const Pose &_pose)
  : pos(_pose.pos), rot(_pose.rot)
{
}

//////////////////////////////////////////////////
Pose::Pose(const ignition::math::Pose3d &_pose)
  : pos(_pose.Pos()), rot(_pose.Rot())
{
}

//////////////////////////////////////////////////
Pose::~Pose()
{
}

//////////////////////////////////////////////////
void Pose::Set(const Vector3 &_pos, const Quaternion &_rot)
{
  this->pos = _pos;
  this->rot = _rot;
}

//////////////////////////////////////////////////
void Pose::Set(const Vector3 &_pos, const Vector3 &_rpy)
{
  this->pos = _pos;
  this->rot.SetFromEuler(_rpy);
}

//////////////////////////////////////////////////
void Pose::Set(double _x, double _y, double _z,
               double _roll, double _pitch, double _yaw)
{
  this->pos.Set(_x, _y, _z);
  this->rot.SetFromEuler(math::Vector3(_roll, _pitch, _yaw));
}

//////////////////////////////////////////////////
bool Pose::IsFinite() const
{
  return this->pos.IsFinite() && this->rot.IsFinite();
}

//////////////////////////////////////////////////
Pose Pose::GetInverse() const
{
  Quaternion inv = this->rot.GetInverse();
  return Pose(inv * (this->pos*-1), inv);
}

//////////////////////////////////////////////////
Pose Pose::operator+(const Pose &_obj) const
{
  Pose result;

  result.pos = this->CoordPositionAdd(_obj);
  result.rot = this->CoordRotationAdd(_obj.rot);

  return result;
}

//////////////////////////////////////////////////
const Pose &Pose::operator+=(const Pose &obj)
{
  this->pos = this->CoordPositionAdd(obj);
  this->rot = this->CoordRotationAdd(obj.rot);

  return *this;
}

//////////////////////////////////////////////////
const Pose &Pose::operator-=(const Pose &_obj)
{
  this->pos = this->CoordPositionSub(_obj);
  this->rot = this->CoordRotationSub(_obj.rot);

  return *this;
}

//////////////////////////////////////////////////
bool Pose::operator ==(const Pose &_pose) const
{
  return this->pos == _pose.pos && this->rot == _pose.rot;
}

//////////////////////////////////////////////////
bool Pose::operator!=(const Pose &_pose) const
{
  return this->pos != _pose.pos || this->rot != _pose.rot;
}

//////////////////////////////////////////////////
Pose Pose::operator*(const Pose &pose)
{
  return Pose(this->CoordPositionAdd(pose),  pose.rot * this->rot);
}

//////////////////////////////////////////////////
Pose &Pose::operator=(const Pose &_pose)
{
  this->pos = _pose.pos;
  this->rot = _pose.rot;
  return *this;
}

//////////////////////////////////////////////////
Pose &Pose::operator=(const ignition::math::Pose3d &_pose)
{
  this->pos = _pose.Pos();
  this->rot = _pose.Rot();
  return *this;
}

//////////////////////////////////////////////////
Vector3 Pose::CoordPositionAdd(const Vector3 &_pos) const
{
  Quaternion tmp;
  Vector3 result;

  // result = pose.rot + pose.rot * this->_pos * pose.rot!
  tmp.w = 0.0;
  tmp.x = _pos.x;
  tmp.y = _pos.y;
  tmp.z = _pos.z;

  tmp = this->rot * (tmp * this->rot.GetInverse());
  result.x = this->pos.x + tmp.x;
  result.y = this->pos.y + tmp.y;
  result.z = this->pos.z + tmp.z;

  return result;
}

//////////////////////////////////////////////////
Vector3 Pose::CoordPositionAdd(const Pose &_pose) const
{
  Quaternion tmp;
  Vector3 result;

  // result = _pose.rot + _pose.rot * this->pos * _pose.rot!
  tmp.w = 0.0;
  tmp.x = this->pos.x;
  tmp.y = this->pos.y;
  tmp.z = this->pos.z;

  tmp = _pose.rot * (tmp * _pose.rot.GetInverse());

  result.x = _pose.pos.x + tmp.x;
  result.y = _pose.pos.y + tmp.y;
  result.z = _pose.pos.z + tmp.z;

  return result;
}

//////////////////////////////////////////////////
Quaternion Pose::CoordRotationAdd(const Quaternion &_rot) const
{
  return Quaternion(_rot * this->rot);
}

//////////////////////////////////////////////////
void Pose::Reset()
{
  // set the position to zero
  this->pos.Set();
  this->rot.SetToIdentity();
}


//////////////////////////////////////////////////
Pose Pose::CoordPoseSolve(const Pose &_b) const
{
  Quaternion q;
  Pose a;

  a.rot = this->rot.GetInverse() * _b.rot;
  q = a.rot * Quaternion(0, this->pos.x, this->pos.y, this->pos.z);
  q = q * a.rot.GetInverse();
  a.pos = _b.pos - Vector3(q.x, q.y, q.z);

  return a;
}

//////////////////////////////////////////////////
Pose Pose::RotatePositionAboutOrigin(const Quaternion &_rot) const
{
  Pose a = *this;
  a.pos.x =  (1.0 - 2.0*_rot.y*_rot.y - 2.0*_rot.z*_rot.z) * this->pos.x
            +(2.0*(_rot.x*_rot.y+_rot.w*_rot.z)) * this->pos.y
            +(2.0*(_rot.x*_rot.z-_rot.w*_rot.y)) * this->pos.z;
  a.pos.y =  (2.0*(_rot.x*_rot.y-_rot.w*_rot.z)) * this->pos.x
            +(1.0 - 2.0*_rot.x*_rot.x - 2.0*_rot.z*_rot.z) * this->pos.y
            +(2.0*(_rot.y*_rot.z+_rot.w*_rot.x)) * this->pos.z;
  a.pos.z =  (2.0*(_rot.x*_rot.z+_rot.w*_rot.y)) * this->pos.x
            +(2.0*(_rot.y*_rot.z-_rot.w*_rot.x)) * this->pos.y
            +(1.0 - 2.0*_rot.x*_rot.x - 2.0*_rot.y*_rot.y) * this->pos.z;
  return a;
}

//////////////////////////////////////////////////
void Pose::Round(int _precision)
{
  this->rot.Round(_precision);
  this->pos.Round(_precision);
}

//////////////////////////////////////////////////
ignition::math::Pose3d Pose::Ign() const
{
  return ignition::math::Pose3d(this->pos.Ign(), this->rot.Ign());
}
