#ifndef AVECTOR3_H
#define AVECTOR3_H
/********************************************************************
 * BaseGLIB   Andrzej Lukaszewski
 *
 * 3D Vectors
 *
 * CLASSES:
 *            AUnitVector3,  AVector3 
 ********************************************************************/
#include <ABase.H>

class AUnitVector3;

class AVector3 {  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public:
   AVector3();
   AVector3(const AVector3 &v);
   AVector3(const AUnitVector3 &v);
   AVector3(AReal x, AReal y, AReal z);

   const AVector3 &operator=(const AVector3 &v);

   AReal X() const { return V[0]; }
   AReal Y() const { return V[1]; }
   AReal Z() const { return V[2]; }
   AReal operator[](int n) const;
   operator AReal *() {return V;}
   operator const AReal *() const {return V;}
   
   AVector3 operator+=(const AVector3 &v);
   AVector3 operator-=(const AVector3 &v);

   const AVector3 &operator*=(AReal c);
   const AVector3 &operator/=(AReal c);

   AVector3 operator-() const;
   AReal Length() const;
   AReal LenSqr() const;

   friend  bool operator==(const AVector3 &lside, const AVector3 &rside);
   friend  AVector3 operator+(const AVector3 &lside, const AVector3 &rside);
   friend  AVector3 operator-(const AVector3 &lside, const AVector3 &rside);
   friend  AReal    operator*(const AVector3 &lside, const AVector3 &rside);//DOT
   friend  AVector3 operator^(const AVector3 &lside, const AVector3 &rside);//CROSS
   friend  AVector3 operator*(const AVector3 &lside, AReal f);
   friend  AVector3 operator/(const AVector3 &lside, AReal f);
   friend  AVector3 operator*(AReal t, const AVector3& rside);

private:
   AReal V[3];
};


class AUnitVector3 { //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public:

   AUnitVector3();
   AUnitVector3(const AVector3 &v);
   AUnitVector3(const AReal *v);
   AUnitVector3(AReal x, AReal y, AReal z);

   const AUnitVector3 &operator=(const AUnitVector3 &v);

   AReal X() const { return V[0]; }
   AReal Y() const { return V[1]; }
   AReal Z() const { return V[2]; }
   AReal operator[](int n) const;

   AUnitVector3 operator-() const;

private:
   AReal V[3];
   void normalize();
};


/********************************************************************
 * Implementations 
 ********************************************************************/ 
inline AVector3::AVector3() {
}

inline AVector3::AVector3(const AVector3 &v) {
   V[0] = v.V[0];
   V[1] = v.V[1];
   V[2] = v.V[2];
}

inline AVector3::AVector3(const AUnitVector3 &v) {
   V[0] = v.X();
   V[1] = v.Y();
   V[2] = v.Z();
}

inline AVector3::AVector3(AReal x, AReal y, AReal z) {
   V[0] = x;
   V[1] = y;
   V[2] = z;
}

inline const AVector3 &AVector3::operator=(const AVector3 &v) {
   V[0] = v.V[0];
   V[1] = v.V[1];
   V[2] = v.V[2];
   return *this;
}

inline AReal AVector3::operator[](int n) const {
   assert(n >= 0 && n <= 2);
   return V[n];
}

inline AVector3 AVector3::operator+=(const AVector3 &v) {
   V[0] += v.V[0];
   V[1] += v.V[1];
   V[2] += v.V[2];
   return *this;
}

inline AVector3 AVector3::operator-=(const AVector3 &v) {
   V[0] -= v.V[0];
   V[1] -= v.V[1];
   V[2] -= v.V[2];
   return *this;
}

inline const AVector3 &AVector3::operator*=(AReal c) {
   V[0] *= c;
   V[1] *= c;
   V[2] *= c;
   return *this;
}

inline const AVector3 &AVector3::operator/=(AReal c) {
   assert(c != 0);
   V[0] /= c;
   V[1] /= c;
   V[2] /= c;
   return *this;
}

inline AReal AVector3::Length() const {
   return sqrt(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
}

inline AReal AVector3::LenSqr() const {
   return V[0]*V[0] + V[1]*V[1] + V[2]*V[2];
}

inline AVector3 AVector3::operator-() const {
   return AVector3(-V[0], -V[1], -V[2]);
}

inline bool operator==(const AVector3 &lside, const AVector3 &rside) {
   return ( AClose(lside.V[0], rside.V[0]) &&
            AClose(lside.V[1], rside.V[1]) &&
            AClose(lside.V[2], rside.V[2]) );
}

inline AVector3 operator+(const AVector3 &lside, const AVector3 &rside) {
   return AVector3(lside.V[0]+rside.V[0], lside.V[1]+rside.V[1], lside.V[2]+rside.V[2]);
}

inline AVector3 operator-(const AVector3 &lside, const AVector3 &rside) {
   return AVector3(lside.V[0]-rside.V[0], lside.V[1]-rside.V[1], lside.V[2]-rside.V[2]);
}


inline AVector3 operator*(const AVector3 &lside, AReal n) {
   return AVector3(lside.V[0]*n, lside.V[1]*n, lside.V[2]*n);
}

inline AVector3 operator/(const AVector3 &lside, AReal n) {
   assert(n != 0);
   return AVector3(lside.V[0]/n, lside.V[1]/n, lside.V[2]/n);
}

inline AReal operator*(const AVector3 &lside, const AVector3 &rside) {
   return (lside.V[0] * rside.V[0] + lside.V[1] * rside.V[1] + lside.V[2] * rside.V[2]);
}

inline AVector3 operator^(const AVector3 &lside, const AVector3 &rside) {
   return AVector3(
             +(lside.V[1] * rside.V[2]  -  rside.V[1] * lside.V[2]),
             -(lside.V[0] * rside.V[2]  -  rside.V[0] * lside.V[2]),
             +(lside.V[0] * rside.V[1]  -  rside.V[0] * lside.V[1])
          );
}

inline AVector3 operator*(AReal t, const AVector3& rside) {
   return AVector3( t*rside.V[0], t*rside.V[1], t*rside.V[2] );
}



inline void AUnitVector3::normalize() {
   AReal invLength = 1 / sqrt( V[0]*V[0] + V[1]*V[1] + V[2]*V[2] );
   V[0] *= invLength;
   V[1] *= invLength;
   V[2] *= invLength;
}

inline AUnitVector3::AUnitVector3() {
}

inline AUnitVector3::AUnitVector3(const AVector3 &v) {
   V[0] = v.X();
   V[1] = v.Y();
   V[2] = v.Z();
   normalize();
}

inline AUnitVector3::AUnitVector3(const AReal *v) {
   V[0] = v[0];
   V[1] = v[1];
   V[2] = v[2];
   normalize();
}

inline AUnitVector3::AUnitVector3(AReal x, AReal y, AReal z) {
   V[0] = x;
   V[1] = y;
   V[2] = z;
   normalize();
}

inline const AUnitVector3 &AUnitVector3::operator=(const AUnitVector3 &v) {
   V[0] = v.V[0];
   V[1] = v.V[1];
   V[2] = v.V[2];
   return *this;
}

inline AReal AUnitVector3::operator[](int n) const {
   assert(n >= 0 && n <= 2);
   return V[n];
}

inline AUnitVector3 AUnitVector3::operator-() const {
   AUnitVector3 tmp;
   tmp.V[0] = -V[0];
   tmp.V[1] = -V[1];
   tmp.V[2] = -V[2];
   return tmp;
}

#endif /* AVECTOR3_H */
