///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Copyright (C) 2006 by Intel Corporation and Carnegie Mellon University    //
// Contacts: casey.j.helfrich @ intel.com                                    //
//           bdr @ cs.cmu.edu                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#ifndef __PRIMITIVES_HXX
#define __PRIMITIVES_HXX

#include <stdlib.h>
#include <iostream>
#include <math.h>

#include "GlobalConstants.hxx"

using namespace std;

////////////////////////////////////////////////////////////////////////////// 
// Currently ticks are integers, not sure what they should really be.
// For now, lets assume ticks are measured in microseconds.  This is a LOCAL
// only time.  There is no external synch between catoms (from the Catom
// point of view).

typedef unsigned Ticks;
typedef unsigned int catomID;
typedef unsigned int featureID;

////////////////////////////////////////////////////////////////////////////// 
// The Point3D class to describe and manipulate points in three space.

bool floatStrictLess(float a, float b);

bool floatEq(float a, float b);

class Point3D
{  
private:
  double x;
  double y;
  double z;

public:
  // constructors
  Point3D(double xx, double yy, double zz) : x(xx), y(yy), z(zz) { };
  Point3D(double r, double phi, double theta, bool polar) {
    x = r * cos(phi) * cos(theta);
    y = r * cos(phi) * sin(theta);
    z = r * sin(phi);
  };
  Point3D( const Point3D& a ) : x(a.x), y(a.y), z(a.z) { };
  Point3D( const double *a ): x(a[0]), y(a[1]), z(a[2]) { };
  Point3D() : x(0), y(0), z(0) { };

  // accessor methods
  double getX() const { return x; };
  double getY() const { return y; };
  double getZ() const { return z; };
  
  char* toString(char* buffer = 0) const {
    static char tbuff[64];
    if (buffer == 0) {
      buffer = tbuff;
    }
    sprintf(buffer, "(%g,%g,%g)",
	    getX(), getY(), getZ());
    return buffer;
  }
  
  double getR() const { return sqrt((x*x)+(y*y)+(z*z)); };
  double norm() const { return getR(); }
  double getPhi() const { return acos(z / getR()); };
  double getTheta() const { return atan(y/x); };

  // update data
  void setData(const Point3D& a) { x=a.x; y=a.y; z=a.z; };
  void setX(double newval) { x=newval; };
  void setY(double newval) { y=newval; };
  void setZ(double newval) { z=newval; };

  Point3D& operator=( const Point3D& a ) { setData(a); return *this; };
  Point3D& operator+=( const Point3D& a ) { x+=a.x; y+=a.y; z+=a.z; return *this; };
  Point3D& operator-=( const Point3D& a ) { x-=a.x; y-=a.y; z-=a.z; return *this; };
  Point3D& operator*=( double s ) { x*=s; y*=s; z*=s; return *this; };
  Point3D& operator/=( double s ) { x/=s; y/=s; z/=s; return *this; };
 
  // Rotatation methods
  Point3D eulerRotate(double roll, double pitch, double yaw) const;
  
  // math operations and comparisons
  Point3D operator+(const Point3D& a) const { Point3D x=*this; return x+=a; };
  Point3D operator-(const Point3D& a) const { Point3D x=*this; return x-=a; };
  Point3D operator-() const { Point3D x; return x-=*this; }    // unary -
  Point3D operator*(double s) const { Point3D x=*this; return x*=s; };
  Point3D operator/(double s) const { Point3D x=*this; return x/=s; };
  
  bool operator<(const Point3D& a) const { return norm()<a.norm(); };
  bool operator>(const Point3D& a) const { return norm()>a.norm(); };
  bool operator==(const Point3D& a) const { return floatEq(a.x,x) && floatEq(a.y,y) && floatEq(a.z,z); };
  bool operator!=(const Point3D& a) const { return !(floatEq(a.x,x)&&floatEq(a.y,y)&&floatEq(a.z,z)); };

  double distanceFrom(const Point3D& a) const { return (*this-a).norm(); };
  double dot(const Point3D& a) const { return x*a.x+y*a.y+z*a.z; };
  Point3D cross(const Point3D& a) const { 
    return Point3D( y*a.z-z*a.y, z*a.x-x*a.z, x*a.y-y*a.x );
  };

  // for compatibility
  Point3D plus(const Point3D& a) const { return *this+a; };
  Point3D minus(const Point3D& a) const { return *this-a; };
  bool isLessThan(const Point3D& a) const { return *this<a; };
  bool isGreaterThan(const Point3D& a) const { return *this>a; };
  bool isEqual(const Point3D& a) const { return *this==a; };
  
  // for hashing
  size_t hash(void) const {
    return (size_t)((x*(y*67)*(z*511))*1023);
  }
};

inline Point3D operator*(double a, const Point3D& p) { return p*a; }
ostream& operator<<(ostream& os, const Point3D& p);


//////////////////////////////////////////////////////////////////////////////
// The WorldPoint3D class (subclass of Point3D)

class WorldPoint3D: public Point3D {
public:
  WorldPoint3D(double xx, double yy, double zz) : Point3D(xx,yy,zz) {};
  WorldPoint3D(double r, double phi, double theta, bool polar) : Point3D(r, phi, theta, polar) {};
  WorldPoint3D(const Point3D& a) : Point3D(a) {};
  WorldPoint3D(const double *a) : Point3D(a) {};
  WorldPoint3D() : Point3D() {};
};


//////////////////////////////////////////////////////////////////////////////
// The RelPoint3D class (subclass of Point3D)

class RelPoint3D: public Point3D {
public:
  RelPoint3D(double xx, double yy, double zz) : Point3D(xx,yy,zz) {};
  RelPoint3D(double r, double phi, double theta, bool polar) : Point3D(r, phi, theta, polar) {};
  RelPoint3D(const Point3D& a) : Point3D(a) {};
  RelPoint3D(const double *a) : Point3D(a) {};
  RelPoint3D() : Point3D() {};
};


//////////////////////////////////////////////////////////////////////////////
// The DPRLine class (for drawing connecting lines between catoms)

class DPRLine {
private:

  // Internal variables
  catomID src, dest;
  Point3D srcPt,destPt;
  unsigned int r, g, b;
  char type;

public:

  // Constructors
  DPRLine(catomID, catomID);
  DPRLine(catomID, catomID, unsigned int, unsigned int, unsigned int);
  DPRLine(catomID, Point3D);
  DPRLine(catomID, Point3D, unsigned int, unsigned int, unsigned int);
  ~DPRLine() {};

  // Equality test
  bool equals(catomID, catomID);
  bool equals(catomID, Point3D);

  // Accessors
  void setType(int _t){type = _t;}
  int getType(){return type;}
  catomID getSrc() { return src; }
  catomID getDest() { return dest; }
  Point3D getSrcPt() { return srcPt; }
  Point3D getDestPt() { return destPt; }
  uint getRed(){return r;}
  uint getGreen(){return g;}
  uint getBlue(){return b;}
};


////////////////////////////////////////////////////////////////////////////// 
// The Plane class to describe a plane in 3D space using 3 Point3Ds

class Plane
{
public:
  Point3D a;
  Point3D b;
  Point3D c;
  
  // accessor methods
  Point3D getA();
  Point3D getB();
  Point3D getC();
  
  // constructors
  Plane(Point3D aa, Point3D bb, Point3D cc);
};


////////////////////////////////////////////////////////////////////////////// 
// Class to represent GUIDs.  Don't count on them being in sequence.  In
// reality they will be randomly assigned 128 bit integers or the like.
//
// internally, a GUID with 0 as an ID will not be considered a valid GUID, 
// see isaGUID()

class GUID 
{
private:
  unsigned long id;
  
public:
  // Math Operations

  // return true if two IDs are the same
  bool isEqual(const GUID other) const;	 
  
  // return true if provided GUID is greater
  bool isGreater(const GUID other) const;    

  // Accessor Methods
  unsigned long getID() const;
  void setID(unsigned long i);
  
  // Constructors
  GUID();
  GUID(unsigned long i);
  GUID(const GUID& guid);
  static GUID getAnonGUID();
  
  // Prediates
  bool isaGUID() const;
};

struct lessGUID {
  bool operator()(const GUID &a, const GUID &b) const {
    return b.isGreater(a);
  }
};

#endif    /* __PRIMITIVES_H */
