#include <stdlib.h>
#include <stdio.h>
#include <gl/glut.h>
#include <math.h>
#include <sys\timeb.h>

#define XWINSIZE 800 
#define YWINSIZE 600 
#define XWINPOS 100
#define YWINPOS 100

#define SPEED 0.15f


float q=20.0;


double tint=5.0;
float xi=3.0;
float yi=9.0;
float zi=12.0;
float vxi=-6.0;
float vyi=3.0;
float vzi=12.0;
float vy, vx, vz;
float yaccel=0;
float xaccel=0;
float zaccel=0;
float dampy=1;
float dampx=1;
float dampz=1;

double tint2=5.0;
float xi2=8.0;
float yi2=4.0;
float zi2=3.0;
float vxi2=7.0;
float vyi2=7.0;
float vzi2=-12.0;
float vy2, vx2, vz2;
float yaccel2=0;
float xaccel2=0;
float zaccel2=0;

float temp;
float coldelay=20.0;


typedef struct
{
	float x;
	float y;
	float z;
} vector;

struct camera
{
	vector pos;
	vector view;
	vector up;
};

struct camera Camera;
int frame = 0, time, timebase = 0;
float t;

struct camera setcamposition(struct camera Cam,
		float posx, float posy, float posz,
		float viewx, float viewy, float viewz,
		float upx, float upy, float upz)
{
	Cam.pos.x = posx;
	Cam.pos.y = posy;
	Cam.pos.z = posz;
	
	Cam.view.x = viewx;
	Cam.view.y = viewy;
	Cam.view.z = viewz;

	Cam.up.x = upx;
	Cam.up.y = upy;
	Cam.up.z = upz;

	return Cam;
}

struct camera movecamera(struct camera Cam, float speed)
{
	vector vpvec;
	vpvec.x = Cam.view.x - Cam.pos.x;
	vpvec.y = Cam.view.y - Cam.pos.y;
	vpvec.z = Cam.view.z - Cam.pos.z;

	Cam.pos.x = Cam.pos.x + vpvec.x * speed;
	Cam.pos.z = Cam.pos.z + vpvec.z * speed;
	
	Cam.view.x = Cam.view.x + vpvec.x * speed;
	Cam.view.z = Cam.view.z + vpvec.z * speed;

	return Cam;
}

struct camera strafecamera(struct camera Cam, float speed)
{
	vector vpvec;
	vpvec.x = Cam.view.x - Cam.pos.x;
	vpvec.y = Cam.view.y - Cam.pos.y;
	vpvec.z = Cam.view.z - Cam.pos.z;

	Cam.pos.x = Cam.pos.x - vpvec.z * speed;
	Cam.pos.z = Cam.pos.z + vpvec.x * speed;

	Cam.view.x = Cam.view.x - vpvec.z * speed;
	Cam.view.z = Cam.view.z + vpvec.x * speed;

	return Cam;
}

struct camera rotatecamera(struct camera Cam, float speed)
{
	vector vpvec;
	vpvec.x = Cam.view.x - Cam.pos.x;
	vpvec.y = Cam.view.y - Cam.pos.y;
	vpvec.z = Cam.view.z - Cam.pos.z;

	Cam.view.z = (float) (Cam.pos.z + sin(speed) * vpvec.x + cos(speed) * vpvec.z);
	Cam.view.x = (float) (Cam.pos.x + cos(speed) * vpvec.x - sin(speed) * vpvec.z);

	return Cam;
}

struct camera upcamera(struct camera Cam, float speed)
{
	Cam.view.y = Cam.view.y + 2 * speed; 

	if((Cam.view.y - Cam.pos.y) > 8) Cam.view.y = Cam.pos.y + 8;
	if((Cam.view.y - Cam.pos.y) < -8) Cam.view.y = Cam.pos.y - 8;

	return Cam;
}

struct camera flycamera(struct camera Cam, float speed)
{
	Cam.view.y = Cam.view.y + speed;
	Cam.pos.y = Cam.pos.y + speed;

	return Cam;
}

void init()
{
	glShadeModel(GL_SMOOTH);
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0f, XWINSIZE/YWINSIZE, 0.1f, 100.0f);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	Camera = setcamposition(Camera, 0, 2.5f, 5, 0, 2.5f, 0, 0, 1, 0);
}


void reshape(int w, int h)
{
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//gluPerspective(45.0f, XWINSIZE/YWINSIZE, 0.1f, 100.0f); 
	gluPerspective(45.0f, w/h, 0.1f, 100.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(Camera.pos.x, Camera.pos.y, Camera.pos.z,
		  Camera.view.x, Camera.view.y, Camera.view.z,
		  Camera.up.x, Camera.up.y, Camera.up.z);
}

void drawgrid()
{
	float i;

	glBegin(GL_LINES);
	for(i = 0; i <= q; i += 4)
	{
		glColor3f(0.1f, 0.1f, 0.1f);
		glVertex3f(0, 0, i);
		glVertex3f(q, 0, i);
		glVertex3f(i, 0, 0);
		glVertex3f(i, 0, q);
	}
	glEnd();
}

void drawviewpoint()
{
	glColor3f(1.0, 1.0, 1.0);
        glPointSize(3.0);

	glBegin(GL_POINTS);
		glVertex3f(Camera.view.x, Camera.view.y, Camera.view.z);
	glEnd();	
}

/*
void drawfunc()
{
	float x;
	int i = 25;

	glColor3f(1, 1, 1);
	glBegin(GL_LINE_STRIP);
	for(x = -i; x <= i; x += .01)
	{
		glVertex3f(x, sin(x), -cos(x));
		glVertex3f(x, -sin(x), cos(x));
		glVertex3f(x, exp(-pow(x, 2)), 0);	
	}
	glEnd();
}
*/





void display(void)
{
	
float pos[3];
float pos2[3];

struct timeb a;
struct timeb c;
double tchange;


struct timeb a2;
struct timeb c2;
double tchange2;

/*time initializer and changer*/
if ((tint==5.0))
{
	ftime(& a);
	tint=a.time+a.millitm/1000.0;
	tchange=tint;
}
else
{
	ftime(& c);
	tchange=c.time+c.millitm/1000.0;
}

if ((tint2==5.0))
{
	ftime(& a2);
	tint2=a2.time+a2.millitm/1000.0;
	tchange2=tint2;
}
else
{
	ftime(& c2);
	tchange2=c2.time+c2.millitm/1000.0;
}


/*kinematics for motion*/
pos[0]=xi;
pos[1]=yi;
pos[2]=zi;
pos[0]=pos[0]+(vxi*(tchange-tint)+xaccel*(tchange-tint)*(tchange-tint)/2.0);
pos[1]=pos[1]+(vyi*(tchange-tint)+yaccel*(tchange-tint)*(tchange-tint)/2.0);
pos[2]=pos[2]+(vzi*(tchange-tint)+zaccel*(tchange-tint)*(tchange-tint)/2.0);

pos2[0]=xi2;
pos2[1]=yi2;
pos2[2]=zi2;
pos2[0]=pos2[0]+(vxi2*(tchange2-tint2)+xaccel2*(tchange2-tint2)*(tchange2-tint2)/2.0);
pos2[1]=pos2[1]+(vyi2*(tchange2-tint2)+yaccel2*(tchange2-tint2)*(tchange2-tint2)/2.0);
pos2[2]=pos2[2]+(vzi2*(tchange2-tint2)+zaccel2*(tchange2-tint2)*(tchange2-tint2)/2.0);


/*collision and kinematics for bounce*/






if(pos[2]<0.0)
{
vz=vzi+zaccel*(tchange-tint);
vzi=-dampz*vz;
vy=vyi+yaccel*(tchange-tint);
vyi=vy;
vx=vxi+xaccel*(tchange-tint);
vxi=vx;
tint=5.0;
zi=0.0;
yi=pos[1];
xi=pos[0];

}

if(pos2[2]<0.0)
{
vz2=vzi2+zaccel2*(tchange2-tint2);
vzi2=-dampz*vz2;
vy2=vyi2+yaccel2*(tchange2-tint2);
vyi2=vy2;
vx2=vxi2+xaccel2*(tchange2-tint2);
vxi2=vx2;
tint2=5.0;
zi2=0.0;
yi2=pos2[1];
xi2=pos2[0];

}
if(pos[2]>q)
{
vz=vzi+zaccel*(tchange-tint);
vzi=-dampz*vz;
vy=vyi+yaccel*(tchange-tint);
vyi=vy;
vx=vxi+xaccel*(tchange-tint);
vxi=vx;
tint=5.0;
zi=q;
yi=pos[1];
xi=pos[0];

}

if(pos2[2]>q)
{
vz2=vzi2+zaccel2*(tchange2-tint2);
vzi2=-dampz*vz2;
vy2=vyi2+yaccel2*(tchange2-tint2);
vyi2=vy2;
vx2=vxi2+xaccel2*(tchange2-tint2);
vxi2=vx2;
tint2=5.0;
zi2=q;
yi2=pos2[1];
xi2=pos2[0];

}


if(pos[1]<0.0)
{
vz=vzi+zaccel*(tchange-tint);
vzi=vz;
vy=vyi+yaccel*(tchange-tint);
vyi=-dampy*vy;
vx=vxi+xaccel*(tchange-tint);
vxi=vx;
tint=5.0;
zi=pos[2];
yi=0.0;
xi=pos[0];

}

if(pos2[1]<0.0)
{
vz2=vzi2+zaccel2*(tchange2-tint2);
vzi2=vz2;
vy2=vyi2+yaccel2*(tchange2-tint2);
vyi2=-dampy*vy2;
vx2=vxi2+xaccel2*(tchange2-tint2);
vxi2=vx2;
tint2=5.0;
zi2=pos2[2];
yi2=0.0;
xi2=pos2[0];

}
if(pos[1]>q)
{
vz=vzi+zaccel*(tchange-tint);
vzi=vz;
vy=vyi+yaccel*(tchange-tint);
vyi=-dampy*vy;
vx=vxi+xaccel*(tchange-tint);
vxi=vx;
tint=5.0;
zi=pos[2];
yi=q;
xi=pos[0];
}
if(pos2[1]>q)
{
vz2=vzi2+zaccel2*(tchange2-tint2);
vzi2=vz2;
vy2=vyi2+yaccel2*(tchange2-tint2);
vyi2=-dampy*vy2;
vx2=vxi2+xaccel2*(tchange2-tint2);
vxi2=vx2;
tint2=5.0;
zi2=pos2[2];
yi2=q;
xi2=pos2[0];

}
if(pos[0]<0.0)
{
vz=vzi+zaccel*(tchange-tint);
vzi=vz;
vy=vyi+yaccel*(tchange-tint);
vyi=vy;
vx=vxi+xaccel*(tchange-tint);
vxi=-dampx*vx;
tint=5.0;
zi=pos[2];
yi=pos[1];
xi=0.0;

}

if(pos2[0]<0.0)
{
vz2=vzi2+zaccel2*(tchange2-tint2);
vzi2=vz2;
vy2=vyi2+yaccel2*(tchange2-tint2);
vyi2=vy2;
vx2=vxi2+xaccel2*(tchange2-tint2);
vxi2=-dampx*vx2;
tint2=5.0;
zi2=pos2[2];
yi2=pos2[1];
xi2=0.0;

}
if(pos[0]>q)
{
vz=vzi+zaccel*(tchange-tint);
vzi=vz;
vy=vyi+yaccel*(tchange-tint);
vyi=vy;
vx=vxi+xaccel*(tchange-tint);
vxi=-dampx*vx;
tint=5.0;
zi=pos[2];
yi=pos[1];
xi=q;

}

if(pos2[0]>q)
{
vz2=vzi2+zaccel2*(tchange2-tint2);
vzi2=vz2;
vy2=vyi2+yaccel2*(tchange2-tint2);
vyi2=vy2;
vx2=vxi2+xaccel2*(tchange2-tint2);
vxi2=-dampx*vx2;
tint2=5.0;
zi2=pos2[2];
yi2=pos2[1];
xi2=q;

}


if((fabs(pos[0]-pos2[0])<.3) && (fabs(pos[1]-pos2[1])<.3) && (fabs(pos[2]-pos2[2])<.3) && coldelay>=3.0)
{
vz2=vzi2+zaccel2*(tchange2-tint2);
vzi2=vz2;
vz=vzi+zaccel*(tchange-tint);
vzi=vz;
vy2=vyi2+yaccel2*(tchange2-tint2);
vyi2=vy2;
vy=vyi+yaccel*(tchange-tint);
vyi=vy;
vx=vxi+xaccel*(tchange-tint);
vxi=vx;
vx2=vxi2+xaccel2*(tchange2-tint2);
vxi2=vx2;



temp=vyi2;
vyi2=dampy*vyi;
vyi=dampy*temp;

temp=vxi2;
vxi2=dampx*vxi;
vxi=dampx*temp;

temp=vzi2;
vzi2=dampz*vzi;
vzi=dampz*temp;


xi=pos[0];
yi=pos[1];
xi2=pos2[0];
yi2=pos2[1];
zi=pos[2];
zi2=pos2[2];

tint=5.0;
tint2=5.0;


coldelay=0.0;



}
coldelay=coldelay+1;

	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	gluLookAt(Camera.pos.x, Camera.pos.y, Camera.pos.z,
		  Camera.view.x, Camera.view.y, Camera.view.z,
		  Camera.up.x, Camera.up.y, Camera.up.z);

	drawgrid();
	drawviewpoint();

	

	glPushMatrix();
	glColor3f(0.0f, 0.7f, 1.0f);
	glTranslatef(pos[0],pos[1],pos[2]);
	glutSolidSphere(0.25f, 100.0, 16.0);
	glFlush();
	glPopMatrix();





	glPushMatrix();
	glColor3f(1.0f, 0.7f, 1.0f);
	glTranslatef(pos2[0],pos2[1],pos2[2]);
	glutSolidSphere(0.25f, 100.0, 16.0);
	glFlush();
	glPopMatrix();
	glColor3f(0.0,1.0,0.0);
	

/*cube*/

	glBegin(GL_LINES);
	
	glVertex3f(0,0,0);
	glVertex3f(0,q,0);
	
	glVertex3f(0,q,0);
	glVertex3f(0,q,q);
	
	glVertex3f(0,q,q);
	glVertex3f(0,0,q);
	
	glVertex3f(0,0,q);
	glVertex3f(0,0,0);
	
	


	glVertex3f(q,0,0);
	glVertex3f(q,q,0);
	
	glVertex3f(q,q,0);
	glVertex3f(q,q,q);
	
	glVertex3f(q,q,q);
	glVertex3f(q,0,q);
	
	glVertex3f(q,0,q);
	glVertex3f(q,0,0);
	
	


	glVertex3f(0,0,0);
	glVertex3f(q,0,0);
	
	glVertex3f(0,0,q);
	glVertex3f(q,0,q);
	
	glVertex3f(0,q,q);
	glVertex3f(q,q,q);
	
	glVertex3f(0,q,0);
	glVertex3f(q,q,0);
	
	
	glEnd();


	glFlush();
	glutSwapBuffers();
}

void idle(void)
{
	/* FPS CODE!
	frame++;
	time = glutGet(GLUT_ELAPSED_TIME);

	if(time - timebase > 1000)
	{
		printf("FPS: %f\n", frame*1000.0/(time - timebase));
		timebase = time;
		frame = 0;
	}
	*/

	glutPostRedisplay();
}

/*
void motion()
{
	glutPostRedisplay();
}
*/

void keyboard(unsigned char key, int x, int y)
{
	switch(key)
	{
		case 'i':
			Camera = upcamera(Camera, +SPEED);
			break;
		case 'k':
			Camera = upcamera(Camera, -SPEED);
			break;
		case 'l':
			Camera = rotatecamera(Camera, +SPEED);
			break;
		case 'j': 
			Camera = rotatecamera(Camera, -SPEED);	
			break;
		case 'w':
			Camera = movecamera(Camera, +SPEED);
			break;
		case 's':
			Camera = movecamera(Camera, -SPEED);
			break;
		case 'a':
			Camera = strafecamera(Camera, -SPEED);
			break;
		case 'd':
			Camera = strafecamera(Camera, +SPEED);
			break;
		case 'r':
			Camera = flycamera(Camera, +SPEED);
			break;
		case 'f':
			Camera = flycamera(Camera, -SPEED);
			break;
		case 'q':
			exit(0);
			break;
	}
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(XWINSIZE, YWINSIZE);
	glutInitWindowPosition(XWINPOS, YWINPOS);
	glutCreateWindow("Jeff Project");

	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	//glutMotionFunc(motion);
	glutIdleFunc(idle);
	//glutMouseFunc(mouse);
	glutKeyboardFunc(keyboard);
	glutMainLoop();

	return 0;
}
