/***************************************************************************
 *   Copyright (C) 2007 by A.J. Tavakoli                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef POINT3_H
#define POINT3_H

#include <iostream>
#include <cmath>
#include "Vector3.h"


namespace Math3D {


template<class Real>
class Point3 {
public:
  // components of vector
  Real m[3];

  inline Point3() { }
  inline Point3(const Point3 &v) { m[0] = v.m[0]; m[1] = v.m[1]; m[2] = v.m[2]; }
  inline Point3(Real x, Real y, Real z) { m[0] = x; m[1] = y; m[2] = z; }
  inline Point3(const Real *v) { m[0] = v[0]; m[1] = v[1]; m[2] = v[2]; }
  inline Point3(const Vector3<Real> &v) { m[0] = v[0]; m[1] = v[1]; m[2] = v[2]; }

   // overloaded operators
  inline Real & operator [] (int i);
  inline const Real & operator [] (int i) const;
  inline Point3 operator + (const Point3<Real> &v) const;
  inline Point3 operator + (const Vector3<Real> &v) const;
  inline Point3 operator - (const Vector3<Real> &v) const;
  inline const Point3 & operator += (const Vector3<Real> &v);
  inline Vector3<Real> operator - (const Point3 &v) const;  
  inline const Point3 & operator -= (const Vector3<Real> &v);
  inline Real operator * (const Point3 &v) const;
  inline Real operator * (const Vector3<Real> &v) const;
  inline Point3<Real> operator * (Real s) const;
  inline const Point3<Real> & operator = (const Vector3<Real> &v);

  inline void set(Real x, Real y, Real z);
};


template<class Real>
inline Real & Point3<Real>::operator [] (int i) {
  // no error checking done for the sake of speed
  return m[i];
}


template<class Real>
inline const Real & Point3<Real>::operator [] (int i) const {
  // no error checking done for the sake of speed
  return m[i];
}


template<class Real>
inline Point3<Real> Point3<Real>::operator + (const Point3<Real> &p) const {
  return Point3(m[0] + p.m[0], m[1] + p.m[1], m[2] + p.m[2]);
}


template<class Real>
inline Point3<Real> Point3<Real>::operator + (const Vector3<Real> &v) const {
   return Point3(m[0] + v.m[0], m[1] + v.m[1], m[2] + v.m[2]);
}


template<class Real>
inline Point3<Real> Point3<Real>::operator - (const Vector3<Real> &v) const {
   return Point3(m[0] - v.m[0], m[1] - v.m[1], m[2] - v.m[2]);
}


// adds a vector to point and returns resulting point
template<class Real>
inline const Point3<Real> & Point3<Real>::operator += (const Vector3<Real> &v) {
  m[0] += v.m[0];
  m[1] += v.m[1];
  m[2] += v.m[2];

  return (*this);
}


// subtracts two points and returns resulting vector
template<class Real>
inline Vector3<Real> Point3<Real>::operator - (const Point3<Real> &v) const {
  return Vector3<Real>(m[0] - v.m[0], m[1] - v.m[1], m[2] - v.m[2]);
}


// dot product, provided for convenience since so quite often points are
// interpreted as vectors.
template<class Real>
inline Real Point3<Real>::operator *(const Point3<Real> &v) const {
  return m[0]*v.m[0] + m[1]*v.m[1] + m[2]*v.m[2];
}


// dot product, provided for convenience since so quite often points are
// interpreted as vectors.
template<class Real>
inline Real Point3<Real>::operator *(const Vector3<Real> &v) const {
  return m[0]*v.m[0] + m[1]*v.m[1] + m[2]*v.m[2];
}


// same as above, but with point on right
template<class Real>
inline Real operator *(const Vector3<Real> &v, const Point3<Real> &p) {
  return p.m[0]*v.m[0] + p.m[1]*v.m[1] + p.m[2]*v.m[2];
}


// multiplies a point (scales) by a scalar
template<class Real>
inline Point3<Real> Point3<Real>::operator *(Real s) const {
  return Point3<Real>(m[0]*s, m[1]*s, m[2]*s);
}


// same as above, but with point on right
template<class Real>
inline Point3<Real> operator *(Real s, const Point3<Real> &p) {
  return Point3<Real>(p.m[0]*s, p.m[1]*s, p.m[2]*s);
}


// assigns point's coordinates to the values of vector's components
template<class Real>
inline const Point3<Real> & Point3<Real>::operator = (const Vector3<Real> &v) {
  m[0] = v[0];
  m[1] = v[1];
  m[2] = v[2];

  return (*this);
}


// assign the values of the vector's components to x, y, and z
template<class Real>
inline void Point3<Real>::set(Real x, Real y, Real z) {
  m[0] = x;
  m[1] = y;
  m[2] = z;
}


// overloaded stream insertion operator for Point3
template<class Real>
std::ostream& operator << (std::ostream &out, const Point3<Real> &p) {
  out << "<" << p.m[0] << ", " << p.m[1] << ", " << p.m[2] << ">";
  return out;
}


} // namespace Math3D


#endif
