/*
  Nev, ETR azonosito:
*/

#include <GL/glut.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

// Define a constant for the value of PI
#define GL_PI 3.1415f

static int MenuID, IdleMenu;
static int IdlePrint = 0;

static GLfloat xRot = 0.0, yRot = 0.0;

// globĂĄlis ambiens fĂŠny szĂ­ne.
GLfloat ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f };

//  0. sorszĂĄmĂş pontszerĹą fĂŠnyforrĂĄs szĂ­ne
GLfloat l0_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
GLfloat l0_diffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat l0_position[] = { 0.0f, 0.0f, 70.0f, 1.0f };

// textĂşra azonosĂ­tĂłk
GLuint texture1;
GLuint texture2;

char atlatsz = 1;


// Reduces a normal vector specified as a set of three coordinates,
// to a unit normal vector of length one.
void ReduceToUnit(float vector[3])
  {
	float length;
	
	// Calculate the length of the vector		
	length = (float)sqrt((vector[0]*vector[0]) + 
						(vector[1]*vector[1]) +
						(vector[2]*vector[2]));

	// Keep the program from blowing up by providing an exceptable
	// value for vectors that may calculated too close to zero.
	if(length == 0.0f)
		length = 1.0f;

	// Dividing each element by the length will result in a
	// unit normal vector.
	vector[0] /= length;
	vector[1] /= length;
	vector[2] /= length;
  }


// Points p1, p2, & p3 specified in counter clock-wise order
void calcNormal(float v[3][3], float out[3])
  {
	float v1[3],v2[3];
	static const int x = 0;
	static const int y = 1;
	static const int z = 2;

	// Calculate two vectors from the three points
	v1[x] = v[0][x] - v[1][x];
	v1[y] = v[0][y] - v[1][y];
	v1[z] = v[0][z] - v[1][z];

	v2[x] = v[1][x] - v[2][x];
	v2[y] = v[1][y] - v[2][y];
	v2[z] = v[1][z] - v[2][z];

	// Take the cross product of the two vectors to get
	// the normal vector which will be stored in out
	out[x] = v1[y]*v2[z] - v1[z]*v2[y];
	out[y] = v1[z]*v2[x] - v1[x]*v2[z];
	out[z] = v1[x]*v2[y] - v1[y]*v2[x];

	// Normalize the vector (shorten length to one)
	ReduceToUnit(out);
  }

/*
  Teendok:
    - normalvektorok beallitasa
    - textura koordinatak beallitasa
*/
void CylinderApprox(int n, double radius, double height)
{
  int i, j;
  GLfloat angle;
  GLdouble stack_step, stack_actual;

  if(n < 3)
    n = 3;

  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, texture1);

  glBegin(GL_QUAD_STRIP);
  for(i = 0, angle = 0.0; i <= n; i++, angle += 2.0 * GL_PI / n)
    {
       //henger palĂĄst normĂĄlvektora
      glNormal3f(cos(angle), sin(angle), 0.0);
      glTexCoord2f((float)i/n, 1.0);
      glVertex3f(radius * cos(angle), radius * sin(angle), height / 2.0);
      glTexCoord2f((float)i/n, 0.0);
      glVertex3f(radius * cos(angle), radius * sin(angle), -height / 2.0);
    }
  glEnd();

  glDisable(GL_TEXTURE_2D);
}

/*
  Teendok:
    - normalvektor beallitasa
    - textura koordinatak beallitasa
*/
void Tabla()
{

  glPushMatrix();
  glTranslatef(0.0, 0.0, 1.0);

	float normal[3];
	float vertices[3][3]={{-6.0, -6.0, 0.0},
			      {6.0, -6.0, 0.0},
			      {0.0, 6.0, 0.0}};

  calcNormal(vertices, normal);

  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, texture2);

  // átlátszóság előkészítése
  if(atlatsz) {
    glEnable(GL_BLEND);
  }
  glEnable(GL_CULL_FACE);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDepthMask(GL_FALSE);

  glBegin(GL_TRIANGLES);
//  glNormal3f(0.0, 0.0, 1.0);
  glNormal3fv(normal);
  glTexCoord2f(0.0, 0.0);
  glVertex3f(-6.0, -6.0, 0.0);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(6.0, -6.0, 0.0);
  glTexCoord2f(0.5, 1.0);
  glVertex3f(0.0, 6.0, 0.0);
  glEnd();

  glDepthMask(GL_TRUE);
  glDisable(GL_BLEND);

  glDisable(GL_TEXTURE_2D);

  glPopMatrix();
}

// Called to draw scene
void RenderScene(void)
{
  GLfloat x,y, x2, y2, angle;  // Storage for coordinates and angles
  int iPivot = 1;		// Used to flag alternating colors 
  int i;

  // Clear the window with current clearing color
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // >> Modellezo programresz
  glColor3f(1.0, 1.0, 1.0);
 
  glPushMatrix();
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);
  glRotatef(yRot, 0.0f, 1.0f, 0.0f); 


  /*
    Tartooszlop
    Erre keruljon a metal_texture2 minta.
  */
  glPushMatrix();
  glTranslatef(0.0, -5.0, 0.0);
  glRotatef(90.0, 1.0, 0.0, 0.0);
  CylinderApprox(32, 0.8, 28.0);
  glPopMatrix();

  /*
    Tabla
    - Erre keruljon a korforgalom.bmp minta
      megfelelo resze kivagva.
    - Legyen 30%-ban atlatszo, ami ki/bekacsolhato az F1/F2-vel.
  */  
  glColor4f(1.0, 1.0, 1.0, 0.3);  // ĂĄtlĂĄtszĂłsĂĄg mĂŠrtĂŠkĂŠnek ĂŠs szĂ­nnnek a megadĂĄsa
  glPushMatrix();
  glTranslatef(0.0, 5.0, 0.0);
  Tabla();
  glPopMatrix();

  glPopMatrix();

  // << Modellezo programresz 
  
  // Flush drawing commands
  glutSwapBuffers();
}

// This function does any needed initialization on the rendering
// context. 
void SetupRC()
{
  // Black background
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f );

  glEnable(GL_DEPTH_TEST);

  //  fĂŠnyek engedĂŠlyezĂŠse
  glEnable( GL_LIGHTING );

  // globĂĄlis ambiens fĂŠny beĂĄllĂ­tĂĄsa
  glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambientLight );

  // pontszerĹą fĂŠnyforrĂĄs beĂĄllĂ­tĂĄsa
  glLightfv(GL_LIGHT0, GL_AMBIENT, l0_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, l0_diffuse);
  glEnable(GL_LIGHT0);

  // ambiens és diffúz anyagjellemzők színkövetés alapján
  glEnable( GL_COLOR_MATERIAL );
  glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );

  //  normĂĄlvektorok automatikus normalizĂĄlĂĄsa
  glEnable(GL_NORMALIZE);

  // textĂşrĂĄk beolvasĂĄsa
  texture1 = TextureLoad("metal_texture2.bmp", GL_FALSE, GL_LINEAR, GL_LINEAR, GL_REPEAT);
  texture2 = TextureLoad("korforgalom.bmp", GL_FALSE, GL_LINEAR, GL_LINEAR, GL_REPEAT);
  
}

void SpecialKeys(int key, int x, int y)
{
	if(key == GLUT_KEY_UP)
		xRot-= 5.0f;

	if(key == GLUT_KEY_DOWN)
		xRot += 5.0f;

	if(key == GLUT_KEY_LEFT)
		yRot -= 5.0f;

	if(key == GLUT_KEY_RIGHT)
		yRot += 5.0f;

	if(xRot > 356.0f)
		xRot = 0.0f;

	if(xRot < -1.0f)
		xRot = 355.0f;

	if(yRot > 356.0f)
		yRot = 0.0f;

	if(yRot < -1.0f)
		yRot = 355.0f;

 	if(key == GLUT_KEY_F1)
 		atlatsz = !atlatsz;

	// Refresh the Window
	glutPostRedisplay();
	}
  
void ChangeSizeOrtho(int w, int h)
{
  GLfloat nRange = 25.0f;
  
  // Prevent a divide by zero
  if(h == 0)
    h = 1;
  
  // Set Viewport to window dimensions
  glViewport(0, 0, w, h);
  
  // Reset projection matrix stack
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  
  // Establish clipping volume (left, right, bottom, top, near, far)
  if (w <= h) 
    glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
  else 
    glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
  
  // Reset Model view matrix stack
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void ChangeSizePerspective(GLsizei w, GLsizei h)
{
  GLfloat fAspect;
  
  // Prevent a divide by zero
  if(h == 0)
    h = 1;
  
  // Set Viewport to window dimensions
  glViewport(0, 0, w, h);
  
  fAspect = (GLfloat)w/(GLfloat)h;
  
  // Reset coordinate system
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  
  // Produce the perspective projection
  gluPerspective(60.0f,    // fovy
		 fAspect,  // aspect
		 10.0,     // zNear
		 100.0     // zFar
		 );
  gluLookAt(0.0, 0.0, 50.0, // eye
	    0.0, 0.0, 0.0,  // center
	    0.0, 1.0, 0.0   // up
	    );
  
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void ProcessMenu(int value)
{
  switch(value)
    {
    case 1:
      printf("1. menupont kivalasztva.\n");
      break;
      
    case 2:
      printf("2. menupont kivalasztva.\n");
      break;
      
    case 3:
      printf("Idle kiiratas bekapcsolva.\n");
      IdlePrint = 1;
      break;

    case 4:
      printf("Idle kiiratas kikapcsolva.\n");
      IdlePrint = 0;
      break;
      
    case 5:
      exit(0);

    default:
      break;
    }
  
  glutPostRedisplay();
}

int main(int argc, char* argv[])
{
  // >> Inicializalas

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  //glutInitWindowSize(300, 300);
  glutCreateWindow("GLUT Alap");

  // << Inicializalas

  // >> Callback fuggvenyek

  glutReshapeFunc(ChangeSizeOrtho); // Parhuzamos vetites
  //glutReshapeFunc(ChangeSizePerspective); // Perspektiv vetites

  glutSpecialFunc(SpecialKeys);
  glutDisplayFunc(RenderScene);
  
  // << Callback fuggvenyek

  // >> Menu

  IdleMenu = glutCreateMenu(ProcessMenu);
  glutAddMenuEntry("Idle kiiratas bekapcsolasa", 3);
  glutAddMenuEntry("Idle kiiratas kikapcsolasa", 4);

  MenuID = glutCreateMenu(ProcessMenu);
  glutAddMenuEntry("1. menupont", 1);
  glutAddMenuEntry("2. menupont", 2);
  glutAddSubMenu("Idle fuggveny", IdleMenu);
  glutAddMenuEntry("Kilepes", 5);
  glutAttachMenu(GLUT_RIGHT_BUTTON);

  // << Menu

  SetupRC();
  glutMainLoop();
  
  return 0;
}