/* Copyright (c) 2015  Gerald Knizia
 * 
 * This file is part of the IboView program (see: http://www.iboview.org)
 * 
 * IboView 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, version 3.
 * 
 * IboView 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 details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
 * 
 * Please see IboView documentation in README.txt for:
 * -- A list of included external software and their licenses. The included
 *    external software's copyright is not touched by this agreement.
 * -- Notes on re-distribution and contributions to/further development of
 *    the IboView software
 */

#ifndef CX_VEC3_H
#define CX_VEC3_H

#include <cstddef>
#include <cmath>

#define TVec3 TVector3<T>

namespace ct {

template<class T>
inline T sqr(T x) { return x*x; }

// that is only here to avoid external dependencies...
template<class T>
struct TVector3
{
   typedef T value_type;
   T m[3];

   TVector3() {};
   TVector3(T x_, T y_, T z_) {m[0] = x_; m[1] = y_; m[2] = z_;}
   explicit TVector3(T const *xyz) {m[0] = xyz[0]; m[1] = xyz[1]; m[2] = xyz[2];}
   template<class S> explicit TVector3(TVector3<S> const &other) {m[0] = static_cast<T>(other[0]); m[1] = static_cast<T>(other[1]); m[2] = static_cast<T>(other[2]);}


   void operator += (TVec3 const &other) {this->m[0] += other.m[0]; this->m[1] += other.m[1]; this->m[2] += other.m[2];}
   void operator -= (TVec3 const &other) {this->m[0] -= other.m[0]; this->m[1] -= other.m[1]; this->m[2] -= other.m[2];}
   void operator *= (T f) {this->m[0] *= f; this->m[1] *= f; this->m[2] *= f;}
   void operator /= (T f) {*this *= 1/f;}

//   T &operator[] (unsigned i) { return this->m[i]; }
//   T const &operator[] (unsigned i) const { return this->m[i]; }
//   T &operator[] (int i) { return this->m[i]; }
//   T const &operator[] (int i) const { return this->m[i]; }
//   T &operator[] (unsigned long i) { return this->m[i]; }
//   T const &operator[] (unsigned long i) const { return this->m[i]; }
//   T &operator[] (long i) { return this->m[i]; }
//   T const &operator[] (long i) const { return this->m[i]; }
// ^- in VC long is *also* 32bit, which means that there is no way of
// defining these functions in such a way that int, long, and size_t will work at the same time.
// (because size_t may also be typedef'd to one of those)
   T &operator[] (size_t i) { return this->m[i]; }
   T const &operator[] (size_t i) const { return this->m[i]; }

   operator T* () {return &this->m[0];}
   operator T const* () const {return &this->m[0];}

   T LengthSq() const { return m[0]*m[0] + m[1]*m[1] + m[2]*m[2]; }
   T Length() const { return std::sqrt(this->LengthSq()); }
   // normalize *this
   void Normalize() { (*this) *= 1./this->Length(); }
   // return normalized copy of *this
   TVec3 Normalized() const { TVec3 cp(*this); cp.Normalize(); return cp; }

   T const &x () const { return m[0]; }
   T const &y () const { return m[1]; }
   T const &z () const { return m[2]; }
   T &x () { return m[0]; }
   T &y () { return m[1]; }
   T &z () { return m[2]; }

   std::size_t size() const { return 3; }
   T *begin() { return &m[0]; }
   T const *begin() const { return &m[0]; }
   T *end() { return &m[size()]; }
   T const *end() const { return &m[size()]; }
};

template<class T> inline TVec3 operator + (TVec3 const &a, TVec3 const &b) { return TVec3(a[0]+b[0], a[1]+b[1], a[2]+b[2]); }
template<class T> inline TVec3 operator - (TVec3 const &a, TVec3 const &b) { return TVec3(a[0]-b[0], a[1]-b[1], a[2]-b[2]); }
template<class T> inline TVec3 operator - (TVec3 const &a) { return TVec3(-a[0], -a[1], -a[2]); }
template<class T> inline TVec3 operator * (T f, TVec3 const &b) { return TVec3(f*b[0], f*b[1], f*b[2]); }
template<class T> inline TVec3 operator * (TVec3 const &b, T f) { return TVec3(f*b[0], f*b[1], f*b[2]); }
template<class T> inline T Dot(TVec3 const &a, TVec3 const &b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
template<class T> inline T LengthSq(TVec3 const &a) { return Dot(a,a); }
template<class T> inline T Length(TVec3 const &a) { return std::sqrt(Dot(a,a)); }
template<class T> inline T DistSq(TVec3 const &a, TVec3 const &b) { return LengthSq(a-b); }
template<class T> inline T Dist(TVec3 const &a, TVec3 const &b) { return std::sqrt(DistSq(a,b)); }
template<class T> inline TVec3 Normalized(TVec3 const &a) { return (1./Length(a))*a; }


template<class T> inline T DistSq(T const a[3], T const b[3]) { return sqr(a[0]-b[0]) + sqr(a[1]-b[1]) + sqr(a[2]-b[2]); }
template<class T> inline T DistSq3(T const *a, T const *b) { return sqr(a[0]-b[0]) + sqr(a[1]-b[1]) + sqr(a[2]-b[2]); }
template<class T> inline T DistSq2(T const *a, T const *b) { return sqr(a[0]-b[0]) + sqr(a[1]-b[1]); }

template<class T>
static void Cross(TVector3<T> &Out, TVector3<T> const &a, TVector3<T> const &b)
{
   Out[0] = a[1]*b[2] - b[1] * a[2];
   Out[1] = a[2]*b[0] - b[2] * a[0];
   Out[2] = a[0]*b[1] - b[0] * a[1];
}

template<class T>
static TVector3<T> Cross(TVector3<T> const &a, TVector3<T> const &b)
{
   TVector3<T> Out;
   Cross(Out, a, b);
   return Out;
}


} // namespace ct

#undef TVec3

#endif // CX_VEC3_H
