/* 
 * 3D starfield with unusual objects, movement and shooting :)
 * by andrey mirtchovski (aam396@mail.usask.ca)
 *
 */


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


int w=500, h=500;

enum {
    SCROLL_LEFT = 1,
    SCROLL_RIGHT,
    SCROLL_UP,
    SCROLL_DOWN
} type = SCROLL_RIGHT;


typedef struct _star {
    float x, y, z;
    float vx, vy, vz;
	
    float r, g, b;				// color

	int rotx, roty, rotz;		// rotation descriptor
	int drotx, droty, drotz;	// rotation vector

	int type;	/* used with spheres -- type of object to display */
} star;


star *stars = NULL;
star shoot[20];
star sphere[10];

int num_stars = 500;

int spin_x=0, spin_y=0, old_x = 0, old_y = 0;

/* take care of moving around with the mouse */
float move_x=0, move_y=0;

int in_window = 0;

int cnt = 100;
int add = 1;
int shot = 0;
int spheres = 1;
int render_stars = 1;
int render_help = 1;

int num_shot = 0;		/* how many shots were there  == maximum 20!!*/

void init() {
	int i;

	glClearColor(0, 0, 0, 0);
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
   // glOrtho(-1, 1, -1, 1, -1, 1);
	glOrtho(-1, 1,-1, 1,-1, 1000);
//	glFrustum(-1, 1,-1, 1, 0.1, 2);
    glMatrixMode(GL_MODELVIEW);

	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_DEPTH_TEST);

	for(i = 0; i < num_stars; i++) {
		stars[i].x = (float)rand()/(float)(RAND_MAX) - 0.5;
		stars[i].y = (float)rand()/(float)(RAND_MAX) - 0.5;
		stars[i].z = (float)rand()/(float)(RAND_MAX) - 0.5;

		stars[i].vx =0; // (float)rand()/(float)(RAND_MAX*500) - 1/250;
		stars[i].vy =0; // (float)rand()/(float)(RAND_MAX*500) - 1/250;
		stars[i].vz = (float)rand()/(float)(RAND_MAX*500) - 1/250;

		stars[i].r = 300*stars[i].vz;
		stars[i].g = 0; //(float)rand()/(float)(RAND_MAX) + stars[i].vz;
		stars[i].b = 0;//(float)rand()/(float)(RAND_MAX) + stars[i].vz;

	}

	for(i = 0; i < 10; i++) {
		/* make all the spheres */

		sphere[i].x = (float)rand()/(float)(RAND_MAX) - 0.5;
		sphere[i].y = (float)rand()/(float)(RAND_MAX) - 0.5;
		sphere[i].z = (float)rand()/(float)(RAND_MAX) - 0.5;

		sphere[i].vx =0; // (float)rand()/(float)(RAND_MAX*500) - 1/250;
		sphere[i].vy =0; // (float)rand()/(float)(RAND_MAX*500) - 1/250;
		sphere[i].vz = (float)rand()/(((float)RAND_MAX)*100.0);

		sphere[i].r = 0;
		sphere[i].g = 0; //(float)rand()/(float)(RAND_MAX) + sphere[i].vz;
		sphere[i].b = 1 - sphere[i].vz;//(float)rand()/(float)(RAND_MAX) + sphere[i].vz;

		sphere[i].rotx = rand()%10 - 5;
		sphere[i].roty = rand()%10 - 5;
		sphere[i].rotz = rand()%10 - 5;

		sphere[i].drotx = rand()%10 - 5;
		sphere[i].droty = rand()%10 - 5;
		sphere[i].drotz = rand()%10 - 5;
		
		sphere[i].type = rand()%4;
	}

	for(i = 0; i < 20; i++) {
		shoot[i].z = 0.001;

		shoot[i].vx =0; // (float)rand()/(float)(RAND_MAX*500) - 1/250;
		shoot[i].vy =0; // (float)rand()/(float)(RAND_MAX*500) - 1/250;
		shoot[i].vz = -0.008;

		shoot[i].r = 0;
		shoot[i].g = 1; //(float)rand()/(float)(RAND_MAX) + shoot[i].vz;
		shoot[i].b = 0;//(float)rand()/(float)(RAND_MAX) + shoot[i].vz;
	
	}

	glPointSize(2);
}


void
display(void)
{
    int i, j;
	float pos = 4.5;
	

	char *help[] = {"commands:", 
					"t -- toggle star rendering",
					"s -- toggle wire objects",
					"a -- toggle type of star rendering (funny)",
					"h -- toggle this menu",
					"f -- switch to full-screen mode",
					"space -- shoot at nothing",
					"mouse -- shoot at nothing",
					"ESC -- quit"};

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);


	if(render_help) {
		/* help menu was requested */
		glPushMatrix();
		glColor3f(0.5, 0.5, 0.5);
		glRasterPos3f(-4, pos, -0.5);
		for(j = 0; j < 9; j++) {
			for(i = 0; i < strlen(help[j]); i++) {
				glutBitmapCharacter(GLUT_BITMAP_8_BY_13, help[j][i]);
			}
			pos -= 0.2;
			glRasterPos3f(-4, pos, -0.5);
		}
		glPopMatrix();
	}

	

	glPushMatrix();

//	glRotatef(spin_x, 0, 1, 0);
//  glRotatef(spin_y, 1, 0, 0);

	glTranslatef(-move_x, move_y, 0);

	glColor3f(0.3, 0.3, 0.3);
	glutWireCube(1.0);

	if(render_stars)
    for (i = 0; i < num_stars; i++) {
		glBegin(GL_LINE_STRIP);
		glColor3f(stars[i].r, stars[i].g, stars[i].b);
		glVertex3f(stars[i].x, stars[i].y, stars[i].z);
		glColor3f(0, 0, 0);
		if(add)
			glVertex3f(stars[i].x, stars[i].y, stars[i].z - 20*stars[i].vz);
		else
			glVertex3f(stars[i].x, stars[i].y, stars[i].z * 100 *stars[i].vz);
		glEnd();
    }

	if(spheres)
	for (i = 0; i < 10; i++) {

		glPushMatrix();
		
		glColor3f(sphere[i].r, sphere[i].g, sphere[i].b);

		glTranslatef(sphere[i].x, sphere[i].y, sphere[i].z);

		glRotatef(sphere[i].rotx, 1, 0, 0);
		glRotatef(sphere[i].roty, 0, 1, 0);
		glRotatef(sphere[i].rotz, 0, 0, 1);

		switch(sphere[i].type) {
			case 0: glutWireSphere(0.03, 5, 5);
				break;
			case 1: glutWireCone(0.03, 0.03, 5, 5);
				break;
			case 2: glutWireCube(0.03);
				break;
			case 3: glutWireTorus(0.03, 0.06, 5, 5);
				break;
	
		}
		glPopMatrix();
    }
	

	if(shot) {
		for(i = 0; i < 20 ; i++) {
			if(shoot[i].z > 0.0)
				continue;
			glLineWidth(6);
			glBegin(GL_LINE_STRIP);
				glColor3f(shoot[i].r, shoot[i].g, shoot[i].b);
				glVertex3f(shoot[i].x-0.001, shoot[i].y, shoot[i].z);
				glColor3f(0, 0, 0.3);
				glVertex3f(shoot[i].x-0.001, shoot[i].y, shoot[i].z + shoot[i].vz);
			glEnd();
			glBegin(GL_LINE_STRIP);
				glColor3f(shoot[i].r, shoot[i].g, shoot[i].b);
				glVertex3f(shoot[i].x+0.001, shoot[i].y, shoot[i].z);
				glColor3f(0, 0, 0.3);
				glVertex3f(shoot[i].x+0.001, shoot[i].y, shoot[i].z + shoot[i].vz);
			glEnd();
			glLineWidth(1);
		}
	}
    
	glPopMatrix();


    glutSwapBuffers();
}

void motion(x, y) {

	spin_x = old_x - x;
	spin_y = old_y - y;

	glutPostRedisplay();

}

void mouse(int button, int state, int x, int y)
{
	int i;

	if(state == GLUT_DOWN) {
		switch(button) {
			case 0:
				if(++num_shot < 20) {
					for(i = 0; i < 20; i++) {
						if(shoot[i].z > 0.0) {
							shoot[i].x = move_x;
							shoot[i].y = -move_y+0.1;
							shoot[i].z = 0;
							break;
						}
					}
				} else {
					num_shot = 19;
				}
				shot = 1;

				break;
		}
	} 

	glutPostRedisplay();

}
void idle() {
	int i; 

	if(render_stars)
	for(i = 0; i < num_stars; i++) {
		stars[i].z += stars[i].vz;
		stars[i].z = stars[i].z > 0 ? -0.5 : stars[i].z;
	}

	if(spheres)
	for(i = 0; i < 10; i++) {
		sphere[i].z += sphere[i].vz;
		sphere[i].rotx += sphere[i].drotx;
		sphere[i].roty += sphere[i].droty;
		sphere[i].rotz += sphere[i].drotz;
		sphere[i].z = sphere[i].z > 0.5 ? -0.5 : sphere[i].z;
	}

	if(shot) {
		for(i = 0; i < 20; i++) {
			if(shoot[i].z <= 0.0) {
				shoot[i].z += shoot[i].vz;
			}
			if(shoot[i].z < -0.5) {
				shoot[i].z = 0.001;
				num_shot--;
			}
			if(!num_shot) {
				shot = 0;
				break;
			}
		}
	}

//	cnt = 500 + 250*sin(cnt);
	glutPostRedisplay();
}

void passive(int x, int y) {

	if(x < w && x > 0)
		move_x = (float)x/(float)w - 0.5;
	if(y < h && y > 0)
		move_y = (float)y/(float)h - 0.5;


	glutPostRedisplay();

}

void entry(int ent) {
	in_window = ent == GLUT_ENTERED ? 1 : 0;
}

void
keyboard(unsigned char key, int x, int y)
{
	int i;
	switch (key) {
		case 27: exit(0);
				break;
		case 'f': 
				glutSetCursor(GLUT_CURSOR_NONE);
				glutFullScreen();
				break;
		case 'a': add = !add;
				break;
		case 's': spheres = !spheres;
				break;
		case 't': render_stars = !render_stars;
				break;
		case 'h': render_help = !render_help;
				break;
		case ' ':
				if(++num_shot < 20) {
					for(i = 0; i < 20; i++) {
						if(shoot[i].z > 0.0) {
							shoot[i].x = move_x;
							shoot[i].y = -move_y+0.1;
							shoot[i].z = 0;
							break;
						}
					}
				} else {
					num_shot = 19;
				}
				shot = 1;

				break;						
	}
}

void reshape(int width, int height) {
	w = width;
	h = height;
	glClearColor(0, 0, 0, 0);
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
	glOrtho(-1, 1,-1, 1,-1, 1000);
	glFrustum(-1, 1,-1, 1, 0.1, 2);
    glMatrixMode(GL_MODELVIEW);
}

int main(int argc, char** argv)
{
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(w, h);
    glutInit(&argc, argv);

    glutCreateWindow("Starfield");
    glutDisplayFunc(display);
    glutIdleFunc(idle);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutPassiveMotionFunc(passive);
	glutEntryFunc(entry);
	glutKeyboardFunc(keyboard);
	glutReshapeFunc(reshape);

    stars = (star*)malloc(sizeof(star) * num_stars);

	init();

    glutMainLoop();
    return 0;
}


