//-----------------------------------------------------//
//
//  uswlib.hpp
//
//	Librerias USW.
//
//	UnSitioWeb.com para programar juegos
//
//	libUSW (http://UnSitioWeb.com)
//	(cc) 2009 Vicente J. Fdez.
//
//-----------------------------------------------------//



#include <stdio.h>		// Libreria standard de entrada y salida.
#include <windows.h>	// Fichero de inclusion para programas Windows.
#include <time.h>		// Fichero de inclusion para funciones de hora.
#include <stdarg.h>		// Fichero de inclusion para argumetos tipo "prinf".
#include <fstream>		// Fichero de inclusion para ficheros.

#include <GL/gl.h>		// Fichero de inclusion de OpenGL.
#include <GL/glu.h>		// Fichero de inclusion de GLU.
						

//------------------------------------------------------------------------
// Pequeña clase para poner un cuadrado con textura en pantalla.
// Se pinta en ortogonal, se planta un dibujo en la pantalla.
// La carga se hara a traves del objeto de aplicacion que hemos creado
// antes y su objeto gestor de texturas.
// Si es un PNG con trasparencia, la parte transperente no se pintara.

class bmpUSW
{
public:
 	// Identificativo de textura para
	// la clase texturasUSW
	GLuint tt;
	
	// Constructor. Sin carga.
	bmpUSW();

	// Constructor. Con carga de textura.
	// *fich:   Fichero a cargar.
	// *z:      Fichero ZIP que lo contiene.
	//          Si es NULL no esta en un ZIP.
	//
	bmpUSW(char *fich, char *z=NULL);

	// Carga, si no se habia cargado.
	// *fich:   Fichero a cargar.
	// *z:      Fichero ZIP que lo contiene.
	//          Si es NULL no esta en un ZIP.
	//
	void carga(char* fich, char* z=NULL);
	
	// Pinta el grafico en pantalla.
	// x:   Coordenada X de la esquina superior derecha.
	// y:   Coordenada Y de la esquina superior derecha.
	// tx:  Ancho que queremos.
	// ty:  Alto que queremos.
	//
	void pinta(int x, int y, int tx, int ty);
};
						
//--------------------------------------------------------------------------
// Estructura para datos del raton.
// Los datos de esta estructura se actualizaran
// por la funcion que trata los eventos de windows
// de la libreria.
// Para saber del raton solo hay que consultar a traves del
// puntero de la clase appUSW.
//
// Se pueden comprobar las coordenadas del cuadro de seleccion,
// cuando mueves el raton con un boton presionado, por
// ejemplo en el escritorio de Windows, y aparece un cuadrado para
// seleccionar iconos.
// Al soltar cualquiera de los 3 botones se puede comprobar el
// cuadro de seleccion. En la siguiente pasada del bucle se
// igualaran las coordenadas.
// El cuadro de seleccion sera el area de un cuadrado con
// una esquina en las coordenadas "x,y", y la esquina contraria
// en las coordenadas "prev_x,prev_y".


struct ratonUSW
    {
	// Coordenada X actual del raton.
	int x;
	// Coordenada Y actual del raton.
	int y;
	// Coordenada X del cuadrado de seleccion
	int prev_x;
	// Coordenada Y del cuadrado de seleccion.
	int prev_y;
	// Si el boton izquierdo esta presionado.
	bool izquierdo;
	// Si se hacaba de soltar el boton izquierdo.
	bool suelto_izq;
	// Si se ha hecho doble click con el boton izquierdo.
	bool doble_izq;
	// Si el boton derecho esta presionado.
	bool derecho;
	// Si se hacaba de soltar el boton derecho.
	bool suelto_der;
	// Si se ha hecho doble click con el boton derecho.
	bool doble_der;
	// Si el boton central esta presionado.
	bool centro;
	// Si se hacaba de soltar el boton central.
	bool suelto_cen;
	// Al mover la rueda del raton, tendra valor positivo
	// si se ha movido hacia adelante, y valor negativo
	// si la rueda se ha movido hacia atras.
	// Si no se ha movido sera 0.
 	int rueda;
    };



//--------------------------------------------------------------------------
// Clase para LOG.
// Si se habilita la log al crear el objeto,
// crea un fichero done luego se podra escribir 
// lo que creamos conveniente en este fichero.
// Si no se habilita la log sera un objeto inutil.
// Al destruirse el objeto cerrara el fichero.

class ErrLogUSW
{
private:
	// Indica si se ha habilitado la log.
	bool log;
	// Stream para el fichero.
	std::fstream vlog;
	// Indicador interno para nueva linea.
	bool nl;
public:
	// Constructor. No inicia la log.
	ErrLogUSW();
    
	// Constructor. Inicia la log si l=true.
	// *f:  Fichero de log.
	//  l:	Si inicializa la log o sera un
	// 	objeto inutil.
	ErrLogUSW(char* f,bool l=true);
	
	// Destructor. Si se creo el fichero
	// ahora se cerrara.
	~ErrLogUSW();
	
	// Inicia la log en el fichero *f,
	// en caso de no haberse inicializado
	// antes.
	void Inicia(char* f);
	
	// Pone lo que queramos en el fichero de log.
	// La funcion es del tipo "printf", con la misma
	// forma de usarse.
	// Si terminamos la cadena de caracteres con "\n"
	// el objeto entendera que debe cambiar de linea y
	// la proxima escritura la hara en otra linea y
	// añadiendo la hora y fecha.
	void Pon(const char* l,...);
} ;

//--------------------------------------------------------------------------
// Clase para controlar una tecla y que solo se retorne true la primera
// vez al pulsarla. No volvera a retornar true hasta que la tecla sea
// soltada y presionada de nuevo.
// Tambien hace la funcion de un interruptor o switch asociado a la tecla.
// Se usa el "Virtual Key Code" para identificar las teclas (ver el capitulo
// 10 del curso de programacion de juegos de "http://UnSitioWeb.com" )
class ControlTeclaUSW
{
    private:
	// Indicador auxiliar.
  	bool LevantaTecla;
	// Virtual Key Code de la tecla.
	int tec;
	// El interruptor o bandera interna.
	bool sw;
    public:
	// Constructor.
	// t: 	Virtual Key Code de la tecla que
	//      queremos controlar.
	//
	// El interruptor se inicializa a false.
        ControlTeclaUSW(int t);

	// Funcion que retorna true si se ha pulsado la
	// tecla, solo la primera pasada del bucle del
	// programa. No retornara true hasta que la tecla
	// se haya soltado y se haya presionado de nuevo.
	// Esta funcion no cambia el estado de la bandera.
        bool Pulsacion();
        
	// Retorna el estado del interruptor, true o false.
	// Internamente comprueba si se ha pulsado la tecla
	// y, si es el caso, cambia el interruptor.
	bool Interruptor();
        
	// Pone el indicador en el estado que queramos.
	// Util para inicializarlo en un estado o para
	// cambiarlo sin haber pulsado la tecla.
	void PonInterruptor(bool i);

};
//--------------------------------------------------------------------------
// Clase para texto (en textura). Carga y usa ficheros creados por
// el programa fonttex.exe.
// Basado en "LFontRenderer" de Lev Povalahev, Copyright (c) 2002.
// Para el acceso a ZIPs usa ZLIB y MINIZIP ("http://www.zlib.net").
//
// La modificacion hace que se puedan cargar las fuentes desde
// un fichero ZIP o desde un recurso definido por el usuario
// cuyo tipo sea "FONTTEX" en el fichero rc.
// Ejemplo de entradas en fichero rc:
//	#define LETRA_1 109
// 	LETRA_1 FONTTEX font/comic.fnt
//
class letraUSW
{
private:
	// Indicador para saber si se ha cargado
	// alguna fuente o no.
	bool hay;
	// Numero de fuentes cargadas.
	int nfuentes;
	// Para los nombres de ficheros.
	char fuentes[10][100];
	// Para los nombres asignados a cada fuente.
	char nombre[10][20];
	// Para los zips de donde estaban.
	char zips[10][100];

public:
	// Constructor.
	letraUSW();

	// Se debe ejecutar si cambia el viewport.
	void Refresca();
	
	// Carga una fuente.
	// Debe ser un fichero creado por el programa "fonttex.exe".
	// Se puede cargar desde disco, desde un fichero ZIP o
	// desde un recurso.
	//
	// *n:  Nombre que vamos a asignar a esta fuente para luego
	//      poder llamarla por el.
	// *l:  Nombre del fichero generado por "fonttex.exe" a
	//      cargar. En caso de cargar desde recurso no se
	//      usa. Se pondra "".
	// *zf: Nombre del fichero zip donde esta el fichero de fuente.
	//      NULL indica que el fichero de fuente no esta en un zip 
	//      si no en disco directamente. Si cargamos desde un recurso
	//      pondremos "".
	// r:   Identificador del recurso donde esta la fuente.
	//      0 indica que no esta en un recurso.
	//      El recurso debe ser del tipo "FONTTEX".
	//
	void Carga(char *n, char *l, char *zf=NULL, int r=0);
	
	// Pondremos el color con el que pintar
	// el texto.
	void Color(float r,float g, float b);

	// Pondremos el alto de las letras.
	// El ancho se modificara proporcionalmente.
	void Alto(int t);

	// Pondremos el ancho. Esta no afecta al alto.
	void Ancho(float t);

	// Podemos escribir en italica.
	void Italica(bool s);
	
	// Podemos indicar un angulo y
	// no escribir recto.
	void Giro(float g);
	
	// Seleccionamos una fuente de las que hemos
	// cargado, mediante un indice (empezando en 0).
	void Fuente(int f);
	
	// Seleccionamos una fuente de las que hemos
	// cargado, mediante en nombre que le pusimos
	// al cargarla.
	void Fuente(char *f);
	
	// Podemos saber la longitud de una cadena tal y
	// como se pintaria en pantalla.
	GLuint Longitud(char *c);

	// Despues de indicar como queremos escribir, podemos
	// escribir con esta funcion al estilo "printf".
	// x e y seran las coordenadas en pantalla en pixels.
	// Todavia no se pintara nada en pantalla hasta que se
	// ejecute la funcion "Pon".
	void Escribe(int x, int y, const char *l,...);
	
	// Tras escribir una o mas lineas con la funcion "Escribe",
	// esta funcion las pinta en pantalla.
	void Pon();
};

//--------------------------------------------------------------------------
// Clase para cargar y gestionar texturas.
// Se ocupa de cargarlas desde fichero de grafico o desde dentro de
// un fichero ZIP.
// Si se carga una textura cuyo nombre es igual a una cargada antes,
// usara la anterior y no hara la carga (para prevenir problemas de memoria).
// Las texturas se pueden borrar para liberar espacio para otras.
// Al destruirse el objeto, borra todas las texturas que tiene en memoria.
// Al cargar una textura retorna un GLuint que NO es el identificador de
// textura de OpenGL. Es un identificador para usar solo con este objeto
// de gestion de texturas.
//
// Carga ficheros BMP, PNG, JPG y TGA.
// Usa la libreria SOIL para la carga (si no carga algun fichero ver
// documentacion de la pagina de SOIL "http://lonesock.net/soil.html").
// Para el acceso a ZIPs usa ZLIB y MINIZIP ("http://www.zlib.net")

class texturasUSW
{
private:
	// Maximo numero de texturas que acepta.
	GLuint	MAXTEX;
	GLuint *repetidas;	// Almacen para el contador de cargas de cada textura
	GLuint *Texturas;	// Almacen para los identificativos de texturas OGL
	GLint MaxTex;		// Indice que indica el ultimo indice usado de Texturas[]
	char   (*Ficheros)[30];	// Almacen para los nombres de fichero de las texturas
	GLuint (*tam)[2];	// Almacen para el tamaño original de la textura

public:
	// Constructor.
	texturasUSW();

	// Constructor que ademas reserva espacio.
	// mt: 	Maximo numero de texturas que se podran
	//      tener cargadas a la vez.
	//
	texturasUSW(GLuint mt);

	// Destructor.
	~texturasUSW();

	// Reserva de espacio. Solo se debe llamar
	// si se ha creado el objeto con el constructor
	// sin parametros.
	// mt: 	Maximo numero de texturas que se podran
	//      tener cargadas a la vez.
	//
	void Reserva(GLuint mt=30);
    
	// Carga un fichero grafico y crea la textura OpenGL
	// correspondiente.
	//
	// *fichero:  	Nombre del fichero a cargar.
	// *ficherozip:	Nombre del fichero zip en que esta.
	//              NULL si no esta en un zip.
	// mipm:        Si crea mipmaps o no.
	// alreves:     Si da la vuelta a la textura con
	//              respecto al eje X (lo de arriba a abajo).
	// &tamx:       Direccion de una variable donde dejar el
	//              ancho del grafico original. NULL si no
	//              nos hace falta ese dato.
	// &tamy:       Direccion de una variable donde dejar el
	//              alto del grafico original. NULL si no
	//              nos hace falta ese dato.
	//
	// Retorna el identificador de la textura para este objeto.
	// Se puede usar luego con "Bind" y "Borra".
	// N0 es el identificador de textura de OpenGL.
	GLuint Carga(	char *fichero, char *ficherozip=NULL,
			bool mipm=false, bool alreves=false,
			GLuint *tamx=NULL, GLuint *tamy=NULL);

	// Hace bind de la textura para usarla en el programa con
	// OpenGL (lo mismo que "glBindTexture(GL_TEXTURE_2D,...)" ).
	// t: Es el identificativo de textura retornado por "Carga".
	//
	void Bind(GLuint t);

	// Permite borrar textura que no usemos mas.
	// t: Es el identificativo de textura retornado por "Carga".
	//
	void Borra(GLuint t);
};


//--------------------------------------------------------------------------
// Clase para la aplicacion.
// Solo se debe crear una en el programa.
// Sera el objeto principal del programa y el que nos haga el "trabajo sucio".
// Crea una ventana centrada de 600x400 cuyo titulo es
// "UnSitioWeb.com", si habilitamos log crea un fichero llamado
// "log.txt" e inicializa OpenGL. La ventana no se podra cambiar de
// tamaño con el raton, solo por programa.
// Despues podemos cambiar el tamaño y titulo de la ventana o poner el
// programa a pantalla completa.

class appUSW
{
private:
	HINSTANCE IdAplicacion; // Para guardar el identificador del programa
	HWND IdVentana;  // Para guardar el identificador de la ventana
	HDC DevContex;	// Device Context para conectar la ventana con OpenGL.
	 HGLRC OGLrc;	// Render Context para la ventana OpenGL.
	// Array para guardar el titulo de la ventana.
	char TituloVentana[200];
	// Punteros a las funciones de usuario.
	void (*fInicializa)(); // Puntero para inicializacion
	void (*fBucle)();      // Puntero para el bucle principal
	void (*fCierra) ();    // Puntero para liberar memoria al cerrar.
	// Definimos un rectangulo (programacion windows)
	// lo usaremos para almacenar el area cliente de nuestra ventana
	RECT rect;
	// Variable para guardar nuestro calculo
	 // de FPS reales
	double FPS_reales;
	// Variable donde ponemos los FPS a los que
	// queremos que vaya nuestro programa.
	int FPS_que_queremos;
	// Indicador para saber si estamos
	// a pantalla completa o no.
	bool PantallaCompleta;
	MSG Mensaje; // Varible para contener los mensajes de windows.
	// Definimos tres variables de 64 bits que usaremos para
	// obtener el dato de tiempo en cada pasada del bucle
	LARGE_INTEGER t1,t2,frecuencia;
	// Esta variable de tipo coma flotante de doble precision
	// para guardar el tiempo en segundos que tarda cada pasada
	// del programa.
	double segundos;
	// Para el identificdor del icono de la ventana.
	int IDIcono;
	// Funcion que crea la ventana principal.
	void IniciaVentana( 	bool PanCom=false );
	// Funcion que inicializa OpenGL.
	void IniciaGL(int stencil = 0 );

       

public:
	// Constructor.
	//
	// MT:		Numero maximo de texturas a usar a la vez.
	// log:		true o false. Habilita o no la posibilidad de usar
	//			fichero de log.
	// icono:   	Identificador del icono (el del archivo de recursos)
 	//          	para el icono de la ventana. 0: sin icono.
	 // stencil: 	Si es 0 (valor por defecto) no se usa stencil buffer.
	//          	Otro valor seran los bit del buffer de stencil.
	//
	// Crea una ventana centrada de 600x400 cuyo titulo es
	// "UnSitioWeb.com", si habilitamos log crea un fichero llamado
	// "log.txt" e inicializa OpenGL.
	appUSW(GLuint MT=30,bool log=false, int icono=0, int stencil= 0);
	
	// Destructor
	~appUSW(); 

	// Puntero para acceder a la matriz de teclado.
	// El indice a usar sera el Virtual Key Code (ver el capitulo
	// 10 del curso de programacion de juegos de "http://UnSitioWeb.com" )
	// Por ejemplo para controlar la tecla "E" sera: "Teclas['E']".
	// Por ejemplo para controlar la tecla "F5" sera: "Teclas[VK_F5]".
	bool *Teclas;
        
	// Puntero para acceder a los datos de raton.
	// Ver estructura "ratonUSW".
	// Por ejemplo para controlar el boton derecho sera: "Raton->derecho".
	ratonUSW *Raton;
    
	// Objeto para gestionar la log.
	// Ver su declaracion de clase.
	// Si se habilita la log en el constructor, creara un fichero llamado
	// "log.txt" donde se escribira la log.
	ErrLogUSW Log;
    
	// Objeto para gestionar la escritura de texto.
	// Ver su declaracion de clase.
	// Antes de usarlo se debe cargar una fuente.
	letraUSW Letra;
    
	// Objeto para gestionar texturas.
	// Ver su declaracion de clase.
	texturasUSW Tex;

	int AnchoVentana;	// Lo que dice el nombre de la variable
	int AltoVentana;	// Lo que dice el nombre de la variable
	int PosXventana;	// Lo que dice el nombre de la variable
	int PosYventana;	// Lo que dice el nombre de la variable

	// Funcion para poner lo que queramos como titulo de ventana.
	void PonTitulo(char *titulo);
    
	// Funcion para poner pantalla completa y cambiar resolucion.
	//
	// x:   Ancho en pixels de la resolucion que queremos.
	// y:   Alto en pixels de la resolucion que queremos.
	// bpp: Bits Por Pixel que buscamos.
	//
	// Si esta funcion no encuentra en nuestra tarjeta una
	// resolucion como la que le decimos, pondra el programa
	// a pantalla completa manteniendo la resolucion actual.
	// Si esto ultimo es lo que se quiere, mejor dejar los
	// valores por defecto (0,0,0).
	bool PantaCompleta(int x=0, int y=0, int bpp=0);

	// Cambia el tamaño de la ventana.
	// Si esta en pantalla completa, regresa a la resolucion
	// del sistema y pne la aplicacion en ventana de nuevo.
	//
	// x:   Ancho de la ventana.
	// y:   Alto de la ventana.
	// px:  Coordenada X de la esquina superior izquierda de la
	//      ventana. Posicion X.
	// py:  Coordenada Y de la esquina superior izquierda de la
	//      ventana. Posicion Y.
	//
	// Si se deja el valor por defecto de px y py la ventana
	// aparece centrada.
	void TamVentana( int x, int y, int px=-1, int py=-1);
    
	// Retorna el ancho del area cliente de la ventana en
	// pixels, que coincide con el ancho del viewport de 
	// OpenGL (el ancho de nuestro area de dibujo).
	int TamX() ;
    
	// Retorna el alto del area cliente de la ventana en
	// pixels, que coincide con el alto del viewport de 
	// OpenGL (el alto de nuestro area de dibujo).
	int TamY() ;
       
	// Funcion para pasarle a nuestro objeto aplicacion
	// la funcion de inicializacion de los datos de nuestro
	// programa, carga de ficheros, creacion de objetos, ...
	void PonFuncionInicia(void (*fI)() );
    
	// Funcion con la que le decimos al objeto aplicacion
	// cual va a ser la funcion del bucle principal del
	// programa.
	void PonFuncionPrincipal( void (*fB)() );

	// Funcion para decirle al objeto aplicacion cual es
	// nuestra funcion de cierre, para liberar memoria o lo
	// que creamos oportuno antes de terminar el programa.
	void PonFuncionCierre( void (*fC) () );

	// Para poner los FPS deseados.
	void PonFPS(int fps);
    
	// Nos dice los FramesPorSegundo a los que vamos.
	double DimeFPS();
    
	// Funcion que cambia los bufferes trasero y frontal
	// ( back buffer y front buffer).
	// No es necerario usarla al final de nuestro bucle
	// principal, la funcion "Corre" cambia los bufferes
	// despues de cada frame.
	// La podemos usar si queremos algun cambio entre
	// medias para algun efecto o lo que se nos ocurra.
	void CambiaBuffer() ;

	// Esta funcion ejecuta la funcion de inicializacion
	// de usuario (la que le pasamos con "PonFuncionInicia" )
	// y luego entra en el bucle principal de la aplicacion
	// (la que le pasamos en "PonFuncionPrincipal" ).
	// Controla la velocidad segun los PFS por defecto (50)
	// o los que le digamos ( "PonFPS" ).
	// Tambien termina de escribir el pantalla el texto
	// que tengamos pendiente del objeto "Letra".
	// Antes de nuestro bucle principal limpia los bufferes
	// de Color y Profundidad, y despues intercambia los
	// bufferes trasero y delantero.
	// Cuando termina la aplicacion (al presionar "ESCape")
	// ejecuta la funcion de finalizacion de usuario
	// (la que le pasamos en "PonFuncionCierre" ).
	int Corre();
	
	// Funcion que hace que se termine el programa, no sin
	// antes ejecutar la funcion de cierre y borrar lo
	// necesario. De forma ordenada.
	void Terminar();
};