/**********************************************************
 *
 *nd.h
 *
 *High dimensional wrapper for OpenGL
 *
 *********************************************************/


#ifndef _ND_H_
#define _ND_H_


#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <vector>
//#include <windows.h>
#include <X11/X.h>    // Basic X library
#include <GL/gl.h>    // Basic Mesa OpenGL library
#include <GL/glut.h>  // Mesa OpenGL utility toolkit

using namespace std;

#define PI 3.151692
//constant that produces radians when multiplied by a degree value (PI/180)
#define TO_RADIANS 0.017563292619953296769236907685886

void translateNd(vector< vector<GLdouble> > & m, const vector<GLdouble> & p);

//translates the internal modelview matrix
void glTranslateNd(const vector<GLdouble> & p);

void rotateNd(vector< vector<GLdouble> > & m, GLdouble theta, GLint axis);

void rotateNd(vector< vector<GLdouble> > & m, GLdouble theta, GLint axis1, GLint axis2);

//rotates internal modelview matrix around an axis
void glRotateNd(GLdouble theta, GLint axis);

//rotates internal modelview matrix by twoplanes
     void glRotateNd(GLdouble theta, GLint axis1, GLint axis2);

void multMatrixNd(const vector< vector<GLdouble> > & m1, 
		  const vector< vector<GLdouble> > & m2, 
		  vector< vector<GLdouble> > & m3);

void perspectiveDivision(vector<GLdouble> & p, GLint d);

void matrixCoordinateProduct(const vector< vector<GLdouble> > & m,
			     const vector<GLdouble> & p,
			     vector<GLdouble> & r);

vector<GLdouble> matrixCoordinateProduct(const vector< vector<GLdouble> > & m,
					 const vector<GLdouble> & p);

void printCoordinate(const vector<GLdouble> & p);

void printMatrix(const vector< vector<GLdouble> > & m);

void glBeginNd(GLint mode);  //push new CallData on the CallList

void glEndNd(); 

void glVertexNdi(const vector<GLint> & v);

void glVertexNdf(const vector<GLdouble> & v);

void setIDmatrix(vector< vector<GLdouble> > & m);

vector< vector<GLdouble> > getIDmatrix(GLint size);

//returns an identity matrix with size of n+1 x n+1
vector< vector<GLdouble> > getIDmatrix();

void glDimensions(GLint n);

//mutator access to internal matrices
void glSetModelviewMatrixNd(const vector< vector<GLdouble> > & m);

void glSetProjectionMatrixNd(const vector< vector<GLdouble> > & m);

void glFrustumNd(const vector<GLdouble> & f);

class VertexInfo
{
public:
  VertexInfo( vector<GLdouble> flist );
  VertexInfo( vector<GLint> ilist );

  bool isfloat(){return _float;}
  bool isint(){return _int;}
  vector<GLint> ilist() {return _ivert;}
  vector<GLdouble> flist() {return _fvert;}
  GLfloat* color() {return _color;} 
  void perspectiveDivisionOnVertex();
  void projectionOnVertex(const vector< vector<GLdouble> > & proj);

private:
  vector<GLint> _ivert;
  vector<GLdouble> _fvert;
  GLfloat _color[4];
  bool _float, _int;
};


//one of these for each glBegin/glEnd pair.

class CallData
{
public:
  CallData(GLint mode);
  void addVertex(vector<GLdouble> v);
  void addVertex(vector<GLint> v);
  GLint mode(){return _mode;}
  unsigned int nVertices() {return _vinfo.size();}
  VertexInfo & operator [] (GLint x) {return _vinfo[x];}
  void perspectiveDivisionOnVertices();
  void projectionOnVertices(const vector< vector<GLdouble> > & proj);
private:
  vector<VertexInfo> _vinfo;
  GLint _mode;
};

class NDCallList
{
public:
  NDCallList(){};
  void toOpenGL3d();  //turns the data into 3d OpenGL calls
  void printOpenGL3d(); //prints the generated OpenGL calls to stdout
  void printNd();       //prints the nd calls
  void addtoList(GLint mode);
  void addVertex(vector<GLdouble> v);
  void addVertex(vector<GLint> v);
  unsigned int modes() {return _data.size();}
  void perspectiveDivisionOnList();
  void projectionOnList(const vector< vector<GLdouble> > & proj);

private:
  vector<CallData> _data;
};

class ND
{
public:
  ND() {_d = 4;}
  void setDimension(GLint d);
  size_t dimension() {return _d;}
  vector< vector<GLdouble> > modelview() {return _modelview;}
  vector< vector<GLdouble> > projection() {return _projection;}
  void setFrustum(const vector<GLdouble> & f){_frustum = f;}
  void setProjectionMatrix(const vector< vector<GLdouble> > & proj){_projection = proj;}
  void setModelviewMatrix(const vector< vector<GLdouble> > & model){_modelview = model;}
  vector< vector<GLdouble> > projectionMatrix() {return _projection;}
  vector< vector<GLdouble> > modelviewMatrix() {return _modelview;}
  vector<GLdouble> frustum() {return _frustum;}


private:
  GLint _d;  //number of dimensions to render in
  vector< vector<GLdouble> > _modelview, _projection;
  vector<GLdouble> _frustum;
};


//ND nd;
//NDCallList NDlist;

#endif
