#ifndef DEF_ENGINE
 #define DEF_ENGINE

//------------------------------------------//
//	TKA4 CEngine and basis Graph classes	//
//				19. 04. 2007				//
//------------------------------------------//

//--- Pragma 
#pragma comment (lib, "opengl32.lib")  
#pragma comment (lib, "glu32.lib")     
//=== Pragma 


//--- Includes
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>

//=== Includes 


//--- Defines
#ifndef PI
	#define PI 3.1415926535897932384626433832795
#endif
//=== Defines


//--- Class declarations
class txEngine;
class txObject;
//=== Class declarations


	// Direction of Execution
	enum EDirection { 
		d_for,	// Forward
		d_both,	// Forward & Bacward
		d_back,	// Never try to use it!

		d_for_super,	// Without glPushMatrix & glPopMatrix
		d_both_super,	// in Engine->Go()
		d_back_super,	

		d_nothing
	};


//---  Abstract Class Object TX  ---//
/*	Base for all Graph Objects.	   */
class txObject {
	friend txEngine;
	
	//--- OBJ Matrix ---//
	class ObjMatrix {
	public:
		GLfloat  sx, sy, sz;
		GLfloat  tx, ty, tz;
		GLfloat  rx, ry, rz;

	public : 
		ObjMatrix(){ sx=sy=sz=tx=ty=tz=rx=ry=rz=1; };

		virtual inline void ZoomIn() { sz = sy = sx+=0.1; };
		virtual inline void ZoomOut() { sz = sy = sx-=0.1; };
	};
	//=== OBJ Matrix ===//

	protected:
		txObject *prev, *next;
		EDirection dir;

	public: 
		ObjMatrix  matrix;

	public:
		txObject() { prev = NULL; next = NULL; dir = d_both; };
		virtual ~txObject() { if (next != NULL) delete next; };

		virtual void  Execute_Forward() {};
		virtual void  Execute_Backward() {};

		EDirection  GetDirection() { return dir; };
		void  SetDirection(EDirection d) { dir=d; };

		virtual inline void Zoom() {};
};



//---  Class Engine TX  ---//
/*	Make processing of 
	graph TX_Objects.	 */

class txEngine {
	
	private:
		txObject *first, *final;
		txObject *current;
	
	public:
		txEngine() { first = new txObject(); first->dir=d_nothing; final = first; current = first; };
		~txEngine() { if (first != NULL) 	delete first; };

		void  AddObject(txObject *new_);

		//--- Events ---
		inline void  Init();
		inline void  Go();
		
		inline void  OnZoomIn();
		inline void  OnZoomOut();
};


//------------------------- :: Functions :: ---------------------------

//--- txEngine :: Add TX_Object 
void  txEngine :: Init()
{
	glEnableClientState (GL_VERTEX_ARRAY);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
};


//--- txEngine :: Add TX_Object 
void  txEngine :: AddObject(txObject *new_)
{
	new_ -> prev = final;
	
	final -> next = new_;
	final = new_;
};


//--- txEngine :: OnZoomIn
void  txEngine :: OnZoomIn()
{
	for (current = first; current != NULL; current = current -> next)
		current->matrix.ZoomIn();
};


//--- txEngine :: OnZoomOut
void  txEngine :: OnZoomOut()
{
	for (current = first; current != NULL; current = current -> next)
		current->matrix.ZoomOut();
};


//--- txEngine :: Go drawing
void  txEngine :: Go()
{
	// Forward & Both -> next
	for (current = first; current != NULL; current = current -> next)
		switch (current -> GetDirection()) {
			case d_for : 
				glPushMatrix();
				current->Zoom();
				current->Execute_Forward(); 
				glPopMatrix();
				break;

			case d_both : 
				glPushMatrix();
				current->Zoom();
				current->Execute_Forward(); 
				current->dir = d_back;
				glPopMatrix();
				break;

			case d_for_super : 
				current->Zoom();
				current->Execute_Forward(); 
				break;

			case d_both_super : 
				current->Zoom();
				current->Execute_Forward(); 
				current->dir = d_back_super;
				break;
		};

	// Backward -> prev
	for ( current = final ; current != NULL; current = current -> prev)
	{
		if (current -> dir == d_back) {
			glPushMatrix();
			current->Execute_Backward(); 
			current->dir = d_both;
			glPopMatrix();
		};
		
		if (current -> dir == d_back_super) {
			current->Execute_Backward(); 
			current->dir = d_both_super;
		};
	};
};


#endif

