/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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 3 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, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#ifndef hsGGeometry3Defined
#define hsGGeometry3Defined

#include "hsTypes.h"
struct hsVector3;
struct hsPoint3;
struct hsScalarTriple;
class hsStream;

/*
    If value is already close to hsScalar1, then this is a good approx. of 1/sqrt(value)
*/
static inline hsScalar hsInvSqrt(hsScalar value)
{
    hsScalar    guess;
    hsScalar    threeOverTwo = hsScalar1 + hsScalarHalf;

    value = hsScalarDiv2(value);
    guess = threeOverTwo - value;       // with initial guess = 1.0

    // repeat this line for better approx
    guess = hsScalarMul(guess, threeOverTwo - hsScalarMul(hsScalarMul(value, guess), guess));
    guess = hsScalarMul(guess, threeOverTwo - hsScalarMul(hsScalarMul(value, guess), guess));

    return guess;
}

/////////////////////////////////////////////////////////////////////////////////////////////
struct hsScalarTriple
{
//protected:
//  hsScalarTriple() : fX(privateData[0]), fY(privateData[1]), fZ(privateData[2]) {}
//  hsScalarTriple(hsScalar x, hsScalar y, hsScalar z) 
//      : fX(privateData[0]), fY(privateData[1]), fZ(privateData[2]) { fX = x, fY = y, fZ = z; }
//  
//  union {
//      u_long128   privateTemp;
//      hsScalar    privateData[4];
//  };
//public:
//
//  int operator=(const hsScalarTriple& o) { privateTemp = o.privateTemp; }
//  hsScalarTriple(const hsScalarTriple& o) : fX(privateData[0]), fY(privateData[1]), fZ(privateData[2]) 
//              { *this = o; }
//
//  hsScalar& fX;
//  hsScalar& fY;
//  hsScalar& fZ;
protected:
    hsScalarTriple() {}
    hsScalarTriple(hsScalar x, hsScalar y, hsScalar z) : fX(x), fY(y), fZ(z) {}
public:
    hsScalar    fX, fY, fZ;

    hsScalarTriple*     Set(hsScalar x, hsScalar y, hsScalar z) { fX= x; fY = y; fZ = z;    return this;}
    hsScalarTriple*     Set(const hsScalarTriple *p) { fX = p->fX; fY = p->fY; fZ = p->fZ;  return this;}
    
    hsScalar InnerProduct(const hsScalarTriple &p) const;
    hsScalar InnerProduct(const hsScalarTriple *p) const;

//  hsScalarTriple LERP(hsScalarTriple &other, hsScalar t);
#if HS_SCALAR_IS_FIXED
     hsScalar       Magnitude() const;
     hsScalar       MagnitudeSquared() const;
#else
     hsScalar       Magnitude() const { return hsSquareRoot(MagnitudeSquared()); }
     hsScalar       MagnitudeSquared() const { return (fX * fX + fY * fY + fZ * fZ); }
#endif

     hsBool IsEmpty() const { return fX == 0 && fY == 0 && fZ == 0; }

    hsScalar    operator[](int i) const;
    hsScalar&   operator[](int i);
    
    void Read(hsStream *stream);
    void Write(hsStream *stream) const;

};


///////////////////////////////////////////////////////////////////////////
inline hsScalar& hsScalarTriple::operator[] (int i) 
{
    hsAssert(i >=0 && i <3, "Bad index for hsScalarTriple::operator[]");
     return *(&fX + i); 
}
inline hsScalar hsScalarTriple::operator[] (int i) const
{
    hsAssert(i >=0 && i <3, "Bad index for hsScalarTriple::operator[]");
     return *(&fX + i); 
}
inline hsScalar hsScalarTriple::InnerProduct(const hsScalarTriple &p) const
{
    hsScalar tmp = fX*p.fX;
    tmp += fY*p.fY;
    tmp += fZ*p.fZ;
    return tmp;
}
inline hsScalar hsScalarTriple::InnerProduct(const hsScalarTriple *p) const
{
    hsScalar tmp = fX*p->fX;
    tmp += fY*p->fY;
    tmp += fZ*p->fZ;
    return tmp;
}

//inline hsScalarTriple hsScalarTriple::LERP(hsScalarTriple &other, hsScalar t)
//{
//  hsScalarTriple p = other - this;
//  p = p / t;
//  return this + p;
//}



/////////////////////////////////////////////////////////////////////////////////////////////
struct hsPoint3  : public hsScalarTriple {  
    hsPoint3() {};
    hsPoint3(hsScalar x, hsScalar y, hsScalar z) : hsScalarTriple(x,y,z) {}
    explicit hsPoint3(const hsScalarTriple& p) : hsScalarTriple(p) {}
    
    hsPoint3*    Set(hsScalar x, hsScalar y, hsScalar z) { return (hsPoint3*)this->hsScalarTriple::Set(x,y,z);}
    hsPoint3*    Set(const hsScalarTriple* p) { return (hsPoint3*)this->hsScalarTriple::Set(p) ;}

    friend inline hsPoint3 operator+(const hsPoint3& s, const hsPoint3& t);
    friend inline hsPoint3 operator+(const hsPoint3& s, const hsVector3& t);
    friend inline hsPoint3 operator-(const hsPoint3& s, const hsPoint3& t);
    friend inline hsPoint3 operator-(const hsPoint3& s);
    friend inline hsPoint3 operator*(const hsScalar& s, const hsPoint3& t);
    friend inline hsPoint3 operator*(const hsPoint3& t, const hsScalar& s);
    friend inline hsPoint3 operator/(const hsPoint3& t, const hsScalar& s);
    hsBool operator==(const hsPoint3& ss) const
    {
            return (ss.fX == fX && ss.fY == fY && ss.fZ == fZ);
    }
    hsBool operator!=(const hsPoint3& ss) const { return !(*this == ss); }
    hsPoint3 &operator+=(const hsScalarTriple &s) { fX += s.fX; fY += s.fY; fZ += s.fZ; return *this; }
    hsPoint3 &operator*=(const hsScalar s) { fX *= s; fY *= s; fZ *= s; return *this; }
};


/////////////////////////////////////////////////////////////////////////////////////////////

struct hsVector3 : public hsScalarTriple {  

    hsVector3() {};
    hsVector3(hsScalar x, hsScalar y, hsScalar z) : hsScalarTriple(x,y,z) {}
    explicit hsVector3(const hsScalarTriple& p) : hsScalarTriple(p) { }
    hsVector3(const hsPoint3 *p1, const hsPoint3 *p2) { 
        fX = p1->fX - p2->fX, fY= p1->fY - p2->fY, fZ = p1->fZ - p2->fZ; }

    hsVector3*   Set(hsScalar x, hsScalar y, hsScalar z) { return (hsVector3*)hsScalarTriple::Set(x,y,z); }
    hsVector3*   Set(const hsScalarTriple* p) { return (hsVector3*)hsScalarTriple::Set(p) ;}
    hsVector3*   Set(const hsScalarTriple* p1, const hsScalarTriple* p2) { return Set(p1->fX-p2->fX,p1->fY-p2->fY,p1->fZ-p2->fZ);}

    void            Normalize()
                {
#if HS_BUILD_FOR_PS2
                    hsScalar    length = this->Magnitude();
                     hsIfDebugMessage(length == 0, "Err: Normalizing hsVector3 of length 0", 0);
                    if (length == 0)
                        return;
                    NormalizeVU0(length, (MATRIX4)this);
#else
                    hsScalar    length = this->Magnitude();
//                   hsIfDebugMessage(length == 0, "Err: Normalizing hsVector3 of length 0", 0);
                    if (length == 0)
                        return;
                    hsScalar    invMag = hsScalarInvert(length);
                    
                    fX = hsScalarMul(fX, invMag);
                    fY = hsScalarMul(fY, invMag);
                    fZ = hsScalarMul(fZ, invMag);
#endif
                }
    inline void     Renormalize()       // if the vector is already close to unit length
                {
                    hsScalar    mag2 = *this * *this;
                    hsIfDebugMessage(mag2 == 0, "Err: Renormalizing hsVector3 of length 0", 0);
                    if (mag2 == 0)
                        return;
                    hsScalar    invMag = hsInvSqrt(mag2);
                    
                    fX = hsScalarMul(fX, invMag);
                    fY = hsScalarMul(fY, invMag);
                    fZ = hsScalarMul(fZ, invMag);
                }

//  hsVector3 &Sub(const hsPoint3& s, const hsPoint3& t)
//  {   Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ); 
//          return *this; };
    friend inline hsVector3 operator+(const hsVector3& s, const hsVector3& t);
    friend inline hsVector3 operator-(const hsVector3& s, const hsVector3& t);
    friend inline hsVector3 operator-(const hsVector3& s);
    friend inline hsVector3 operator*(const hsScalar& s, const hsVector3& t);
    friend inline hsVector3 operator*(const hsVector3& t, const hsScalar& s);
    friend inline hsVector3 operator/(const hsVector3& t, const hsScalar& s);
    friend inline hsScalar operator*(const hsVector3& t, const hsVector3& s);
    friend  hsVector3 operator%(const hsVector3& t, const hsVector3& s);
#if 0 // Havok reeks
    friend hsBool32 operator==(const hsVector3& s, const hsVector3& t)
    {
            return (s.fX == t.fX && s.fY == t.fY && s.fZ == t.fZ);
    }
#else // Havok reeks
    hsBool operator==(const hsVector3& ss) const
    {
            return (ss.fX == fX && ss.fY == fY && ss.fZ == fZ);
    }
#endif // Havok reeks
    hsVector3 &operator+=(const hsScalarTriple &s) { fX += s.fX; fY += s.fY; fZ += s.fZ; return *this; }
    hsVector3 &operator-=(const hsScalarTriple &s) { fX -= s.fX; fY -= s.fY; fZ -= s.fZ; return *this; }
    hsVector3 &operator*=(const hsScalar s) { fX *= s; fY *= s; fZ *= s; return *this; }
    hsVector3 &operator/=(const hsScalar s) { fX /= s; fY /= s; fZ /= s; return *this; }
};

struct hsPoint4 {   
    hsScalar    fX, fY, fZ, fW;
    hsPoint4() {}
    hsPoint4(hsScalar x, hsScalar y, hsScalar z, hsScalar w) :  fX(x), fY(y), fZ(z), fW(w) {}
    hsScalar&       operator[](int i); 
    hsScalar        operator[](int i) const;

    hsPoint4& operator=(const hsPoint3&p) { Set(p.fX, p.fY, p.fZ, hsScalar1); return *this; }

    hsPoint4*   Set(hsScalar x, hsScalar y, hsScalar z, hsScalar w) 
        { fX = x; fY = y; fZ = z; fW = w; return this; }
};


inline hsVector3 operator+(const hsVector3& s, const hsVector3& t)
{
    hsVector3       result;
    
    return *result.Set(s.fX + t.fX, s.fY + t.fY, s.fZ + t.fZ);
}

inline hsVector3 operator-(const hsVector3& s, const hsVector3& t)
{
    hsVector3       result;
    
    return *result.Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ);
}

// unary minus
inline hsVector3 operator-(const hsVector3& s)
{
    hsVector3       result; 
    return *result.Set(-s.fX, -s.fY, -s.fZ);
}

inline hsVector3 operator*(const hsVector3& s, const hsScalar& t)
{
    hsVector3       result; 
    return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t));
}

inline hsVector3 operator/(const hsVector3& s, const hsScalar& t)
{
    hsVector3       result; 
    return *result.Set(hsScalarDiv(s.fX, t), hsScalarDiv(s.fY, t), hsScalarDiv(s.fZ, t));
}

inline hsVector3 operator*(const hsScalar& t, const hsVector3& s)
{
    hsVector3       result;
    
    return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t));
}

inline hsScalar operator*(const hsVector3& t, const hsVector3& s)
{
    return hsScalarMul(t.fX, s.fX) + hsScalarMul(t.fY, s.fY) + hsScalarMul(t.fZ, s.fZ);
}

////////////////////////////////////////////////////////////////////////////

inline hsPoint3 operator+(const hsPoint3& s, const hsPoint3& t)
{
    hsPoint3        result;
    
    return *result.Set(s.fX + t.fX, s.fY + t.fY, s.fZ + t.fZ);
}

inline hsPoint3 operator+(const hsPoint3& s, const hsVector3& t)
{
    hsPoint3        result;
    
    return *result.Set(s.fX + t.fX, s.fY + t.fY, s.fZ + t.fZ);
}

inline hsPoint3 operator-(const hsPoint3& s, const hsPoint3& t)
{
    hsPoint3        result;
    
    return *result.Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ);
}

// unary -
inline hsPoint3 operator-(const hsPoint3& s)
{
    hsPoint3        result;
    return *result.Set(-s.fX, -s.fY, -s.fZ);
}

inline hsPoint3 operator-(const hsPoint3& s, const hsVector3& t)
{
    hsPoint3        result;
    
    return *result.Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ);
}

inline hsPoint3 operator*(const hsPoint3& s, const hsScalar& t)
{
    hsPoint3        result; 
    return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t));
}

inline hsPoint3 operator/(const hsPoint3& s, const hsScalar& t)
{
    hsPoint3        result; 
    return *result.Set(hsScalarDiv(s.fX, t), hsScalarDiv(s.fY, t), hsScalarDiv(s.fZ, t));
}

inline hsPoint3 operator*(const hsScalar& t, const hsPoint3& s)
{
    hsPoint3        result;
    
    return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t));
}

inline hsScalar hsPoint4::operator[] (int i) const
{
    hsAssert(i >=0 && i <4, "Bad index for hsPoint4::operator[]");
     return *(&fX + i); 
}

inline hsScalar& hsPoint4::operator[] (int i)
{
    hsAssert(i >=0 && i <4, "Bad index for hsPoint4::operator[]");
     return *(&fX + i); 
}

typedef hsPoint3 hsGUv;



struct hsPointNorm {
    hsPoint3    fPos;
    hsVector3   fNorm;

    void Read(hsStream* s) { fPos.Read(s); fNorm.Read(s); }
    void Write(hsStream* s) const { fPos.Write(s); fNorm.Write(s); }
};


struct hsPlane3 {
    hsVector3 fN;
    hsScalar fD;

    hsPlane3() { }
    hsPlane3(const hsVector3* nrml, hsScalar d) 
    { fN = *nrml; fD=d; }
    hsPlane3(const hsPoint3* pt, const hsVector3* nrml)
    { fN = *nrml; fD = -pt->InnerProduct(nrml); }
    
    // create plane from a triangle (assumes clockwise winding of vertices)
    hsPlane3(const hsPoint3* pt1, const hsPoint3* pt2, const hsPoint3* pt3);

    hsVector3 GetNormal() const { return fN; }

    void Read(hsStream *stream);
    void Write(hsStream *stream) const;
};

#endif