# include <math.h>
# include <stdio.h>
# include "app_window.h"
# include "bicycle.h"

const double pi = 3.14159265;
//getter functions for bike positions
double Bicycle::getX() {
	return location.x;
}

double Bicycle::getY() {
	return location.y;
}

double Bicycle::getZ() {
	return location.z;
}
//this function draws the bicycle
//it makes the necessary rotations and translations then calls a function for each separate part of the bike
void drawBicycle(Bicycle *bicycle) {
	glPushMatrix();
		glTranslated(bicycle->getX(), bicycle->getY(), bicycle->getZ());
		glRotated(bicycle->rot * 180 / pi, 0, 1, 0);
		//Back wheel
		glPushMatrix();
			glTranslated(12, 0, 0);
			glRotated(bicycle->backWheel * 180 / pi, 0, 0, 1);
			wheel();
		glPopMatrix();
		//Fork
		glPushMatrix();
			setColor(1, 0, 0, 1);
			glTranslated(-12, 0, 0);
			glRotated(bicycle->turnAngle, cos(70 * pi / 180), sin(70 * pi / 180), 0);
			glRotated(-20, 0, 0, 1);
			fork();
			//Front wheel
			glPushMatrix();
				glRotated(bicycle->frontWheel * 180 / pi, 0, 0, 1);
				wheel();
			glPopMatrix();
			//handles
			glPushMatrix();
				glTranslated(0, 11.5, 0);
				setColor(1, 0, 0, 1);
				handles();
			glPopMatrix();
		glPopMatrix();
		//Frame
		setColor(1, 0, 0, 1);
		frame();
		//Pedals
		glPushMatrix();
			glTranslated(1, 0, 0);
			pedals((bicycle->pedal) * 180 / pi);
		glPopMatrix();
		//Seat
		glPushMatrix();
			setColor(0.2, 0.2, 0.2, 1);
			glTranslated(4.4, 9.4, 0);
			seat();
		glPopMatrix();
	glPopMatrix();
}
//draws the seat for the bike using a box
void seat() {
	glPushMatrix();
		glRotated(90, 0, 1, 0);
		glRotated(-90, 1, 0, 0);
		glTranslated(0, 3.5, 0.5);
		drawBox(1.5, 2.5, 0.5);
	glPopMatrix();
}
//this function draws the pedals of the bike using cylinders and boxes 
void pedals(double angle) {
	//inner cylinder connecting pedals
	glPushMatrix();
		setColor(0.3, 0.3, 0.3, 1.0);
		glRotated(90, 1, 0, 0);
		drawCylinder(7, 0.4);
	glPopMatrix();
	//pedal bars
	glPushMatrix();
		glRotated(angle, 0, 0, 1);
		//right pedal bar
		glPushMatrix();
			glTranslated(0, 1.75, -3);
			drawCylinder(3.5, 0.4);
		glPopMatrix();
		//left pedal bar
		glPushMatrix();
			glTranslated(0, -1.75, 3);
			drawCylinder(3.5, 0.4);
		glPopMatrix();
	glPopMatrix();
	//drawing the pedals out of boxes
	glPushMatrix();
		setColor(0.1, 0.1, 0.1, 1);
		glTranslated(-3.2*sin(angle*pi / 180), 3.2*cos(angle*pi / 180), -3.5);
		drawBox(1, 0.5, 1.5);
	glPopMatrix();
	glPushMatrix();
		glTranslated(3.2*sin(angle*pi / 180), -3.2*cos(angle*pi / 180), 3.5);
		drawBox(1, 0.5, 1.5);
	glPopMatrix();
}
//this function draws the fork of the bike using cylinders
void fork() {
	//cylinder connecting handle to fork
	glPushMatrix();
		glTranslated(0, 9.7, 0);
		drawCylinder(3.5, 0.7);
	glPopMatrix();
	//two cylinders going down to axle of front wheel in fork
	glPushMatrix();
		glTranslated(0, 4.0, 0);
		glPushMatrix();
			glTranslated(0, 0, -1.3);
			drawCylinder(7.8, 0.7);
		glPopMatrix();
		glPushMatrix();
			glTranslated(0, 0, 1.3);
			drawCylinder(7.8, 0.7);
		glPopMatrix();
	glPopMatrix();
	//connect front bars through the front wheel
	glPushMatrix();
		glTranslated(0, 8, 0);
		glRotated(90, 1, 0, 0);
		drawCylinder(4, 0.7);
	glPopMatrix();
	glPushMatrix();
		glRotated(90, 1, 0, 0);
		glTranslated(0, 0, 0);
		drawCylinder(4, 0.7);
	glPopMatrix();
}
//this fucntion draws the frame of the bike using cylinders
void frame() {
	//back bars to wheel
	glPushMatrix();
		glPushMatrix();
			glTranslated(12 * cos(-30 * pi / 180), 12 * sin(-30 * pi / 180) + 4, -2);
			glTranslated(1.6, 6, 0);
			drawCylinder(8.5, 0.7);
		glPopMatrix();
		glPushMatrix();
			glTranslated(12 * cos(-30 * pi / 180), 12 * sin(-30 * pi / 180) + 4, 2);
			glTranslated(1.6, 6, 0);
			drawCylinder(8.5, 0.7);
		glPopMatrix();
	glPopMatrix();
	//connect back bars
	glPushMatrix();
		glRotated(90, 1, 0, 0);
		glTranslated(12, 0, -8);
		drawCylinder(5, 0.7);
	glPopMatrix();
	//back bars to back wheel
	glPushMatrix();
		glRotated(90, 1, 0, 0);
		glTranslated(12, 0, 0);
		drawCylinder(5, 0.7);
	glPopMatrix();
	//main bar to handle
	glPushMatrix();
		glTranslated(1.5, 8, 0);
		glRotated(90, 0, 0, 1);
		drawCylinder(21, 0.7);
	glPopMatrix();
	//cylinder from peddle to seat
	glPushMatrix();
		glTranslated(1, 5.5, 0);
		drawCylinder(9, 0.7);
	glPopMatrix();
	//connect pedals with "gear"
	glPushMatrix();
		glTranslated(1, 0, 0);
		glRotated(90, 1, 0, 0);
		drawCylinder(1, 2);
	glPopMatrix();
}
//this functions draws the wheel of the bike using cylinders
void wheel() {
	//draw the wheel of the bike
	setColor(0.1, 0.1, 0.1, 1);
	glRotated(90, 1, 0, 0);
	drawCylinder(2, 6.5);
}
//this function draws the handles and grips for the bike using cylinders
void handles() {
	//draw handle
	glPushMatrix();
		glRotated(90, 1, 0, 0);
		drawCylinder(10, 0.7);
	glPopMatrix();
	//draw grips for handle
	glPushMatrix();
		setColor(0.1, 0.1, 0.1, 1);
		glTranslated(0,0,4);
		glRotated(90, 1, 0, 0);
		drawCylinder(3, 0.75);
		glTranslated(0,-1.2,0);
		drawCylinder(0.6, 1);
	glPopMatrix();
	glPushMatrix();
		setColor(0.1, 0.1, 0.1, 1);
		glTranslated(0, 0, -4);
		glRotated(90, 1, 0, 0);
		drawCylinder(3, 0.75);
		glTranslated(0, 1.2, 0);
		drawCylinder(0.6, 1);
	glPopMatrix();

}
//this function will update the bike with the given parameters in the bike object
void update(Bicycle *bicycle) {
	double frontY = 0;
	double frontX = 0;
	double backX = 0;
	double backY = 0;
	//angle of the bike itself
	double bikeAngle = bicycle->rot;
	//angle of the turn on the front wheel
	double turnAngle = bicycle->turnAngle;
	//speed of the bike relative to the angle at which the bike is turned
	double speed = (bicycle->speed)*(1 - (turnAngle*turnAngle) / 6400.0);
	double angle1 = turnAngle*pi / 180;
	//calculates how much to move
	//if the front wheel is turned
	if (angle1 != 0) {
		double rAngle = 24 * tan(pi / 2 - angle1);
		double angle2 = speed / rAngle;
		backX = rAngle*sin(angle2);
		backY = rAngle*(1 - cos(angle2));
		double bAngle = rAngle / sin(pi / 2 - angle1);
		frontY = rAngle - bAngle*cos(angle1 + angle2);
		frontX = bAngle*sin(angle1 + angle2) - 24.0;
	}
	//if front wheen isnt turned
	else {
		frontX = speed;
		backX = speed;
	}
	//updates front, back wheel and pedals based on speed bike is going at
	bicycle->pedal += speed / 20;
	bicycle->backWheel += minmax((speed / 15), -0.1, 0.1);
	double speedIncrement = minmax((sqrt(frontX*frontX + frontY*frontY) / 15) * 1, -0.1, 0.1);
	//changes directions of rotation animation for wheel if going forward of backward
	if (speed>0)
		bicycle->frontWheel += speedIncrement;
	else
		bicycle->frontWheel -= speedIncrement;
	//resets pedals once they make a full rotation
	if (bicycle->pedal>2 * pi)
		bicycle->pedal -= 2 * pi;
	//adjust the front change to match the new location of the front wheel
	frontX += 24.0;
	//calculate the center of the bike based on wheel positions
	double centerX = backX + (frontX - backX) / 2.0;
	double centerY = backY + (frontY - backY) / 2.0;
	//calculate how much to change bike angle
	double bikeAngleChange = atan2(frontY - backY, frontX - backX);
	//move coord system to center bike
	centerX -= 12;
	//rotate the matrices to adjust for bike location
	double changeX = centerX*cos(bikeAngle - bikeAngleChange) - centerY*sin(bikeAngle - bikeAngleChange);
	double changeY = centerX*sin(bikeAngle - bikeAngleChange) + centerY*cos(bikeAngle - bikeAngleChange);
	//update the location and rotation of the bike
	bicycle->location.x -= changeX;
	bicycle->location.z += changeY;
	bicycle->rot += bikeAngleChange;
}
//this functions returns x bound between an min and a max, used in update function
double minmax(double x, double min, double max) {
	if (x<min)
		return min;
	if (x>max)
		return max;
	else
		return x;
}