

#include <stdio.h>
#include <allegro.h>
#include "alleggl.h"
#include <math.h>
#include <SOIL.h>
#include <fmod.h>
#define TURN_SPEED .1
#define MOVE_SPEED 1.0
#define NUM_TREES  100000
#define WORLD_SIZE 200.0
#define HAYSTACK 100.0
#define FRAND(x) (((rand()%(int)x)*1024.0)/1024.0)
#define DEG(n)   ((n) * 180.0 / M_PI)
//#define SGN(n)  (n>=0 ? 1 : -1)
#define WALK 1
#define IDLE 2
#define STOP 3
#define HEIGHT 10.0
#define HEIGHTF 40.0
#define MAXV 2048
#define NUM_CLOUDS 20
#define CURSOR_SIZE .04

float tree[NUM_TREES][2][3],colors[NUM_TREES][3],clouds[NUM_CLOUDS][6][3],needle[2][6];
int frames = 0,state,recolour=0,gameover,intro=1;
volatile int tim;
float playerx,playery,playerz,lookx,looky,lookz,introroll;
float final_point[3];
float point_xformed[3];
float lookf;
char temp[256];
GLuint cloud,fence,introtex,outro;
BITMAP *mybmp;
FSOUND_STREAM *theme,*intromus,*outromus;

void seek()
{
  float d,x,y,z;
  x=final_point[0]-((needle[0][0]+needle[1][0])/2);
  y=final_point[1]-((needle[0][1]+needle[1][1])/2);
  z=final_point[2]-((needle[0][2]+needle[1][2])/2);
  d=sqrt(x*x+y*y+z*z);
   if (d<10.0) {
     gameover=1;

      FSOUND_Stream_Stop(theme);
      FSOUND_Stream_Play (0,outromus);
    }
}

void grab_mouse( int x, int y )
{
    float value_fov =(M_PI/2);
    float value_aspect = 1.3333;
    float half_window_width = 320.0;
    float half_window_height = 240.0;
    float modifier_x;
    float modifier_y;
    float point[3];
    float point_dist = 10.0;
    float camera_origin[3];
    GLfloat pulledMatrix[16];
    float color[4] = { 0.0, 1.0, 0.0, 1.0 };
    modifier_x = tan( value_fov * 0.5 )
        * (( 1.0 - x / half_window_width ) * ( value_aspect ) );
    modifier_y = tan( value_fov * 0.5 )
        * -( 1.0 - y / half_window_height );
    point[0] = modifier_x * point_dist;
    point[1] = modifier_y * point_dist;
    point[2] = point_dist;
    glGetFloatv(GL_MODELVIEW_MATRIX, pulledMatrix);
    camera_origin[0] = -(
        pulledMatrix[0] * pulledMatrix[12] +
        pulledMatrix[1] * pulledMatrix[13] +
        pulledMatrix[2] * pulledMatrix[14]);
    camera_origin[1] = -(
        pulledMatrix[4] * pulledMatrix[12] +
        pulledMatrix[5] * pulledMatrix[13] +
        pulledMatrix[6] * pulledMatrix[14]);
    camera_origin[2] = -(
        pulledMatrix[8] * pulledMatrix[12] +
        pulledMatrix[9] * pulledMatrix[13] +
        pulledMatrix[10] * pulledMatrix[14]);
    point_xformed[0] = -(
        pulledMatrix[0] * point[0] +
        pulledMatrix[1] * point[1] +
        pulledMatrix[2] * point[2]);
    point_xformed[1] = -(
        pulledMatrix[4] * point[0] +
        pulledMatrix[5] * point[1] +
        pulledMatrix[6] * point[2]);
    point_xformed[2] = -(
        pulledMatrix[8] * point[0] +
        pulledMatrix[9] * point[1] +
        pulledMatrix[10] * point[2]);
    final_point[0] = (point_xformed[0]) + camera_origin[0];
    final_point[1] = (point_xformed[1]) + camera_origin[1];
    final_point[2] = (point_xformed[2]) + camera_origin[2];

}
void draw_tree()
{
  int c,b,n;

  //glPopMatrix();
  //glPushMatrix();
  //glTranslatef(tree[n][0],0,tree[n][1]);
  /*
  glBegin(GL_QUADS)
  glBegin(GL_LINES);
    for (c=0;c<NUM_TREES;c++)
    {
      glColor3f(0.0,0.0,0.0);
      glVertex3fv(&tree[c][0]);
      glColor3f(1.0,.8,.0);
      glVertex3fv(&tree[c][1]);
    }
  glEnd();
  */
  glColor3f(.8,.7,.1);
  glBegin(GL_QUADS);
     glVertex3f(0,0.0,0);
     glVertex3f(0,0.0,WORLD_SIZE);
     glVertex3f(WORLD_SIZE,0.0,WORLD_SIZE);
     glVertex3f(WORLD_SIZE,0.0,0);
  glEnd();
  glColor3f(.0,.0,.0);
  glBegin(GL_LINES);
  for (c=0;c<NUM_CLOUDS;c++)
  {
     glVertex3fv(&clouds[c][4]);
     glVertex3fv(&clouds[c][5]);
  }
  glEnd();
  if (!intro && !gameover)
  {
  glColor3f(.0,.0,1.0);
  glBegin(GL_LINES);
     glVertex3fv(&needle[0]);
     glVertex3fv(&needle[1]);
  glEnd();
  }
  glColor3f(1.0,1.0,1.0);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D,cloud);
  glBegin(GL_QUADS);
  for (c=0;c<NUM_CLOUDS;c++)
  {
     glTexCoord2f(0.0,0.0);
     glVertex3fv(&clouds[c][0]);
     glTexCoord2f(1.0,0.0);
     glVertex3fv(&clouds[c][1]);
     glTexCoord2f(1.0,1.0);
     glVertex3fv(&clouds[c][2]);
     glTexCoord2f(0.0,1.0);
     glVertex3fv(&clouds[c][3]);
  }
  glEnd();

  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D,fence);
  glBegin(GL_QUADS);
     glTexCoord2f(0.0,0.0);
     glVertex3f(0,0.0,0);
     glTexCoord2f(1.0,0.0);
     glVertex3f(0,0.0,WORLD_SIZE);
     glTexCoord2f(1.0,1.0);
     glVertex3f(0,HEIGHTF,WORLD_SIZE);
     glTexCoord2f(0.0,1.0);
     glVertex3f(0,HEIGHTF,0);

     glTexCoord2f(0.0,0.0);
     glVertex3f(0,0.0,WORLD_SIZE);
     glTexCoord2f(1.0,0.0);
     glVertex3f(WORLD_SIZE,0.0,WORLD_SIZE);
     glTexCoord2f(1.0,1.0);
     glVertex3f(WORLD_SIZE,HEIGHTF,WORLD_SIZE);
     glTexCoord2f(0.0,1.0);
     glVertex3f(0,HEIGHTF,WORLD_SIZE);

     glTexCoord2f(0.0,0.0);
     glVertex3f(WORLD_SIZE,0.0,WORLD_SIZE);
     glTexCoord2f(1.0,0.0);
     glVertex3f(WORLD_SIZE,0.0,0);
     glTexCoord2f(1.0,1.0);
     glVertex3f(WORLD_SIZE,HEIGHTF,0);
     glTexCoord2f(0.0,1.0);
     glVertex3f(WORLD_SIZE,HEIGHTF,WORLD_SIZE);

     glTexCoord2f(0.0,0.0);
     glVertex3f(WORLD_SIZE,0.0,0);
     glTexCoord2f(1.0,0.0);
     glVertex3f(0,0.0,0);
     glTexCoord2f(1.0,1.0);
     glVertex3f(0,HEIGHTF,0);
     glTexCoord2f(0.0,1.0);
     glVertex3f(WORLD_SIZE,HEIGHTF,0);

  glEnd();
  glDisable(GL_TEXTURE_2D);
  glLineWidth(5);
  glDrawArrays(GL_LINES, 0,NUM_TREES);

}

void generate_stuff()
{
  int c;
  for (c=0;c<NUM_TREES;c++)
  {
  tree[c][0][0]=FRAND(WORLD_SIZE);
  tree[c][0][2]=FRAND(WORLD_SIZE);
  tree[c][0][1]=FRAND(HAYSTACK*sin((tree[c][0][0]/WORLD_SIZE)*M_PI)*sin((tree[c][0][2]/WORLD_SIZE)*M_PI)*sin((tree[c][0][0]/WORLD_SIZE)*M_PI)*sin((tree[c][0][2]/WORLD_SIZE)*M_PI));
  tree[c][1][0]=tree[c][0][0]-10.0+FRAND(20.0);
  tree[c][1][1]=tree[c][0][1]-10.0+FRAND(20.0);
  tree[c][1][2]=tree[c][0][2]-10.0+FRAND(20.0);
  colors[c][0]=.2+(FRAND(800.0)/1000.0);
  colors[c][1]=colors[c][0]/(1.2+(FRAND(200.0)/1000.0));
  colors[c][2]=(FRAND(200.0)/1000.0);
  }
  /*
  needle[0][0]=FRAND(WORLD_SIZE);
  needle[0][2]=FRAND(WORLD_SIZE);
  needle[0][1]=FRAND(HAYSTACK*sin((needle[0][0]/WORLD_SIZE)*M_PI)*sin((needle[0][2]/WORLD_SIZE)*M_PI)*sin((needle[0][0]/WORLD_SIZE)*M_PI)*sin((needle[0][2]/WORLD_SIZE)*M_PI));
  needle[1][0]=needle[0][0]-10.0+FRAND(20.0);
  needle[1][1]=needle[0][1]-10.0+FRAND(20.0);
  needle[1][2]=needle[0][2]-10.0+FRAND(20.0);
  */
  needle[0][0]=FRAND(WORLD_SIZE);
  needle[0][2]=FRAND(WORLD_SIZE);
  needle[0][1]=5.0+HAYSTACK*sin((needle[0][0]/WORLD_SIZE)*M_PI)*sin((needle[0][2]/WORLD_SIZE)*M_PI)*sin((needle[0][0]/WORLD_SIZE)*M_PI)*sin((needle[0][2]/WORLD_SIZE)*M_PI);
  needle[1][0]=needle[0][0]-10.0+FRAND(20.0);
  needle[1][1]=needle[0][1]-10.0+FRAND(20.0);
  needle[1][2]=needle[0][2]-10.0+FRAND(20.0);
  for (c=0;c<NUM_CLOUDS;c++)
  {
    clouds[c][0][0]=(3*FRAND(WORLD_SIZE))-WORLD_SIZE;
    clouds[c][0][1]=(HAYSTACK*1.8)+FRAND(100.0);
    clouds[c][0][2]=(3*FRAND(WORLD_SIZE))-WORLD_SIZE;
    clouds[c][1][0]=clouds[c][0][0]+80.0+FRAND(100.0);
    clouds[c][1][1]=clouds[c][0][1];
    clouds[c][1][2]=clouds[c][0][2]+80.0+FRAND(100.0);
    clouds[c][2][0]=clouds[c][1][0];
    clouds[c][2][1]=clouds[c][1][1]-40.0-FRAND(80.0);
    clouds[c][2][2]=clouds[c][1][2];
    clouds[c][3][0]=clouds[c][0][0];
    clouds[c][3][1]=clouds[c][2][1];
    clouds[c][3][2]=clouds[c][0][2];
    clouds[c][4][0]=(clouds[c][0][0]+clouds[c][1][0])/2;
    clouds[c][4][1]=((clouds[c][0][1]+clouds[c][2][1])/2)-4.0;
    clouds[c][4][2]=(clouds[c][0][2]+clouds[c][1][2])/2;
    clouds[c][5][0]=clouds[c][4][0];
    clouds[c][5][1]=800.0;
    clouds[c][5][2]=clouds[c][4][2];

  }
}


void timer_proc(void)
{
  float r,g,b;
  if (intro)
  {
    introroll+=.02;
    playerx=(cos(introroll)*(WORLD_SIZE/2)+(WORLD_SIZE/2));
    playery=HAYSTACK;
    playerz=(sin(introroll)*(WORLD_SIZE/2)+(WORLD_SIZE/2));
    looky=introroll+(M_PI);
    lookx=M_PI/4;

    if (key[KEY_SPACE])
    {
      FSOUND_Stream_Stop(intromus);
      FSOUND_Stream_Play (0,theme);
      intro=0;

    }
  } else {
	if (key[KEY_LEFT]) {looky -= TURN_SPEED;}
	if (key[KEY_RIGHT]) {looky += TURN_SPEED;}

	if (key[KEY_UP]) {playerx += cos(looky)*MOVE_SPEED;playerz += sin(looky)*MOVE_SPEED;state=WALK;}
	if (key[KEY_DOWN]) {playerx -= cos(looky)*MOVE_SPEED;playerz -= sin(looky)*MOVE_SPEED;state=WALK;}
  if (!key[KEY_UP] && !key[KEY_DOWN] && state!=IDLE) state=IDLE;
  if (state==STOP) {if (ABS(playery-HEIGHT)>HAYSTACK*sin((playerx/WORLD_SIZE)*M_PI)*sin((playerx/WORLD_SIZE)*M_PI)*sin((playerz/WORLD_SIZE)*M_PI)*sin((playerz/WORLD_SIZE)*M_PI)) {playery-=SGN(playery-HEIGHT)*.4;} else {state=IDLE;lookf=0.0;}}
  if (state==IDLE) {lookf+=.1;playery=((HEIGHT/10.0)*sin(lookf))+HEIGHT; }
  if (state==WALK) {lookf+=0.2;playery=HEIGHT+((HEIGHT/3.0)*sin(lookf));}
  if (playerx> 0.0 && playerx<WORLD_SIZE && playerz> 0.0 && playerz<WORLD_SIZE)
  {
    playery+=(HAYSTACK*sin((playerx/WORLD_SIZE)*M_PI)*sin((playerx/WORLD_SIZE)*M_PI)*sin((playerz/WORLD_SIZE)*M_PI)*sin((playerz/WORLD_SIZE)*M_PI));
  } else playery+=0.0;
//	if (key[KEY_PGUP]) camera.dist -= dist_speed;
	//if (key[KEY_PGDN]) camera.dist += dist_speed;
  if (key[KEY_SPACE])
  {
    if (lookx<M_PI/4) lookx+=.1;
  } else {
    if (lookx>0.0) lookx-=.2;
  }
  if(playerx>(WORLD_SIZE-5.0)) playerx=(WORLD_SIZE-5.0);
  if(playerz>(WORLD_SIZE-5.0)) playerz=(WORLD_SIZE-5.0);
  if(playerx<5.0) playerx=5.0;
  if(playerz<5.0) playerz=5.0;
  final_point[0]+=point_xformed[0];
  final_point[1]+=point_xformed[1];
  final_point[2]+=point_xformed[2];
  seek();
  //if (key[KEY_A]) gameover=1;
  }

}

void draw (void)
{
  int c;
  float r,g,b,mx,my;
 if (gameover==1)
  {
    for (c=0;c<100;c++)
    {
    r=colors[recolour][0];
    g=colors[recolour][1];
    b=colors[recolour][2];
    colors[recolour][0]=b;
    colors[recolour][1]=r;
    colors[recolour][2]=(g/2);
    recolour++;
    if (recolour>=NUM_TREES) {gameover=2;c=110;}
    }

  }
  		glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	glPushMatrix();
	glFrustum (-(4.0/3.0), 4.0/3.0, -1.0, 1.0, 1.0, 8000.0);
	glMatrixMode (GL_MODELVIEW);
	// Clear the RGB buffer and the depth buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Set the modelview matrix to be the identity matrix
	glLoadIdentity();
	glPushMatrix();

	//Set the camera

	glRotatef (DEG(lookx), 1, 0, 0);
	glRotatef (DEG(looky)+90.0, 0, 1, 0);
	glRotatef (DEG(lookz), 0, 0, 1);
	glTranslatef (-playerx, -playery, -playerz);
	// Save the camera matrix

   if (mouse_b)
  {
    grab_mouse(mouse_x,mouse_y);
   // printf("needle: %1.2f %1.2f %1.2f camera: %1.2f %1.2f %1.2f click: %1.2f %1.2f %1.2f\n",needle[0][0],needle[0][1],needle[0][2],playerx,playery,playerz,final_point[0],final_point[1],final_point[2]);
  }
	// Translate and rotate the object
  glColor3f(1.0,1.0,1.0);
  glBegin(GL_POINTS);
   glVertex3fv(&final_point);
  glEnd( );
  draw_tree();

 // draw_trees();
	//glFlush();
  glMatrixMode (GL_PROJECTION);
  glPopMatrix();
  glMatrixMode (GL_MODELVIEW);
  glPopMatrix();
  glPointSize(3.0);
  glDisable(GL_TEXTURE_2D);
  glColor3f(1.0,1.0,1.0);
  mx=(mouse_x-320)/320.0;
  my=-(mouse_y-240)/240.0;
  glBegin(GL_LINES);
  glColor4f(1.0,.0,.0,1.0);
  glVertex2f(mx-CURSOR_SIZE,my-CURSOR_SIZE);
  glColor4f(.0,1.0,.0,1.0);
  glVertex2f(mx+CURSOR_SIZE,my+CURSOR_SIZE);
  glColor4f(.0,.0,1.0,1.0);
  glVertex2f(mx+CURSOR_SIZE,my-CURSOR_SIZE);
  glColor4f(1.0,1.0,.0,1.0);
  glVertex2f(mx-CURSOR_SIZE,my+CURSOR_SIZE);


  //glVertex3f(0.0,0.0,0.0);

  glEnd();
  if (intro)
  {
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D,introtex);
  glBegin(GL_QUADS);
    glTexCoord2f(0.0,0.0);
    glVertex2f(-1.0,1.0);
    glTexCoord2f(1.0,0.0);
    glVertex2f(1.0,1.0);
    glTexCoord2f(1.0,1.0);
    glVertex2f(1.0,-1.0);
    glTexCoord2f(0.0,1.0);
    glVertex2f(-1.0,-1.0);
  glEnd();
  glDisable(GL_TEXTURE_2D);
  }
  if (gameover)
  {
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D,outro);
  glBegin(GL_QUADS);
    glTexCoord2f(0.0,0.0);
    glVertex2f(-1.0,1.0);
    glTexCoord2f(1.0,0.0);
    glVertex2f(1.0,1.0);
    glTexCoord2f(1.0,1.0);
    glVertex2f(1.0,.5);
    glTexCoord2f(0.0,1.0);
    glVertex2f(-1.0,.5);
  glEnd();
  glDisable(GL_TEXTURE_2D);
  }
	allegro_gl_flip();

}


void timer(void)
{
	tim++;
}
END_OF_FUNCTION(timer);

int main ()
{
  int c;
	allegro_init();
  //printf("WAT?\n");
	install_allegro_gl();

	allegro_gl_clear_settings();
	allegro_gl_set (AGL_COLOR_DEPTH, 32);
	allegro_gl_set (AGL_Z_DEPTH, 24);
	allegro_gl_set (AGL_WINDOWED, TRUE);
	allegro_gl_set (AGL_DOUBLEBUFFER, 1);
	allegro_gl_set (AGL_SUGGEST, AGL_COLOR_DEPTH | AGL_Z_DEPTH
	              | AGL_DOUBLEBUFFER | AGL_WINDOWED);

	if (set_gfx_mode(GFX_OPENGL, 640, 480, 0, 0) < 0) {
		allegro_message ("Error setting OpenGL graphics mode:\n%s\n"
		                 "Allegro GL error : %s\n",
		                 allegro_error, allegro_gl_error);
		return -1;
	}
  FSOUND_Init (44100, 32, 0);
  theme=FSOUND_Stream_Open("theme.mp3",FSOUND_LOOP_NORMAL, 0, 0);
  intromus=FSOUND_Stream_Open("intro.mp3",FSOUND_LOOP_NORMAL, 0, 0);
  outromus=FSOUND_Stream_Open("outro.mp3",FSOUND_LOOP_NORMAL, 0, 0);

  FSOUND_Stream_Play (0,intromus);
	install_keyboard();
	install_timer();
	install_mouse();
//	show_mouse(screen);

	LOCK_FUNCTION(timer);
	LOCK_VARIABLE(tim);

	glShadeModel (GL_SMOOTH);
	glEnable (GL_DEPTH_TEST);
	glCullFace (GL_FRONT_AND_BACK);
	glDisable (GL_CULL_FACE);
  glLineWidth(5);
	install_int_ex (timer, BPS_TO_TIMER(30));
  srand(time(NULL));
  //printf("WAT?\n");
  glEnable(GL_TEXTURE_2D);
  generate_stuff();
  glClearColor(.4,.6,.8,.0);
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);
  glVertexPointer(3, GL_FLOAT, 0, tree);
  glColorPointer(3, GL_FLOAT, 0, colors);
  fence = SOIL_load_OGL_texture("fencea.tga",4,0,SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_TEXTURE_REPEATS | SOIL_FLAG_INVERT_Y );
  cloud = SOIL_load_OGL_texture("clouda.tga",4,0,SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_TEXTURE_REPEATS);
  introtex = SOIL_load_OGL_texture("introa.tga",4,0,SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_TEXTURE_REPEATS);
  outro = SOIL_load_OGL_texture("outroa.tga",4,0,SOIL_FLAG_POWER_OF_TWO | SOIL_FLAG_TEXTURE_REPEATS);

  glEnable(GL_BLEND);
  glEnable(GL_ALPHA_TEST);
  glAlphaFunc(GL_EQUAL, 1.0);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  playerx=20.0;playerz=20.0;looky=M_PI/4;

	do {
	  while (tim>0)
	  {
		  timer_proc();
		  tim--;
	  }
	  draw();
	} while (!key[KEY_ESC]);

	set_gfx_mode (GFX_TEXT, 0, 0, 0, 0);



	return 0;
}
END_OF_MAIN()

