#ifndef DEF_PRIMITIVES
 #define DEF_PRIMITIVES
//------------------------------------------//
//	TKA4 CEngine and basis Graph classes	//
//				19. 04. 2007				//
//------------------------------------------//


//--- Includes
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>
#include <math.h>

#include "TX_Engine.h"
//=== Includes 


//--- Pragma 
#pragma comment (lib, "opengl32.lib")  
#pragma comment (lib, "glu32.lib")     

#pragma warning(disable:4305) 
//=== Pragma 


//--- Defines
//=== Defines


//--- Class declarations
union txpoint2d;
union txpoint3d;

class txObj_Begin;
class txObj_Axes;
class txObj_Grid2d;
class txObj_Series2d;
class txObj_Series2d_log;
//=== Class declarations


//--- TX Point Type ---//
// 2D
struct txpoint_real2d { GLfloat x,y; };
struct txpoint_int2d { GLint x,y; };
union txpoint2d { 
	public:
		txpoint_real2d r; txpoint_int2d i; 
	
		txpoint2d() { i.x=0; i.y=0; };
		txpoint2d(GLint x, GLint y = 0) { i.x=x; i.y=y; };
		txpoint2d(GLfloat x, GLfloat y = 0) { r.x=x; r.y=y; };

		inline GLfloat* Addr(float) { return &r.x; };
		inline GLint* Addr(int) { return &i.x; };
		inline GLfloat* Addr() { return &r.x; };
};


// 3D
struct txpoint_real3d { GLfloat x,y,z; };
struct txpoint_int3d { GLint x,y,z; };
union txpoint3d { 
	public :
		txpoint_real3d r; txpoint_int3d i; 
		
		txpoint3d() { i.x=0; i.y=0; i.z=0; };

		txpoint3d(GLint x, GLint y = 0, GLint z = 0) 
			{ i.x=x; i.y=y; i.z=z; };

		txpoint3d(GLfloat x, GLfloat y = 0, GLfloat z = 0) 
			{ r.x=x; r.y=y; r.z=z; };

		txpoint3d(txpoint2d p) 
			{ r.x=p.r.x; r.y=p.r.y; r.z=0; };

		inline GLfloat* Addr(float) { return &r.x; };
		inline GLint* Addr(int) { return &i.x; };
		inline GLfloat* Addr() { return &r.x; };

};


// Color 4D
struct txcolor_real4d { GLfloat r,g,b,a; };
struct txcolor_int4d { GLint r,g,b,a; };
union txcolor4d { 
	public :
		txcolor_real4d r; txcolor_int4d i; 
		
		txcolor4d() { r.r=0; r.g=0; r.b=0; r.a = 1; };

		/*txcolor4d(GLint x, GLint y = 0, GLint z = 0, GLint a = 255) 
			{ i.r=x; i.g=y; i.b=z; i.a = a; };*/

		txcolor4d(GLfloat x, GLfloat y = 0, GLfloat z = 0, GLfloat a = 1.0) 
			{ r.r=x; r.g=y; r.b=z; r.a=a; };

		inline GLfloat* Addr(float) { return &r.r; };
		inline GLint* Addr(int) { return &i.r; };
		inline GLfloat* Addr() { return &r.r; };
};



// View Matrix 
class txview_matrix { 
	public: 
		txpoint3d  scale, tran, rot;

		txview_matrix () 
			{ scale.r.x = 1; scale.r.y = 1; scale.r.z = 1; 
			  tran.r.x = 0; tran.r.y = 0; tran.r.z = 0; 
			  rot.r.x = 0; rot.r.y = 0; rot.r.z = 0; 
			};
};


//---------------------------------------------------------------------
//--- Object Matrix ---
class txObj_Matrix : public txObject {
public:
	txObj_Matrix() {SetDirection(d_for_super);};
	virtual inline void Zoom() { glScalef(matrix.sx,matrix.sy,matrix.sz); };
};



//---  Axes  ---//
class txObj_Axes : public txObject {
	protected : 
		txpoint3d	p[6];
		txpoint3d	res_min, res_max;

		inline virtual void TestMinMax(txpoint3d *min, txpoint3d *max);

	public :
		txObj_Axes ();
		txObj_Axes (txpoint3d *min, txpoint3d *max) { SetAxes((*min), (*max)); };
					
		inline virtual txpoint3d GetMin() const { return res_min; };
		inline virtual txpoint3d GetMax() const { return res_max; };

		virtual void SetAxes(txpoint3d min, txpoint3d max);
		virtual void SetAxesMirror(txpoint3d min, txpoint3d max);

		virtual void Execute_Forward (); 
};


//---  Begin  ---//
class txObj_Begin : public txObject {
protected:
		txview_matrix matr;
				
	public :
		txObj_Begin () { dir = d_both; };
		txObj_Begin (txview_matrix matr_) 
		{ dir = d_both; SetMatrix(matr_); };
		
		inline virtual void  SetMatrix (txview_matrix matr_) 
		{  matr = matr_; };

		virtual void Execute_Forward (); 
		virtual void Execute_Backward (); 
};


//---  Super Begin  ---//
class txObj_SuperBegin : public txObj_Begin {
protected :	
					
	public :
		txObj_SuperBegin() { dir = d_both_super; };
		inline virtual void SetMinMax(txpoint3d min, txpoint3d max);
};


//---  Series 2D  ---//
class txObj_Series2d : public txObject {
	protected: 	
		int count;
		GLenum draw_mode;
		txpoint2d	*p;
		txpoint2d  max, min;
		txcolor4d  color;
		
		bool AntiAlias;

		virtual inline GLfloat GetStep(GLfloat i, GLfloat step)
		{ return i*step; };

	public :
		virtual inline void  Constr_default() 
		{ color = txcolor4d((GLfloat)1.0,1.0,1.0,1.0); 
		  draw_mode = GL_LINE_STRIP;
		  AntiAlias = false;
		};

		txObj_Series2d () { p = NULL; count =  0;  Constr_default(); };

		txObj_Series2d (int *mas, int array_size, float x_step = 1.0) 
			{ Constr_default(); SetY(mas, array_size, x_step); };

		virtual ~txObj_Series2d () { if (p != NULL) delete [] p; };

		virtual inline void SetY(int *mas, int array_size, float x_step=1.0);
		virtual inline void SetY(float *mas, int array_size, float x_step=1.0);
		
		virtual inline void SetAntiAlias(bool b) { AntiAlias = b; };

		virtual inline void SetColor(txcolor4d c) { color = c; };
		virtual inline void SetDrawMode(GLenum m) { draw_mode = m; };


		virtual inline txpoint2d GetMax() const 
			{ 
				return max; 
			};

		virtual inline txpoint2d GetMin() const
			{ 
				return min; 
			};

		virtual void Execute_Forward (); 
};


//---  Series 2D LOG  ---//
class txObj_Series2d_log : public txObj_Series2d {
	protected :
		virtual inline GLfloat GetStep(GLfloat i, GLfloat step)
		{ return log10( (i+1)*step ); };
};


//---  Grid 2D  ---//
class txObj_Grid2d : public txObject {
	protected :
		txpoint3d  max, min;
		txcolor4d  color;
		
		txpoint2d  *px;
		txpoint2d  *py;
		int countx, county;	// count of lines

		virtual inline int 
			Evaluate_Grid(int begin, int count, txpoint2d *px, GLfloat step, char axis = 0);

		virtual inline GLfloat GetStep(GLfloat i, GLfloat step)
		{ return  i*step; };

	public:
		inline virtual void default_constr() 
			{ color = txcolor4d(1.0,1.0,1.0,0.35); 
			  px = new txpoint2d; px = new txpoint2d;
			  countx = 0; county = 0;
			  px=NULL; py=NULL;
			};

		txObj_Grid2d() { default_constr(); };
		txObj_Grid2d(txpoint3d min_, txpoint3d max_) { default_constr(); SetMaxMin(min_, max_); }
		~txObj_Grid2d () { delete [] px; delete [] py; }

		inline virtual void SetMaxMin(txpoint3d min_, txpoint3d max_);
		inline virtual void SetColor(txcolor4d c) { color = c; };

		virtual void Execute_Forward (); 
	
};


//---  Grid 2D LOG  ---//
class txObj_Grid2d_log : public txObj_Grid2d {
	protected :
		virtual inline GLfloat GetStep(GLfloat i, GLfloat step)
		{ return  i*step; };
};



//---  Series 2D  ---//
class txObj_Series2d_XY : public txObj_Series2d {
	protected: 	
		
	public :
		txObj_Series2d_XY () { p = NULL; count =  0;  Constr_default(); dir=d_for; };
		~txObj_Series2d_XY () { if (p != NULL) delete [] p; };

		inline virtual void AddXY(float x, float y);
};


//---  Background  ---//
class txObj_Background : public txObject {
	public :
		txpoint2d p[4];
		txcolor4d c[4];
		
		txObj_Background() 
		{ p[0].r.x = -1.0; p[0].r.y = -1.0; 
		  p[1].r.x =  1.0; p[1].r.y = -1.0;
		  p[2].r.x =  1.0; p[2].r.y =  1.0;
		  p[3].r.x = -1.0; p[3].r.y =  1.0;
		};

		virtual void Execute_Forward();	
};


//
//------------------------- ::::::::::::::: ---------------------------
//------------------------- :: Functions :: ---------------------------
//------------------------- ::::::::::::::: ---------------------------

//-------------------------- Background -----------------------------

//--- txObj_Background :: Execute Forward
void txObj_Background :: Execute_Forward () 
{
	glEnable(GL_BLEND);

	glBegin(GL_QUADS);
	for (int i = 0; i<4; i++)
	{
		glColor4fv(c[i].Addr());
		glVertex2fv(p[i].Addr());
	}
	glEnd();

	glDisable(GL_BLEND);
};


//-------------------------- Series 2d XY -----------------------------

//--- txObj_Series2d_XY :: Add x y
void txObj_Series2d_XY :: AddXY (float x, float y) {
	count++;
	
	txpoint2d *p2 = new txpoint2d[count];
	
	if (p != NULL) {
		for (int i = 0; i < (count -1); i++)
			p2[i] = p[i];
		delete[] p;
	};

	p = p2;

	if (min.r.x > x) min.r.x = x;
	if (min.r.y > y) min.r.y = y;
	
	if (max.r.x < x) max.r.x = x;
	if (max.r.y < y) max.r.y = y;

	p[count-1].r.x = x;
	p[count-1].r.y = y;
};


//-------------------------- Grid 2D ----------------------------------

//--- txObj_Grid2d :: Evaluate Grid 2D
int txObj_Grid2d :: Evaluate_Grid(int begin, int count, txpoint2d *px, GLfloat step, char axis)
{
	float GetStepParam = 0;

	if (axis==0) // By X = const
		for (int i = begin; i<begin + count; i+=2, GetStepParam++) 
		{
			px[i].r.x = min.r.x;
			px[i].r.y = GetStep(GetStepParam, step);
			px[i+1].r.x = max.r.x;
			px[i+1].r.y = px[i].r.y;
		}
	
	if (axis==1) // By Y = const
		for (int i = begin; i<begin + count; i+=2,  GetStepParam++) 
		{
			px[i].r.y = min.r.y;
			px[i].r.x = GetStep(GetStepParam, step);
			px[i+1].r.y = max.r.y;
			px[i+1].r.x = px[i].r.x;
		}

	return count;
};

//--- txObj_Grid2d :: Set Max Min 
void txObj_Grid2d :: SetMaxMin (txpoint3d min_, txpoint3d max_) {
	min = min_; max = max_;
	
	// chotniya!
	countx = 10;  county = 10;

	//--- X = Const
	int tmp_count = 0;
	px = new txpoint2d[countx*2];

	GLfloat  count_step = float(countx);
	GLfloat  step;
	
	if (max.r.y == abs(min.r.y)) {
		step = max.r.y /count_step;
	}
	else {
		
		if (max.r.y > 0) step = max.r.y /count_step; else
		if (min.r.y < 0) step = abs(min.r.y) /count_step;
	}
	step *= 2;


	if (min.r.y < 0)
		tmp_count += Evaluate_Grid(tmp_count, int(count_step), px, -step);

	if (max.r.y > 0)	
		tmp_count += Evaluate_Grid(tmp_count, int(count_step), px, step);

	countx = tmp_count;

	//---- Y = Const
	tmp_count = 0;
	py = new txpoint2d[county*2];

	count_step = float(county);
		
	if (max.r.x == abs(min.r.x)) {
		step = max.r.x /count_step;
	}
	else {
		
		if (max.r.x > 0) step = max.r.x /count_step; else
		if (min.r.x < 0) step = abs(min.r.x) /count_step;
	}
	step *= 2;

	if (min.r.x < 0)
		tmp_count += Evaluate_Grid(tmp_count, int(count_step), py, -step, 1);

	if (max.r.x > 0) 		
		tmp_count += Evaluate_Grid(tmp_count, int(count_step), py, step, 1);

	county = tmp_count;

};


//--- txObj_Grid2d :: Execute Forward
void txObj_Grid2d :: Execute_Forward () 
{
	//glColor4f(color.r.r, color.r.g, color.r.b, color.r.a);

	glLineStipple(1,0xF0F);
	glEnable(GL_LINE_STIPPLE);
    glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);

	glColor4fv(color.Addr());
	glLineWidth(1.0);
	
		glVertexPointer(2, GL_FLOAT, 0, px[0].Addr());
		glDrawArrays(GL_LINES, 0, countx);
	
		glVertexPointer(2, GL_FLOAT, 0, py[0].Addr());
		glDrawArrays(GL_LINES, 0, county);

	glDisable(GL_LINE_STIPPLE);
	glDisable(GL_BLEND);
	glDisable(GL_LINE_SMOOTH);
};


//-------------------------- Series 2d --------------------------------

//--- txObj_Series2d :: Get Y array with x_step INT
void txObj_Series2d :: SetY (int *mas, int array_size, float x_step) {
	count = array_size;
	if (p != NULL) delete[] p;
	p = new txpoint2d[count];

	max.r.y = min.r.y = (GLfloat) mas[0];
	min.r.x = 0.0;
	max.r.x =  GetStep( (GLfloat) (count-1), x_step);

	for (int i = 0; i<count; i++) {
		p[i].r.y = (GLfloat) mas[i];
		p[i].r.x = GetStep( (GLfloat) i, x_step);

		if (max.r.y < p[i].r.y) max.r.y = p[i].r.y;
		if (min.r.y > p[i].r.y) min.r.y = p[i].r.y;
	}
};


//--- txObj_Series2d :: Get Y array with x_step FLOAT
void txObj_Series2d :: SetY (float *mas, int array_size, float x_step) {
	count = array_size;
	if (p != NULL) delete[] p;
	p = new txpoint2d[count];

	max.r.y = min.r.y = mas[0];
	min.r.x = 0.0;
	max.r.x = GetStep( (GLfloat) (count-1), x_step);

	for (int i = 0; i<count; i++) {
		p[i].r.y = mas[i];
		p[i].r.x = GetStep( (GLfloat) i, x_step);

		if (max.r.y < p[i].r.y) max.r.y = p[i].r.y;
		if (min.r.y > p[i].r.y) min.r.y = p[i].r.y;
	}
};


//--- txObj_Series2d :: Drawing of axes (forward)
void txObj_Series2d :: Execute_Forward ()
{
	glColor4fv(color.Addr());
	
	if (AntiAlias) { 
		glLineWidth(1.5);
		glEnable(GL_BLEND);
		glEnable(GL_LINE_SMOOTH); 
	}

	glVertexPointer(2, GL_FLOAT, 0, p[0].Addr());
	glDrawArrays(draw_mode, 0, count);
	
	if (AntiAlias) { 
		glLineWidth(1.0);
		glDisable(GL_LINE_SMOOTH); 
	}

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
};


//-------------------------- Axes -------------------------------------

//--- txObj_Axes :: Constructer default
txObj_Axes :: txObj_Axes () {
	SetAxes( txpoint3d( (GLfloat) 0.0, 0.0, 0.0) , txpoint3d( (GLfloat) 1.0, 1.0, 1.0) );
};


//--- txObj_Axes :: Test
void txObj_Axes :: TestMinMax (txpoint3d *min, txpoint3d *max) 
{
	if (min->r.x > 0) min->r.x = 0;
	if (min->r.y > 0) min->r.y = 0;
	if (min->r.z > 0) min->r.z = 0;

	if (max->r.x < 0) max->r.x = 0;
	if (max->r.y < 0) max->r.y = 0;
	if (max->r.z < 0) max->r.z = 0;
};

//--- txObj_Axes :: Set Axes 
void txObj_Axes :: SetAxes (txpoint3d min, txpoint3d max) {
	
	const float m_coef = 1.05;
		max.r.x *= m_coef;
		max.r.y *= m_coef;
		max.r.z *= m_coef;
		min.r.x *= m_coef;
		min.r.y *= m_coef;
		min.r.z *= m_coef;

	TestMinMax(&min, &max);
	// X axis
	p[0] = txpoint3d ((float) min.r.x, 0.0, 0.0);
	p[1] = txpoint3d ((float) max.r.x, 0.0, 0.0);
	// Y axis
	p[2] = txpoint3d ((float) 0.0, min.r.y, 0.0);
	p[3] = txpoint3d ((float) 0.0, max.r.y, 0.0);
	// Z axis
	p[4] = txpoint3d ((float) 0.0,  0.0, min.r.z);
	p[5] = txpoint3d ((float) 0.0,  0.0, max.r.z);

	res_min = min;
	res_max = max;
};


//--- txObj_Axes :: Set Axes Mirror
void txObj_Axes :: SetAxesMirror (txpoint3d min, txpoint3d max) {
    TestMinMax(&min, &max);

	txpoint3d min2(min), max2(max);
	//----
	if (min.r.x != 0)
	{if (abs(min.r.x) < abs(max.r.x)) min2.r.x = -max.r.x; else max2.r.x = abs(min.r.x);}
	if (min.r.y != 0)
	{if (abs(min.r.y) < abs(max.r.y)) min2.r.y = -max.r.y; else max2.r.y = abs(min.r.y);}
	if (min.r.z != 0)
	{if (abs(min.r.z) < abs(max.r.z)) min2.r.z = -max.r.z; else max2.r.z = abs(min.r.z);}

	//----
	if (min.r.x == 0) min2.r.x = 0;
	if (min.r.y == 0) min2.r.y = 0;
	
	if (max.r.x == 0) max2.r.x = 0;
	if (max.r.y == 0) max2.r.y = 0;

	//----
	SetAxes(min2, max2);
};


//--- txObj_Axes :: Drawing of axes (forward)
void txObj_Axes :: Execute_Forward ()
{
	glColor4f(1.0, 1.0, 1.0, 1.0);
	 
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);
	glLineWidth(1.8);
	
	//------- X Y Z - axes 
	glVertexPointer(3, GL_FLOAT, 0, p[0].Addr());
	glDrawArrays(GL_LINES, 0, 5);
	
	glLineWidth(1.0);
	glDisable(GL_BLEND);
	glDisable(GL_LINE_SMOOTH);
};


//------------------------  Begin  ------------------------------------

//--- txObj_Begin :: Some prepares 
void txObj_Begin :: Execute_Forward ()
{
	glPushMatrix();
	
	glClearColor(0.0, 0.0, 0.0, 0.0);

	glTranslatef(matr.tran.r.x, matr.tran.r.y, matr.tran.r.z);
	
	glScalef(matr.scale.r.x, matr.scale.r.y, matr.scale.r.z);

	glRotatef(matr.rot.r.x, 1, 0, 0);
	glRotatef(matr.rot.r.y, 0, 1, 0);
	glRotatef(matr.rot.r.z, 0, 0, 1);
	
};


//--- txObj_Begin :: Some prepares
void txObj_Begin :: Execute_Backward ()
{
	glPopMatrix();
};


//------------------------  Begin  ------------------------------------

void txObj_SuperBegin :: SetMinMax(txpoint3d min, txpoint3d max)
{
	//--- Begin 
		txview_matrix m;
			
			//--- X
			if (max.r.x != 0)  m.scale.r.x = 1 / max.r.x;  
			else  m.scale.r.x = 1 / abs(min.r.x);  
			
			// Right 
			if ( (min.r.x==0) && (max.r.x>0) ) 
			{
				m.scale.r.x *= 1.8; 
				m.tran.r.x = -0.9;
			}

			// Left 
			if ( (min.r.x<0) && (max.r.x==0) ) 
			{
				m.scale.r.x *= 1.8; 
				m.tran.r.x = 0.9;
			}

			//--- Y 
			if (max.r.y != 0)  m.scale.r.y = 1 / max.r.y;  
			else  m.scale.r.y = 1 / abs(min.r.y);  
			
			// Top 
			if ( (min.r.y==0) && (max.r.y>0) ) 
			{
				m.scale.r.y *= 1.8; 
				m.tran.r.y = -0.9;
			}
			
			// Bottom
			if ( (min.r.y<0) && (max.r.y==0) ) 
			{
				m.scale.r.y *= 1.8; 
				m.tran.r.y = 0.9;
			}
			
			m.scale.r.x *= 0.97;			
			m.scale.r.y *= 0.97;			
			m.scale.r.z *= 1;

//			m.tran.r.x = 0; m.tran.r.y = 0;
//			m.scale.r.x = 0.5;m.scale.r.y = 0.5;
		matr = m;
};

#endif



