/*
 * This code was created by Jeff Molofee '99 
 * (ported to Linux/SDL by Ti Leggett '01)
 *
 * If you've found this code useful, please let me know.
 *
 * Visit Jeff at http://nehe.gamedev.net/
 * 
 * or for port-specific comments, questions, bugreports etc. 
 * email to leggett@eecs.tulane.edu
 */

/* Comment this out if you don't want sound. */
#define SOUND
 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "SDL.h"
#include "SDL_image.h"

#ifdef SOUND
#include "SDL_mixer.h"
#endif

/* screen width, height, and bit depth */
#define SCREEN_WIDTH  320	
#define SCREEN_HEIGHT 240
#define SCREEN_BPP     16

/* Set up some booleans */
#define TRUE  1
#define FALSE 0




/* This is our SDL surface */
SDL_Surface *surface;

#ifdef SOUND
/* Our audio chunk */
Mix_Chunk *chunk;
Mix_Chunk *chunk2;
Mix_Music *music;
#endif

/* Build Our Vertex Structure */
typedef struct
{
    float x, y, z; /* 3D Coordinates */
    float u, v;    /* Texture Coordinates */
} vertex;

/* Build Our Triangle Structure */
typedef struct
{
    vertex vertex[3]; /* Array Of Three Vertices */
} triangle;

/* Build Our Sector Structure */
typedef struct
{
    int numTriangles;   /* Number Of Triangles In Sector */
    triangle *triangle; /* Pointer To Array Of Triangles */
} sector;

sector sector1;     /* Our sector */

GLfloat yrot;       /* Camera rotation variable */
GLfloat xpos, zpos; /* Camera pos variable */

GLfloat walkbias, walkbiasangle; /* Head-bobbing variables */
GLfloat lookupdown;

/* Ambient Light Values */
GLfloat LightAmbient[]  = { 0.5f, 0.5f, 0.5f, 1.0f };
/* Diffuse Light Values */
GLfloat LightDiffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
/* Light Position */
GLfloat LightPosition[] = { 0.0f, 0.0f, 2.0f, 1.0f };

/* constant used for converting to radians */
const float piover180 = 0.0174532925f;

GLuint filter;     /* Which Filter To Use */
GLuint texture[3]; /* Storage for 3 textures */
GLuint floortexture[3]; /* Storage for 3 textures */
GLuint usatexture[2]; /* Storage for 1 textures */
GLuint pngtexture[2]; /* Storage for 1 textures */
GLuint horiztexture[2]; /* Storage for 1 textures */

int walk = 0;
int gira = 0;

int Map[12][12];


#ifdef SOUND
void PlaySound( char *sound, int repeat )
{

    if ( sound == NULL )
	{
	    Mix_HaltChannel( 1 );
	    Mix_FreeChunk( chunk );
	    chunk = NULL;

	    return;
	}

    if ( chunk )
	{
	    Mix_HaltChannel( 1 );
	    Mix_FreeChunk( chunk );

	    chunk = NULL;
	}

    chunk = Mix_LoadWAV( sound );

    if ( chunk == NULL )
	fprintf( stderr, "Failed to load sound: %s\n", sound );

    Mix_PlayChannel( -1, chunk, repeat );

    return;
}
void PlaySound2( char *sound, int repeat )
{

    if ( sound == NULL )
	{
	    Mix_HaltChannel( 2 );
	    Mix_FreeChunk( chunk2 );
	    chunk2 = NULL;

	    return;
	}

    if ( chunk2 )
	{
	    Mix_HaltChannel( 2 );
	    Mix_FreeChunk( chunk2 );

	    chunk2 = NULL;
	}

    chunk2 = Mix_LoadWAV( sound );

    if ( chunk2 == NULL )
	fprintf( stderr, "Failed to load sound: %s\n", sound );

    Mix_PlayChannel( -1, chunk2, repeat );

    return;
}
#endif


void TestColision(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2) 
{

	float a1 = (y2-y1);
	float b1 = (x1-x2);
	double c1 = a1*x1+b1*y1;
	float a2 = (v2-v1);
	float b2 = (u1-u2);
	double c2 = a2*u1+b2*v1;
	
	double det = a1*b2 - a2*b1;

	double xi,yi,xmax,xmin,ymax,ymin,umax,umin,vmax,vmin;

	if (det == 0) {

		/*NO es tallen --- son paralelas*/

	} else {

		xi = (b2*c1-b1*c2)/det;
		yi = (a1*c2-a2*c1)/det;

		if (x1<=x2) {xmin = x1; xmax = x2;}else {xmin = x2; xmax = x1;}
		if (y1<=y2) {ymin = y1; ymax = y2;}else {ymin = y2; ymax = y1;}
		if (u1<=u2) {umin = u1; umax = u2;}else {umin = u2; umax = u1;}
		if (v1<=v2) {vmin = v1; vmax = v2;}else {vmin = v2; vmax = v1;}
		if ((xmin <= xi) && ( xi <= xmax) && (ymin <= yi) && ( yi <= ymax) && (umin <= xi) && ( xi <= umax) && (vmin <= yi) && ( yi <= vmax)) {
			/* es tallen*/
			PlaySound2( "data/target.wav", 0 );
		} else {
			/* es tallen pero no en el segment*/
		}
	}

}



/* function to release/destroy our resources and restoring the old desktop */
void Quit( int returnCode )
{

#ifdef SOUND
    /* Stop playing the music */
    Mix_HaltMusic( );

    /* Free up the memory for the music */
    Mix_FreeMusic( music );

    /* Free up any memory for the sfx */
    Mix_FreeChunk( chunk );
    Mix_FreeChunk( chunk2 );

    /* Close our audio device */
    Mix_CloseAudio( );

    /* Close up the sound sub system */
    SDL_QuitSubSystem( SDL_INIT_AUDIO );
#endif

    /* clean up the window */
    SDL_Quit( );

    /* Deallocate things we allocated */
    if ( sector1.triangle )
	free( sector1.triangle );

    /* and exit appropriately */
    exit( returnCode );
}


/* function to load in bitmap as a GL texture */
int LoadGLTextures( )
{
    /* Status indicator */
    int Status = FALSE;

    /* Create storage space for the texture */
    SDL_Surface *TextureImage[1]; 

    /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */
    if ( ( TextureImage[0] = SDL_LoadBMP( "data/mud.bmp" ) ) )
        {

	    /* Set the status to true */
	    Status = TRUE;

	    /* Create The Texture */
	    glGenTextures( 3, &texture[0] );

	    /* Load in texture 1 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, texture[0] );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
	    
	    /* Nearest Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_NEAREST );

	    /* Load in texture 2 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, texture[1] );

	    /* Linear Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_LINEAR );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_LINEAR );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );

	    /* Load in texture 3 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, texture[2] );

	    /* Mipmapped Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_LINEAR_MIPMAP_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_LINEAR );

	    /* Generate The MipMapped Texture ( NEW ) */
	    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, TextureImage[0]->w,
			       TextureImage[0]->h, GL_BGR,
			       GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
        }
	    Status = FALSE;
    /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */
    if ( ( TextureImage[0] = SDL_LoadBMP( "data/floor.bmp" ) ) )
        {

	    /* Set the status to true */
	    Status = TRUE;

	    /* Create The Texture */
	    glGenTextures( 3, &floortexture[0] );

	    /* Load in texture 1 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, floortexture[0] );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
	    
	    /* Nearest Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_NEAREST );

	    /* Load in texture 2 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, floortexture[1] );

	    /* Linear Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_LINEAR );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_LINEAR );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );

	    /* Load in texture 3 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, floortexture[2] );

	    /* Mipmapped Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_LINEAR_MIPMAP_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_LINEAR );

	    /* Generate The MipMapped Texture ( NEW ) */
	    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, TextureImage[0]->w,
			       TextureImage[0]->h, GL_BGR,
			       GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
        }
    /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */
    if ( ( TextureImage[0] = SDL_LoadBMP( "data/usa.bmp" ) ) )
        {
	 
		
	    /* Set the status to true */
	    Status = TRUE;

	    /* Create The Texture */
	    glGenTextures( 1, &usatexture[0] );


	    /* Load in texture 1 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, usatexture[0] );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
	    
	    /* Nearest Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_NEAREST );

        }
    /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */
    if ( ( TextureImage[0] = SDL_LoadBMP( "data/usamask.bmp" ) ) )
        {
	 
		
	    /* Set the status to true */
	    Status = TRUE;

	    /* Create The Texture */
	    glGenTextures( 1, &usatexture[1] );


	    /* Load in texture 1 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, usatexture[1] );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
	    
	    /* Nearest Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_NEAREST );

        }
    /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */
    if ( ( TextureImage[0] = SDL_LoadBMP( "data/horiz.bmp" ) ) )
        {
	 
		
	    /* Set the status to true */
	    Status = TRUE;

	    /* Create The Texture */
	    glGenTextures( 1, &horiztexture[0] );


	    /* Load in texture 1 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, horiztexture[0] );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
	    
	    /* Nearest Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_NEAREST );

        }
    /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */
    if ( ( TextureImage[0] = SDL_LoadBMP( "data/horiz3.bmp" ) ) )
        {
	 
		
	    /* Set the status to true */
	    Status = TRUE;

	    /* Create The Texture */
	    glGenTextures( 1, &horiztexture[1] );


	    /* Load in texture 1 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, horiztexture[1] );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_BGR,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
	    
	    /* Nearest Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_NEAREST );

        }
    if ( ( TextureImage[0] = IMG_Load( "data/mir.png" ) ) )
        {
	 
		
	    /* Set the status to true */
	    Status = TRUE;

	    /* Create The Texture */
	    glGenTextures( 1, &pngtexture[0] );


	    /* Load in texture 1 */
	    /* Typical Texture Generation Using Data From The Bitmap */
	    glBindTexture( GL_TEXTURE_2D, pngtexture[0] );

	    /* Generate The Texture */
	    glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
			  TextureImage[0]->h, 0, GL_RGBA,
			  GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
	    
	    /* Nearest Filtering */
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			     GL_NEAREST );
	    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
			     GL_NEAREST );

        }

    /* Free up any memory we may have used */
    if ( TextureImage[0] )
	    SDL_FreeSurface( TextureImage[0] );

    return Status;
}


/* Read In A String */
void readstr( FILE *f, char *string )
{
    /* Start A Loop */
    do
        {
	    /* Read One Line */
	    fgets( string, 255, f );
        } while ( ( string[0] == '/' ) || ( string[0] == '\n' ) );

    return;
}

/* Setup Our World */
void SetupWorld( char* worldFile )
{
    FILE *filein;        /* File To Work With */

    int numTriangles;    /* Number of Triangles */
    char oneLine[255];   /* One line from conf file */

    float x, y, z, u, v; /* 3d and texture coordinates */

    int triLoop;         /* Triangle loop variable */
    int verLoop;         /* Vertex loop variable */

    /* Open Our File */
    filein = fopen( worldFile, "rt" );

    /* Grab a line from 'filein' */
    readstr( filein, oneLine );

    /* Read in number of triangle */
    sscanf( oneLine, "NUMPOLLIES %d\n", &numTriangles );

    /* allocate space for our triangles */
    sector1.triangle     = malloc( numTriangles * sizeof( triangle ) );
    if ( sector1.triangle == NULL )
	{
	    fprintf( stderr, "Could not allocate memory for triangles.\n" );
	    Quit( 1 );
	}
    sector1.numTriangles = numTriangles;

    /* Get coords for each triangle */
    for ( triLoop = 0; triLoop < numTriangles; triLoop++ )
	{
	    for ( verLoop = 0; verLoop < 3; verLoop++ )
		{
		    readstr( filein, oneLine );
		    sscanf( oneLine, "%f %f %f %f %f\n", &x, &y, &z, &u, &v );
		    sector1.triangle[triLoop].vertex[verLoop].x = x;
		    sector1.triangle[triLoop].vertex[verLoop].y = y;
		    sector1.triangle[triLoop].vertex[verLoop].z = z;
		    sector1.triangle[triLoop].vertex[verLoop].u = u;
		    sector1.triangle[triLoop].vertex[verLoop].v = v;
		}
	}

    /* Close Our File */
    fclose( filein );

    return;
}

void initMap () 
{
	short	i, j;
		
	/* First initialize them all to FALSE to clear the map */
	for (i = 0; i < 12; i++) {
	  for (j = 0; j < 12; j++) {
		Map[i][j] = FALSE;
	  }
	}

	Map [0][0] = TRUE;
	Map [1][0] = TRUE;
	Map [2][0] = TRUE;
	Map [3][0] = TRUE;
	Map [4][0] = TRUE;
	
	Map [7][0] = TRUE;
	Map [8][0] = TRUE;
	Map [9][0] = TRUE;
	Map [10][0] = TRUE;
	Map [11][0] = TRUE;

	Map [0][1] = TRUE;
	Map [1][1] = TRUE;
	Map [2][1] = TRUE;
	Map [3][1] = TRUE;
	Map [4][1] = TRUE;
	
	Map [7][1] = TRUE;
	Map [8][1] = TRUE;
	Map [9][1] = TRUE;
	Map [10][1] = TRUE;
	Map [11][1] = TRUE;
	
	Map [0][2] = TRUE;
	Map [1][2] = TRUE;
	
	Map [10][2] = TRUE;
	Map [11][2] = TRUE;
	
	Map [0][3] = TRUE;
	Map [1][3] = TRUE;
	
	Map [10][3] = TRUE;
	Map [11][3] = TRUE;
	
	Map [0][4] = TRUE;
	Map [1][4] = TRUE;
	
	Map [10][4] = TRUE;
	Map [11][4] = TRUE;
	
	Map [0][7] = TRUE;
	Map [1][7] = TRUE;
	
	Map [10][7] = TRUE;
	Map [11][7] = TRUE;
	
	Map [0][8] = TRUE;
	Map [1][8] = TRUE;
	
	Map [10][8] = TRUE;
	Map [11][8] = TRUE;
	
	Map [0][9] = TRUE;
	Map [1][9] = TRUE;
	
	Map [10][9] = TRUE;
	Map [11][9] = TRUE;

	Map [0][10] = TRUE;
	Map [1][10] = TRUE;
	Map [2][10] = TRUE;
	Map [3][10] = TRUE;
	Map [4][10] = TRUE;
	
	Map [7][10] = TRUE;
	Map [8][10] = TRUE;
	Map [9][10] = TRUE;
	Map [10][10] = TRUE;
	Map [11][10] = TRUE;

	Map [0][11] = TRUE;
	Map [1][11] = TRUE;
	Map [2][11] = TRUE;
	Map [3][11] = TRUE;
	Map [4][11] = TRUE;
	
	Map [7][11] = TRUE;
	Map [8][11] = TRUE;
	Map [9][11] = TRUE;
	Map [10][11] = TRUE;
	Map [11][11] = TRUE;

}




/* function to reset our viewport after a window resize */
int resizeWindow( int width, int height )
{
    /* Height / width ration */
    GLfloat ratio;

    /* Protect against a divide by zero */
    if ( height == 0 )
	height = 1;

    ratio = ( GLfloat )width / ( GLfloat )height;

    /* Setup our viewport. */
    glViewport( 0, 0, ( GLint )width, ( GLint )height );

    /* change to the projection matrix and set our viewing volume. */
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );

    /* Set our perspective */
    gluPerspective( 45.0f, ratio, 0.1f, 100.0f );

    /* Make sure we're chaning the model view and not the projection */
    glMatrixMode( GL_MODELVIEW );

    /* Reset The View */
    glLoadIdentity( );

    return( TRUE );
}

/* function to handle key press events */
void handleKeyPress( SDL_keysym *keysym )
{
    switch ( keysym->sym )
	{
	case SDLK_ESCAPE:
	    /* ESC key was pressed */
	    Quit( 0 );
	    break;
	case SDLK_F1:
	    /* F1 key was pressed
	     * this toggles fullscreen mode
	     */
	    SDL_WM_ToggleFullScreen( surface );
	    break;
	case SDLK_RIGHT:
	    /* Right arrow key was pressed
	     * this effectively turns the camera right, but does it by
	     * rotating the scene left
	     */
	    yrot -= 1.5f;
	    break;
	case SDLK_LEFT:
	    /* Left arrow key was pressed
	     * this effectively turns the camera left, but does it by
	     * rotating the scene right
	     */
	    yrot += 1.5f;
	    break;
	case SDLK_UP:
	    /* Up arrow key was pressed
	     * this moves the player forward
	     */
	    /* Move On The X-Plane Based On Player Direction */
	    xpos -= ( float )sin( yrot * piover180 ) * 0.05f;
	    /* Move On The Z-Plane Based On Player Direction */
	    zpos -= ( float )cos( yrot * piover180 ) * 0.05f;
	    if ( walkbiasangle >= 359.0f )
		walkbiasangle = 0.0f;
	    else
		walkbiasangle+= 10;

	    /* Causes the player to bounce */
	    walkbias = ( float )sin( walkbiasangle * piover180 ) / 20.0f;
	    break;
	case SDLK_DOWN:
	    /* Down arrow key was pressed
	     * this causes the player to move backwards
	     */
	    /* Move On The X-Plane Based On Player Direction */
	    xpos += ( float )sin( yrot * piover180 ) * 0.05f;
	    /* Move On The Z-Plane Based On Player Direction */
	    zpos += ( float )cos( yrot * piover180 ) * 0.05f;
	    if ( walkbiasangle <= 1.0f )
                walkbiasangle = 359.0f;
	    else
                walkbiasangle -= 10;

	    walkbias = ( float )sin( walkbiasangle * piover180 ) / 20.0f;
	    break;
	case SDLK_SPACE:
#ifdef SOUND
				    /* Play The Death Sound */
				    PlaySound( "data/shot.wav", 0 );
#endif
	    break;
	default:
	    break;
	}

    return;
}

/* general OpenGL initialization function */
int initGL( GLvoid )
{

    /* Load in the texture */
    if ( !LoadGLTextures( ) )
	return FALSE;

    /* Enable Texture Mapping */
    glEnable( GL_TEXTURE_2D );

    /* Enable smooth shading */
    glShadeModel( GL_SMOOTH );
    glDisable (GL_LINE_SMOOTH);

    /* Set the background black */
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

    /* Depth buffer setup */
    glClearDepth( 1.0f );

    /* Enables Depth Testing */
    glEnable( GL_DEPTH_TEST );

    /* The Type Of Depth Test To Do */
    glDepthFunc( GL_LEQUAL );

    /* Really Nice Perspective Calculations */
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

    /* Setup The Ambient Light */
    glLightfv( GL_LIGHT1, GL_AMBIENT, LightAmbient );

    /* Setup The Diffuse Light */
    glLightfv( GL_LIGHT1, GL_DIFFUSE, LightDiffuse );

    /* Position The Light */
    glLightfv( GL_LIGHT1, GL_POSITION, LightPosition );

    /* Enable Light One */
    glEnable( GL_LIGHT1 );

    lookupdown    = 0.0f;
    walkbias      = 0.0f;
    walkbiasangle = 0.0f;
    
    /* Full Brightness, 50% Alpha */
    glColor4f( 1.0f, 1.0f, 1.0f, 0.5f);

    /* Blending Function For Translucency Based On Source Alpha Value */
    glBlendFunc( GL_SRC_ALPHA, GL_ONE );

    return( TRUE );
}

/* Here goes our drawing code */
int drawGLScene( GLvoid )
{
    /* These are to calculate our fps */
    static GLint T0     = 0;
    static GLint Frames = 0;

    /* Floating Point For Temp X, Y, Z, U And V Vertices */
    GLfloat x_m, y_m, z_m, u_m, v_m;
    /* Used For Player Translation On The X Axis */
    GLfloat xtrans = -xpos;
    /* Used For Player Translation On The Z Axis */
    GLfloat ztrans = -zpos;
    /* Used For Bouncing Motion Up And Down */
    GLfloat ytrans = -walkbias - 0.18f;
    /* 360 Degree Angle For Player Direction */
    GLfloat sceneroty = 360.0f - yrot;

    /* Loop variable */
    int loop_m;
    
    float newx, newz;
    int Xmap, Zmap;

    /* per al punt de mira */

    GLfloat xmir = xpos + 0.13 * sin(sceneroty* piover180);
    GLfloat zmir = zpos - 0.13 * cos(sceneroty* piover180);
    GLfloat ymir = +walkbias + 0.17f;


    	if (gira == 1) {
		yrot += 1.5f;
		if (yrot > 359) yrot = 0;
	} else if (gira == -1) {
		yrot -= 1.5f;
		if (yrot < 0) yrot = 359;
	}
    
	if (walk == 1) {
	  
	    newx = xpos - 8 * (( float )sin( yrot * piover180 ) * 0.05f);
	    newz = zpos - 8 * (( float )cos( yrot * piover180 ) * 0.05f);
	   
	    Xmap = floor((newx + 3) * 2);
	    Zmap = floor((newz + 3) * 2);
	    if ((Map[Xmap][Zmap] == FALSE)) {
		
	    	/* Up arrow key was pressed
	    	 * this moves the player forward
	    	 */
	    	/* Move On The X-Plane Based On Player Direction */
	    	xpos -= ( float )sin( yrot * piover180 ) * 0.05f;
	    	/* Move On The Z-Plane Based On Player Direction */
	    	zpos -= ( float )cos( yrot * piover180 ) * 0.05f;
	    	if ( walkbiasangle >= 359.0f )
			walkbiasangle = 0.0f;
		    else
			walkbiasangle+= 10;

	    	/* Causes the player to bounce */
	    	walkbias = ( float )sin( walkbiasangle * piover180 ) / 20.0f;
	    } 
	} else if (walk == -1) {
	  
	    newx = xpos + 8 * (( float )sin( yrot * piover180 ) * 0.05f);
	    newz = zpos + 8 * (( float )cos( yrot * piover180 ) * 0.05f);
	    
	    Xmap = floor((newx + 3) * 2);
	    Zmap = floor((newz + 3) * 2);
	    if ((Map[Xmap][Zmap] == FALSE)) {
		
	    	/* Down arrow key was pressed
	    	 * this causes the player to move backwards
	    	 */
	    	/* Move On The X-Plane Based On Player Direction */
	    	xpos += ( float )sin( yrot * piover180 ) * 0.05f;
	    	/* Move On The Z-Plane Based On Player Direction */
	    	zpos += ( float )cos( yrot * piover180 ) * 0.05f;
	    	if ( walkbiasangle <= 1.0f )
                	walkbiasangle = 359.0f;
	    	else
                	walkbiasangle -= 10;

	    	walkbias = ( float )sin( walkbiasangle * piover180 ) / 20.0f;
	    }
	}

    /* Clear The Screen And The Depth Buffer */
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glLoadIdentity( );

    /* Rotate Up And Down To Look Up And Down */
    glRotatef( lookupdown, 1.0f, 0.0f , 0.0f );
    /* Rotate Depending On Direction Player Is Facing */
    glRotatef( sceneroty, 0.0f, 1.0f , 0.0f );

    /* Translate The Scene Based On Player Position */
    glTranslatef( xtrans, ytrans, ztrans );
   

    
    /* Select A Texture Based On filter */
    glBindTexture( GL_TEXTURE_2D, floortexture[filter] );

	glBegin(GL_QUADS);

	      glTexCoord2f( 3.0, 3.0 );
	      glVertex3f( -3.0, 0.0, 3.0 );

	      glTexCoord2f( -3.0, 3.0 );
	      glVertex3f( 3.0, 0.0, 3.0 );

	      glTexCoord2f( -3.0, -3.0 );
	      glVertex3f( 3.0, 0.0, -3.0 );

	      glTexCoord2f( 3.0, -3.0 );
	      glVertex3f( -3.0, 0.0, -3.0 );

		
	glEnd();
    /* Select A Texture Based On filter */
    glBindTexture( GL_TEXTURE_2D, horiztexture[0] );

	glBegin(GL_QUADS);

	      glTexCoord2f( 0.0, 1.0 );
	      glVertex3f( 3.0, 0.0, -3.0 );

	      glTexCoord2f( 1.0, 1.0 );
	      glVertex3f( -3.0, 0.0, -3.0 );

	      glTexCoord2f( 1.0, 0.0 );
	      glVertex3f( -3.0, 4.0, -3.0 );

	      glTexCoord2f( 0.0, 0.0 );
	      glVertex3f( 3.0, 4.0, -3.0 );

		
	glEnd();
    /* Select A Texture Based On filter */
    glBindTexture( GL_TEXTURE_2D, horiztexture[1] );

	glBegin(GL_QUADS);

	      glTexCoord2f( 0.0, 1.0 );
	      glVertex3f( 3.0, 0.0, -3.0 );

	      glTexCoord2f( 0.0, 0.0 );
	      glVertex3f( 3.0, 4.0, -3.0 );

	      glTexCoord2f( 1.0, 0.0 );
	      glVertex3f( 3.0, 4.0, 3.0 );

	      glTexCoord2f( 1.0, 1.0 );
	      glVertex3f( 3.0, 0.0, 3.0 );

	      glTexCoord2f( 1.0, 1.0 );
	      glVertex3f( -3.0, 0.0, -3.0 );

	      glTexCoord2f( 1.0, 0.0 );
	      glVertex3f( -3.0, 4.0, -3.0 );

	      glTexCoord2f( 0.0, 0.0 );
	      glVertex3f( -3.0, 4.0, 3.0 );

	      glTexCoord2f( 0.0, 1.0 );
	      glVertex3f( -3.0, 0.0, 3.0 );

	      glTexCoord2f( 1.0, 1.0 );
	      glVertex3f( 3.0, 0.0, 3.0 );

	      glTexCoord2f( 0.0, 1.0 );
	      glVertex3f( -3.0, 0.0, 3.0 );

	      glTexCoord2f( 0.0, 0.0 );
	      glVertex3f( -3.0, 4.0, 3.0 );

	      glTexCoord2f( 1.0, 0.0 );
	      glVertex3f( 3.0, 4.0, 3.0 );

		
	glEnd();
    
    /* Select A Texture Based On filter */
    glBindTexture( GL_TEXTURE_2D, texture[filter] );
        
    /* Process Each Triangle */
    for ( loop_m = 0; loop_m < sector1.numTriangles; loop_m++ )
        {
	    /* Start Drawing Triangles */
	    glBegin(GL_TRIANGLES);
	      /* Normal Pointing Forward */
	      glNormal3f( 0.0f, 0.0f, 1.0f);
	      /* X Vertex Of 1st Point */
	      x_m = sector1.triangle[loop_m].vertex[0].x;
	      /* Y Vertex Of 1st Point */
	      y_m = sector1.triangle[loop_m].vertex[0].y;
	      /* Z Vertex Of 1st Point */
	      z_m = sector1.triangle[loop_m].vertex[0].z;
	      /* U Texture Coord Of 1st Point */
	      u_m = sector1.triangle[loop_m].vertex[0].u;
	      /* V Texture Coord Of 1st Point */
	      v_m = sector1.triangle[loop_m].vertex[0].v;

	      /* Set The TexCoord And Vertice */
	      glTexCoord2f( u_m, v_m );
	      glVertex3f( x_m, y_m, z_m );

	      /* X Vertex Of 2nd Point */
	      x_m = sector1.triangle[loop_m].vertex[1].x;
	      /* Y Vertex Of 2nd Point */
	      y_m = sector1.triangle[loop_m].vertex[1].y;
	      /* Z Vertex Of 2nd Point */
	      z_m = sector1.triangle[loop_m].vertex[1].z;
	      /* U Texture Coord Of 2nd Point */
	      u_m = sector1.triangle[loop_m].vertex[1].u;
	      /* V Texture Coord Of 2nd Point */
	      v_m = sector1.triangle[loop_m].vertex[1].v;

	      /* Set The TexCoord And Vertice */
	      glTexCoord2f( u_m, v_m );
	      glVertex3f( x_m, y_m, z_m );

	      /* X Vertex Of 3rd Point */
	      x_m = sector1.triangle[loop_m].vertex[2].x;
	      /* Y Vertex Of 3rd Point */
	      y_m = sector1.triangle[loop_m].vertex[2].y;
	      /* Z Vertex Of 3rd Point */
	      z_m = sector1.triangle[loop_m].vertex[2].z;
	      /*  Texture Coord Of 3rd Point */
	      u_m = sector1.triangle[loop_m].vertex[2].u;
	      /* V Texture Coord Of 3rd Point */
	      v_m = sector1.triangle[loop_m].vertex[2].v;

	      /* Set The TexCoord And Vertice */
	      glTexCoord2f( u_m, v_m );
	      glVertex3f( x_m, y_m, z_m );
	    glEnd( );
	}


	    glEnable(GL_BLEND);
	    glDisable(GL_DEPTH_TEST);
	    
	    glBlendFunc(GL_DST_COLOR,GL_ZERO);
	    
    glBindTexture( GL_TEXTURE_2D, pngtexture[0] );
	glBegin(GL_QUADS);

	      glTexCoord2f( 0.0, 0.0 );
	      glVertex3f( xmir-0.02*cos(sceneroty*piover180), ymir, zmir-0.02*sin(sceneroty*piover180) );

	      glTexCoord2f( 1.0, 0.0 );
	      glVertex3f( xmir+0.02*cos(sceneroty*piover180), ymir, zmir+0.02*sin(sceneroty*piover180) );

	      glTexCoord2f( 1.0, 1.0 );
	      glVertex3f( xmir+0.02*cos(sceneroty*piover180), ymir + 0.025, zmir+0.02*sin(sceneroty*piover180) );

	      glTexCoord2f( 0.0, 1.0 );
	      glVertex3f( xmir-0.02*cos(sceneroty*piover180), ymir + 0.025, zmir-0.02*sin(sceneroty*piover180) );

		
	glEnd();

	    glBlendFunc(GL_DST_COLOR,GL_ZERO);
	    
    glBindTexture( GL_TEXTURE_2D, usatexture[1] );

	glBegin(GL_QUADS);

	      glTexCoord2f( 0.0, 0.0 );
	      glVertex3f( -0.062*cos(sceneroty*piover180), 0.0, -2-0.062*sin(sceneroty*piover180) );

	      glTexCoord2f( 1.0, 0.0 );
	      glVertex3f( 0.062*cos(sceneroty*piover180), 0.0, -2+0.062*sin(sceneroty*piover180) );

	      glTexCoord2f( 1.0, 1.0 );
	      glVertex3f( 0.062*cos(sceneroty*piover180), 0.35, -2+0.062*sin(sceneroty*piover180) );

	      glTexCoord2f( 0.0, 1.0 );
	      glVertex3f( -0.062*cos(sceneroty*piover180), 0.35, -2-0.062*sin(sceneroty*piover180) );

		
	glEnd();

	    glBlendFunc(GL_ONE,GL_ONE);

    glBindTexture( GL_TEXTURE_2D, usatexture[0] );

	glBegin(GL_QUADS);

	      glTexCoord2f( 0.0, 0.0 );
	      glVertex3f( -0.062*cos(sceneroty*piover180), 0.0, -2-0.062*sin(sceneroty*piover180) );

	      glTexCoord2f( 1.0, 0.0 );
	      glVertex3f( 0.062*cos(sceneroty*piover180), 0.0, -2+0.062*sin(sceneroty*piover180) );

	      glTexCoord2f( 1.0, 1.0 );
	      glVertex3f( 0.062*cos(sceneroty*piover180), 0.35, -2+0.062*sin(sceneroty*piover180) );

	      glTexCoord2f( 0.0, 1.0 );
	      glVertex3f( -0.062*cos(sceneroty*piover180), 0.35, -2-0.062*sin(sceneroty*piover180) );

		
	glEnd();


	    glEnable(GL_DEPTH_TEST);
	    glDisable(GL_BLEND);

    TestColision( xpos, zpos, xpos + 9 * sin(sceneroty* piover180), zpos - 9 * cos(sceneroty* piover180), -0.062*cos(sceneroty*piover180), -2-0.062*sin(sceneroty*piover180) , 0.062*cos(sceneroty*piover180) ,-2+0.062*sin(sceneroty*piover180));

    
	    printf("%f %f -- %f %f ------------ %f %f -- %f %f\n", xpos, zpos, xpos + 3 * sin(sceneroty* piover180), zpos - 3 * cos(sceneroty* piover180), -0.062*cos(sceneroty*piover180), -2-0.062*sin(sceneroty*piover180) , 0.062*cos(sceneroty*piover180) ,-2+0.062*sin(sceneroty*piover180) );

    /* Draw it to the screen */
    SDL_GL_SwapBuffers( );

    /* Gather our frames per second */
    Frames++;
    {
	GLint t = SDL_GetTicks();
	if (t - T0 >= 5000) {
	    GLfloat seconds = (t - T0) / 1000.0;
	    GLfloat fps = Frames / seconds;
	    printf("%d frames in %g seconds = %g FPS\n", Frames, seconds, fps);
	    T0 = t;
	    Frames = 0;
	}
    }

    return( TRUE );
}

int main( int argc, char **argv )
{
    /* Flags to pass to SDL_SetVideoMode */
    int videoFlags;
    /* main loop variable */
    int done = FALSE;
    /* used to collect events */
    SDL_Event event;
    SDL_Joystick *js;
    /* this holds some info about our display */
    const SDL_VideoInfo *videoInfo;
    /* whether or not the window is active */
    int isActive = TRUE;

    /* initialize SDL */
    if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO) != 0) 
	{
	    fprintf( stderr, "Video initialization failed: %s\n",
		     SDL_GetError( ) );
	    Quit( 1 );
	}

	
	/* We'll use the first joystick for the demonstration. */
	js = SDL_JoystickOpen(0);
	if (js == NULL) {
		printf("Unable to open joystick: %s\n", SDL_GetError());
	}

    /* Fetch the video info */
    videoInfo = SDL_GetVideoInfo( );

    if ( !videoInfo )
	{
	    fprintf( stderr, "Video query failed: %s\n",
		     SDL_GetError( ) );
	    Quit( 1 );
	}

    /* the flags to pass to SDL_SetVideoMode */
    videoFlags  = SDL_OPENGL;          /* Enable OpenGL in SDL */
    videoFlags |= SDL_GL_DOUBLEBUFFER; /* Enable double buffering */
    videoFlags |= SDL_HWPALETTE;       /* Store the palette in hardware */
    videoFlags |= SDL_RESIZABLE;       /* Enable window resizing */
    videoFlags |= SDL_FULLSCREEN;      /* Enable window fullscreen*/

    /* This checks to see if surfaces can be stored in memory */
    if ( videoInfo->hw_available )
	videoFlags |= SDL_HWSURFACE;
    else
	videoFlags |= SDL_SWSURFACE;

    /* This checks if hardware blits can be done */
    if ( videoInfo->blit_hw )
	videoFlags |= SDL_HWACCEL;

    /* Sets up OpenGL double buffering */
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    /* get a SDL surface */
    surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
				videoFlags );

    /* Verify there is a surface */
    if ( !surface )
	{
	    fprintf( stderr,  "Video mode set failed: %s\n", SDL_GetError( ) );
	    Quit( 1 );
	}
	
    /* Hide the mouse pointer. */
    SDL_ShowCursor(0);

    /* Enable key repeat */
    if ( ( SDL_EnableKeyRepeat( 100, SDL_DEFAULT_REPEAT_INTERVAL ) ) )
	{
	    fprintf( stderr, "Setting keyboard repeat failed: %s\n",
		     SDL_GetError( ) );
	    Quit( 1 );
	}

    /* initialize OpenGL */
    initGL( );

#ifdef SOUND
    /* Initialize Audio sub system */
    if ( SDL_InitSubSystem( SDL_INIT_AUDIO ) == -1 )
	{
	    fprintf( stderr, "Could not initialize audio subsystem: %s\n",
		     SDL_GetError( ) );
	    Quit( 1 );
	}

    /* Open the sound device */
    if ( Mix_OpenAudio( 22060, AUDIO_S16SYS, 2, 512 ) < 0 )
	{
	    fprintf( stderr, "Unable to open audio: %s\n", SDL_GetError( ) );
	    Quit( 1 );
	}
    /* Load in the music */
    music = Mix_LoadMUS( "data/lktheme.mod" );
#endif

    /* Read in the data */
    SetupWorld( "data/world.txt" );

    initMap();
    
    
    /* resize the initial window */
    resizeWindow( SCREEN_WIDTH, SCREEN_HEIGHT );

#ifdef SOUND
    /* Start playing the music */
    Mix_PlayMusic( music, -1 );
#endif

    /* wait for events */
    while ( !done )
	{
	    /* handle the events in the queue */

	    while ( SDL_PollEvent( &event ) )
		{
		    switch( event.type )
			{
			case SDL_ACTIVEEVENT:
			    /* Something's happend with our focus
			     * If we lost focus or we are iconified, we
			     * shouldn't draw the screen
			     */
			    if ( event.active.gain == 0 )
				isActive = FALSE;
			    else
				isActive = TRUE;
			    break;			    
			case SDL_VIDEORESIZE:
			    /* handle resize event */
			    surface = SDL_SetVideoMode( event.resize.w,
							event.resize.h,
							16, videoFlags );
			    if ( !surface )
				{
				    fprintf( stderr, "Could not get a surface after resize: %s\n", SDL_GetError( ) );
				    Quit( 1 );
				}
			    resizeWindow( event.resize.w, event.resize.h );
			    break;
			case SDL_KEYDOWN:
			    /* handle key presses */
			    handleKeyPress( &event.key.keysym );
			    break;
			case SDL_QUIT:
			    /* handle quit requests */
			    done = TRUE;
			    break;
		/* This event is generated when an axis on an open
		joystick is moved. Most joysticks have two axes,
		X and Y (which will be reported as axes 0 and 1). */
	   case SDL_JOYAXISMOTION:
		printf("Joystick %i, axis %i movement to %i\n",
		event.jaxis.which, event.jaxis.axis,
		event.jaxis.value);
		if ((event.jaxis.axis == 0)&&(event.jaxis.value > 0)) {yrot -= 1.5f; gira = -1; if (yrot < 0) yrot = 359;}
		if ((event.jaxis.axis == 0)&&(event.jaxis.value < 0)) {yrot += 1.5f; gira = 1; if (yrot > 359) yrot = 0;}
		if ((event.jaxis.axis == 0)&&(event.jaxis.value == 0)) {gira = 0;}
		if ((event.jaxis.axis == 1)&&(event.jaxis.value > 0)) {walk = -1;}
		if ((event.jaxis.axis == 1)&&(event.jaxis.value < 0)) {walk = 1;}
		if ((event.jaxis.axis == 1)&&(event.jaxis.value == 0)) {walk = 0;}
	   break;
		/* The SDL_JOYBUTTONUP and SDL_JOYBUTTONDOWN events
		are generated when the state of a joystick button
		changes. */
	   case SDL_JOYBUTTONUP:
		/* fall through to SDL_JOYBUTTONDOWN */
		if (event.jbutton.button == 0) walk = 0;
		if (event.jbutton.button == 1) walk = 0;
	   break;
	   case SDL_JOYBUTTONDOWN:
		printf("Joystick %i button %i: %i\n",
		event.jbutton.which,
		event.jbutton.button, event.jbutton.state);
		if (event.jbutton.button == 0) walk = -1;
		if (event.jbutton.button == 1) walk = 1;
		if (event.jbutton.button == 7) PlaySound( "data/shot.wav", 0 );
	   break;
			default:
			    break;
			}
		}

	    /* draw the scene */
	    if ( isActive )
		drawGLScene( );
	}

    /* clean ourselves up and exit */
    Quit( 0 );

    return( 0 );
}

