// @(#)root/gl:$Name:  $:$Id: TGLUtil.h,v 1.7 2005/06/15 10:22:57 brun Exp $
// Author:  Richard Maunder  25/05/2005

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_TGLUtil
#define ROOT_TGLUtil

#ifndef ROOT_Rtypes
#include "Rtypes.h"
#endif

#include <math.h>
#include <assert.h>

/*************************************************************************
 * TGLUtil - A collection of utility classes for GL. Vertex, vector, rect
 * matrix etc. These provide const and non-const accessors Arr() / CArr() to
 * a GL compatible internal field. This means they can be used directly
 * with OpenGL C API calls.
 *
 * They are not intended to be fully featured - just provide minimum
 * required.
 *
 * Also other various common types, error checking etc.
 *
 *************************************************************************/

// TODO: Where should these enums live?
enum  ELODPresets {
   kLow = 20,
   kMed = 50,
   kHigh = 100
};

enum EPosition
{
   kInFront = 0,
   kBehind
};

enum EOverlap
{
   kInside = 0,
   kPartial,
   kOutside
};

// TODO: Namespace
// TODO: Mark all headers as INTERNAL

/*************************************************************************
 * TGLVertex3 - TODO
 *
 *
 *
 *************************************************************************/

class TGLVector3; // Forward declare for Shift()

class TGLVertex3
{
protected:
   // Fields
   Bool_t ValidIndex(UInt_t index) const { return (index < 3); }
   Double_t fVals[3];

public:
   TGLVertex3();
   TGLVertex3(Double_t x, Double_t y, Double_t z);
   TGLVertex3(const TGLVertex3 & other);
   virtual ~TGLVertex3();

   TGLVertex3 & operator=(const TGLVertex3 & rhs) { Set(rhs); return *this; }
   const TGLVertex3 & operator - ()
   { fVals[0] = -fVals[0]; fVals[1] = -fVals[1]; fVals[2] = -fVals[2]; return *this; }

   void Fill(Double_t val) { Set(val,val,val); }
   void Set(Double_t x, Double_t y, Double_t z) { fVals[0]=x; fVals[1]=y; fVals[2]=z; }
   void Set(const TGLVertex3 & other) { fVals[0]=other.fVals[0]; fVals[1]=other.fVals[1]; fVals[2]=other.fVals[2]; }
   void Shift(TGLVector3 & shift);
   void Shift(Double_t xDelta, Double_t yDelta, Double_t zDelta);

   // Accessors
   Double_t & operator [] (Int_t index)
   { if (!ValidIndex(index)) { assert(kFALSE); return fVals[0]; } else { return fVals[index]; } }
   const Double_t& operator [] (Int_t index) const
   { if (!ValidIndex(index)) { assert(kFALSE); return fVals[0]; } else { return fVals[index]; } }
   Double_t   X() const { return fVals[0]; }
   Double_t & X()       { return fVals[0]; }
   Double_t   Y() const { return fVals[1]; }
   Double_t & Y()       { return fVals[1]; }
   Double_t   Z() const { return fVals[2]; }
   Double_t & Z()       { return fVals[2]; }

   const Double_t * CArr() const { return fVals; }
   Double_t * Arr() { return fVals; }

   void Dump() const;

   ClassDef(TGLVertex3,0) // GL 3 component vertex helper/wrapper class
};

/*************************************************************************
 * TGLVector3 - TODO
 *
 *
 *
 *************************************************************************/
class TGLVector3 : public TGLVertex3
{
public:
   TGLVector3();
   TGLVector3(Double_t x, Double_t y, Double_t z);
   TGLVector3(const TGLVector3 & other);
   virtual ~TGLVector3();

   const TGLVector3 & operator -= (const TGLVector3 & val)
   { fVals[0] -= val[0]; fVals[1] -= val[1]; fVals[2] -= val[2]; return *this; }
   const TGLVector3 & operator /= (Double_t val)
   { fVals[0] /= val; fVals[1] /= val; fVals[2] /= val; return *this; }
   const TGLVector3 & operator *= (Double_t val)
   { fVals[0] *= val; fVals[1] *= val; fVals[2] *= val; return *this; }

   inline Double_t Mag() const;
   inline void Normalise();

   ClassDef(TGLVector3,0) // GL 3 component vector helper/wrapper class
};

//______________________________________________________________________________
inline Double_t TGLVector3::Mag() const
{
   return sqrt(fVals[0]*fVals[0] + fVals[1]*fVals[1] + fVals[2]*fVals[2]);
}

//______________________________________________________________________________
inline void TGLVector3::Normalise()
{
   Double_t mag = Mag();
   if ( mag == 0.0 ) {
      assert( kFALSE );
      return;
   }
   fVals[0] /= mag;
   fVals[1] /= mag;
   fVals[2] /= mag;
}

//______________________________________________________________________________
inline Double_t Dot(const TGLVector3 & v1, const TGLVector3 & v2)
{
  return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}

//______________________________________________________________________________
inline TGLVector3 Cross(const TGLVector3 & v1, const TGLVector3 & v2)
{
    return TGLVector3(v1[1]*v2[2] - v2[1]*v1[2],
                      v1[2]*v2[0] - v2[2]*v1[0],
                      v1[0]*v2[1] - v2[0]*v1[1]);
}

//______________________________________________________________________________
inline const TGLVector3 operator / (const TGLVector3 & vec, Double_t val)
{
   return TGLVector3(vec[0] / val, vec[1] / val, vec[2] / val);
}

//______________________________________________________________________________
inline const TGLVector3 operator * (const TGLVector3 & vec, Double_t val)
{
   return TGLVector3(vec[0] * val, vec[1] * val, vec[2] * val);
}

//______________________________________________________________________________
// Vertex + Vector => Vertex
inline TGLVertex3 operator + (const TGLVertex3 & vertex, const TGLVector3 & vec)
{
   return TGLVertex3(vertex[0] + vec[0], vertex[1] + vec[1], vertex[2] + vec[2]);
}

//______________________________________________________________________________
// Vertex - Vertex => Vector
inline TGLVector3 operator - (const TGLVertex3 & vertex, const TGLVertex3 & vec)
{
   return TGLVector3(vertex[0] - vec[0], vertex[1] - vec[1], vertex[2] - vec[2]);
}

//______________________________________________________________________________
// Vector + Vector => Vector
inline TGLVector3 operator + (const TGLVector3 & first, const TGLVector3 & second)
{
   return TGLVector3(first[0] + second[0], first[1] + second[1], first[2] + second[2]);
}

//______________________________________________________________________________
// Vector - Vector => Vector
inline TGLVector3 operator - (const TGLVector3 & first, const TGLVector3 & second)
{
   return TGLVector3(first[0] - second[0], first[1] - second[1], first[2] - second[2]);
}

/*************************************************************************
 * TGLRect - TODO
 *
 *
 *
 *************************************************************************/
class TGLRect
{
private:
   // Fields
   Int_t    fX, fY;           //! Corner
   UInt_t   fWidth, fHeight;  //! Positive width/height

public:
   TGLRect();
   TGLRect(Int_t x, Int_t y, UInt_t width, UInt_t height);
   virtual ~TGLRect(); // ClassDef introduces virtual fns

   // Bitwise copy const & =op are ok at present
   inline void Set(Int_t x, Int_t y, UInt_t width, UInt_t height);
   inline void SetCorner(Int_t x, Int_t y);
   inline void Offset(Int_t dX, Int_t dY);
   void Expand(Int_t x, Int_t y);

   Int_t    X()       const { return fX; }
   Int_t &  X()             { return fX; }
   Int_t    Y()       const { return fY; }
   Int_t &  Y()             { return fY; }
   UInt_t   Width()   const { return fWidth; }
   UInt_t & Width()         { return fWidth; }
   UInt_t   Height()  const { return fHeight; }
   UInt_t & Height()        { return fHeight; }
   Int_t    CenterX() const { return fX + fWidth/2; }
   Int_t    CenterY() const { return fY + fHeight/2; }
   Int_t    Left()    const { return fX; }
   Int_t    Right()   const { return fX + fWidth; }
   Int_t    Top()     const { return fY; }
   Int_t    Bottom()  const { return fY + fHeight; }

   UInt_t Diagonal() const { return static_cast<UInt_t>(sqrt(static_cast<Double_t>(fWidth*fWidth + fHeight*fHeight))); }
   UInt_t Longest() const { return fWidth > fHeight ? fWidth:fHeight; }

   Double_t Aspect() const;
   EOverlap Overlap(const TGLRect & other) const;

   // TODO: Change fields
   //const Double_t * CArr() const { return fVals; }
   //Double_t * Arr() { return fVals; }

   ClassDef(TGLRect,0) // GL rect helper/wrapper class
};

//______________________________________________________________________________
inline void TGLRect::Set(Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   fX = x;
   fY = y;
   fWidth = width;
   fHeight = height;
}

//______________________________________________________________________________
inline void TGLRect::SetCorner(Int_t x, Int_t y)
{
   fX = x;
   fY = y;
}

//______________________________________________________________________________
inline void TGLRect::Offset(Int_t dX, Int_t dY)
{
   fX += dX;
   fY += dY;
}

/*************************************************************************
 * TGLPlane - TODO
 *
 *
 *
 *************************************************************************/
class TGLPlane
{
private:
   // Fields
   Double_t fVals[4];

   // Methods
   void Normalise();

public:
   TGLPlane();
   TGLPlane(Double_t a, Double_t b, Double_t c, Double_t d, Bool_t norm = kTRUE);
   TGLPlane(Double_t eq[4], Bool_t norm = kTRUE);
   virtual ~TGLPlane(); // ClassDef introduces virtual fns

   //inline const TGLPlane& operator *= (Double_t val);
   inline void Set(Double_t a, Double_t b, Double_t c, Double_t d, Bool_t norm = kTRUE);
   inline void Set(Double_t eq[4], Bool_t norm = kTRUE);

   Double_t A() const { return fVals[0]; }
   Double_t B() const { return fVals[1]; }
   Double_t C() const { return fVals[2]; }
   Double_t D() const { return fVals[3]; }

   TGLVector3 Norm() const { return TGLVector3( fVals[0], fVals[1], fVals[2]); }
   inline Double_t DistanceTo(const TGLVertex3 & vertex) const;
   inline TGLVertex3 NearestOn(const TGLVertex3 & point) const;

   const Double_t * CArr() const { return fVals; }
   Double_t * Arr() { return fVals; }

   ClassDef(TGLPlane,0) // GL plane helper/wrapper class
};

//______________________________________________________________________________
inline void TGLPlane::Set(Double_t a, Double_t b, Double_t c, Double_t d, Bool_t norm)
{
   fVals[0] = a;
   fVals[1] = b;
   fVals[2] = c;
   fVals[3] = d;
   if (norm) {
      Normalise();
   }
}

//______________________________________________________________________________
inline void TGLPlane::Set(Double_t eq[4], Bool_t norm)
{
   fVals[0] = eq[0];
   fVals[1] = eq[1];
   fVals[2] = eq[2];
   fVals[3] = eq[3];
   if (norm) {
      Normalise();
   }
}

//______________________________________________________________________________
inline Double_t TGLPlane::DistanceTo(const TGLVertex3 & vertex) const
{
   return (fVals[0]*vertex[0] + fVals[1]*vertex[1] + fVals[2]*vertex[2] + fVals[3]);
}

inline TGLVertex3 TGLPlane::NearestOn(const TGLVertex3 & point) const
{
   TGLVector3 o = Norm() * (Dot(Norm(), TGLVector3(point[0], point[1], point[2])) + D() / Dot(Norm(), Norm()));
   TGLVertex3 v = point - o;
   return v;
}

//______________________________________________________________________________
inline TGLVertex3 Intersection(const TGLPlane & p1, const TGLPlane & p2, const TGLPlane & p3)
{
   Double_t m = Dot(p1.Norm(), Cross(p2.Norm(), p3.Norm()));
   TGLVector3 v = (Cross(p2.Norm(),p3.Norm())* -p1.D()) - (Cross(p3.Norm(),p1.Norm())*p2.D()) - (Cross(p1.Norm(),p2.Norm())*p3.D());
   return v / m;
}

//______________________________________________________________________________
inline void TGLPlane::Normalise()
{
   Double_t mag = sqrt( fVals[0]*fVals[0] + fVals[1]*fVals[1] + fVals[2]*fVals[2] );

   if ( mag == 0.0 ) {
      assert( kFALSE );
      return;
   }

   fVals[0] /= mag;
   fVals[1] /= mag;
   fVals[2] /= mag;
   fVals[3] /= mag;
}

/*************************************************************************
 * TGLMatrix - TODO
 *
 *
 *
 *************************************************************************/
class TGLMatrix
{
private:
   // Fields
   Double_t fVals[16]; // Column MAJOR as per OGL

   // Methods
   Bool_t ValidIndex(UInt_t index) const { return (index < 16); }

public:
   TGLMatrix();
   TGLMatrix(Double_t x, Double_t y, Double_t z);
   TGLMatrix(const Double_t vals[16]);
   TGLMatrix(const TGLMatrix & other);
   virtual ~TGLMatrix(); // ClassDef introduces virtual fns

   const TGLMatrix & operator=(const TGLMatrix & rhs) { Set(rhs.fVals); return *this; }
   //const TGLMatrix & operator*(const TGLMatrix & rhs);

   Double_t & operator [] (Int_t index)
   { if (!ValidIndex(index)) { assert(kFALSE); return fVals[0]; } else { return fVals[index]; } }

   void Set(const Double_t vals[16]);
   void SetIdentity();

   TGLVertex3  GetTranslation() const;
   void        SetTranslation(const TGLVertex3 & trans);
   void        Shift(const TGLVector3 & shift);
   TGLVector3  GetScale() const;
   void        SetScale(const TGLVector3 & scale);

   void TransformVertex(TGLVertex3 & vertex) const;
	void InvRot();

   void Dump() const;

   const Double_t * CArr() const { return fVals; }
   Double_t * Arr() { return fVals; }

   ClassDef(TGLMatrix,0) // GL matrix helper/wrapper class
};

/*************************************************************************
 * TGLUtil - TODO
 *
 *
 *
 *************************************************************************/
class TGLUtil
{
public:
   virtual ~TGLUtil() { }

   static void   CheckError();
   ClassDef(TGLUtil,0) // Wrapper class for misc GL pieces
};

#endif // ROOT_TGLUtil
