#include <math.h>

#include "mathlib.h"
#include "DrawingsGL.h"

#include <GL/gl.h>

static void CubeVertices () {
	// front
	glVertex3f (0.5, -0.5, 0.5);
	glVertex3f (0.5, -0.5, -0.5);
	glVertex3f (0.5, 0.5, -0.5);
	glVertex3f (0.5, 0.5, 0.5);
	// back
	glVertex3f (-0.5, 0.5, 0.5);
	glVertex3f (-0.5, 0.5, -0.5);
	glVertex3f (-0.5, -0.5, -0.5);
	glVertex3f (-0.5, -0.5, 0.5);
	// left
	glVertex3f (-0.5, -0.5, 0.5);
	glVertex3f (0.5, -0.5, 0.5);
	glVertex3f (0.5, 0.5, 0.5);
	glVertex3f (-0.5, 0.5, 0.5);
	// right
	glVertex3f (-0.5, 0.5, -0.5);
	glVertex3f (0.5, 0.5, -0.5);
	glVertex3f (0.5, -0.5, -0.5);
	glVertex3f (-0.5, -0.5, -0.5);
	// bottom
	glVertex3f (-0.5, -0.5, -0.5);
	glVertex3f (0.5, -0.5, -0.5);
	glVertex3f (0.5, -0.5, 0.5);
	glVertex3f (-0.5, -0.5, 0.5);
	// top
	glVertex3f (-0.5, 0.5, 0.5);
	glVertex3f (0.5, 0.5, 0.5);
	glVertex3f (0.5, 0.5, -0.5);
	glVertex3f (-0.5, 0.5, -0.5);
}

void DrawWireCube () {
	glBegin (GL_LINE_STRIP);
		CubeVertices ();
	glEnd ();
}

void DrawSolidCube () {
	glBegin (GL_QUADS);
		CubeVertices ();
	glEnd ();
}

void DrawCircle () {
	int i, segments;
	segments = 20;
	double x, z, rad, drad;

	drad = (M_PI * 2) / segments;

	// Top
	glBegin (GL_TRIANGLE_FAN);
	glVertex3f (0., 0.5, 0.);
	for (i = 0; i <= segments; i++) {
		rad = drad * i;
		sincos (rad, &z, &x);
		glVertex3f (x * 0.5, 0., -z * 0.5);
	}
	glEnd ();
}

void DrawTorus () {
	int i, segments;
	segments = 20;
	double x, z, rad, drad;

	drad = (M_PI * 2) / segments;

	// Top
	glBegin (GL_TRIANGLE_FAN);
	glVertex3f (0., 0.5, 0.);
	for (i = 0; i <= segments; i++) {
		rad = drad * i;
		sincos (rad, &z, &x);
		glVertex3f (x * 0.5, 0.5, -z * 0.5);
	}
	glEnd ();

	// Bottom
	glBegin (GL_TRIANGLE_FAN);
	glVertex3f (0., -0.5, 0.);
	for (i = 0; i <= segments; i++) {
		rad = -drad * i;
		sincos (rad, &z, &x);
		glVertex3f (x * 0.5, -0.5, -z * 0.5);
	}
	glEnd ();

	// Sides
	glBegin (GL_QUAD_STRIP);
	for (i = 0; i <= segments; i++) {
		rad = drad * i;
		sincos (rad, &z, &x);
		glVertex3f (x * 0.5, 0.5, -z * 0.5);
		glVertex3f (x * 0.5, -0.5, -z * 0.5);
	}
	glEnd ();
}

void DrawPoint (float r, float x, float y, float z) {
	glPointSize (r);
	glBegin (GL_POINTS);
	glVertex3f (x, y, z);
	glEnd ();
	glPointSize (1.);
}

void DrawAxis() {
	glColor4f(1., 1., 1., 1.);

	glBegin(GL_LINES);
	glVertex3f(0., 0., 0.);
	glVertex3f(1., 0., 0.);
	glVertex3f(0., 0., 0.);
	glVertex3f(0., 1., 0.);
	glVertex3f(0., 0., 0.);
	glVertex3f(0., 0., 1.);
	glEnd();

	// X
	glPushMatrix();
	glTranslatef(1., 0., 0.);
	glColor3f(1., 0., 0.);
	glScalef(0.2, 0.1, 0.1);
	DrawCone(10);
	glTranslatef(0.4, 0., 0.);
	//		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'x');
	glPopMatrix();

	// Y
	glPushMatrix();
	glTranslatef(0., 1., 0.);
	glRotatef(90, 0., 0., 1.);
	glColor3f(0., 1., 0.);
	glScalef(0.2, 0.1, 0.1);
	DrawCone(10);
	glTranslatef(0.4, 0., 0.);
	//		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'y');
	glPopMatrix();

	// Z
	glPushMatrix();
	glTranslatef(0., 0., 1.);
	glRotatef(90, 0., -1., 0.);
	glColor3f(0., 0., 1.);
	glScalef(0.2, 0.1, 0.1);
	DrawCone(10);
	glPopMatrix();
}

void DrawDisc(float radius, int segments) {
	int i;
	float radiant;
	glBegin(GL_TRIANGLE_FAN);
	glVertex3f(0., 0., 0.);
	for (i = 0; i <= segments; i++) {
		radiant = (float) i * ((2 * M_PI) / (float) segments);
		glVertex3f(radius * cos(radiant), 0., radius * sin(radiant));
	}
	glEnd();
}

void DrawCircle(float radius, int segments) {
	int i;
	float radiant;
	glBegin(GL_LINE_STRIP);
	glVertex3f(0., 0., 0.);
	for (i = 0; i <= segments; i++) {
		radiant = (float) i * ((2 * M_PI) / (float) segments);
		glVertex3f(radius * cos(radiant), 0., radius * sin(radiant));
	}
	glEnd();
}

void DrawCone(int segments) {
	int i;
	float radiant;

	glBegin(GL_TRIANGLE_FAN);
	glVertex3f(1., 0., 0.);
	for (i = 0; i <= segments; i++) {
		radiant = (float) (i * (2. * M_PI) / (float) segments);

		glVertex3f(0., cos(radiant), sin(radiant));
	}

	glEnd();
}

void DrawVector(vector3d start, vector3d end) {
	vector3d direction (end - start);

	glColor3f(1., 1., 1.);
    glLineWidth(2.);
    glBegin(GL_LINES);
    glVertex3f(start[0], start[1], start[2]);
    glVertex3f(end[0], end[1], end[2]);
  glEnd();
  glLineWidth(1.);

	vector3d right;
	vector3d up;
	vector3d direction_norm = direction / direction.length();
	float cone_radius = 0.15;
	vector3d conebottom = end - direction_norm * 0.3;

	// Draw the tip
	if (fabs(direction[0]) > fabs(direction[1]) && fabs(direction[2]) > fabs(direction[1])) {
		up.setValues (0., 1., 0.);
		vector3d right = direction.cross (up);
		right = right / right.length();

		int i;
		int segments = 20.;
		float rad_delta = 2. * M_PI / (float) segments;
		double s,c;

		glColor3f(0.2, 0.8, 0.2);
		glPointSize (10.);
		glBegin (GL_TRIANGLE_FAN);
		glVertex3f (end[0], end[1], end[2]);

		for (i = 0; i <= segments; i ++) {
			sincos (i * rad_delta, &s, &c);
			glVertex3f (conebottom[0] + right[0] * cone_radius * c, conebottom[1] + s * cone_radius, conebottom[2] + right[2] * cone_radius * c);
		}

		glEnd ();
		glBegin (GL_TRIANGLE_FAN);
		glVertex3f (conebottom[0], conebottom[1], conebottom[2]);
		for (i = 0; i <= segments; i ++) {
			sincos (i * rad_delta, &s, &c);
			glVertex3f (conebottom[0] + right[0] * cone_radius * c, conebottom[1] + s * cone_radius, conebottom[2] + right[2] * cone_radius * c);
		}

		glEnd ();
	}
}

void DrawRect2D (float x, float y, float w, float h) {
	glBegin (GL_QUADS);
	glVertex3f (x, y, 0.);
	glVertex3f (x, y + h, 0.);
	glVertex3f (x + w, y + h, 0.);
	glVertex3f (x + w, y, 0.);
	glEnd ();
}
