#ifndef _MA_ALLEGRO_H_
#define _MA_ALLEGRO_H_

#include <png.h>
#include <allegro.h>
#include "loadpng/loadpng.h"
#include "ma_classfactory.h"
#include "utils/utils.h"

// Definición de Prototipos y Tipos de Datos, 
// para facilitar referencias cruzadas
//
class TMABitmap;
class TMAGame;
class TMAScene;
class TMAImageBank;
class TMAFont;
class TMAMidi;
class TMAOgg;
class TMAOggMem;
class TMASound;
class TMASprite;

typedef TMABitmap *PMABitmap;
typedef TMAGame* PMAGame;
typedef TMAScene *PMAScene;
typedef TMAImageBank* PMAImageBank;
typedef TMAFont* PMAFont;
typedef TMAMidi* PMAMidi;
typedef TMAOgg*  PMAOgg;
typedef TMASound* PMASound;
typedef TMASprite *PMASprite;

// ***********************************
// Clase principal del nucleo: TMAGame
// ***********************************
//
class TMAGame : public TMAObject
{
    REGISTER_CLASS protected:
        
        static TMAGame* oc_MAGame;
        static bool ci_endgame;
        static bool ib_soundAvailable;
        
        int ii_game_speed;
        
        PMABitmap ia_screen_buffer[2];
        
        bool make_screen_buffer(int width,int height,int depth);
        void flip_screen_buffer(void);
        
        PMAScene io_current_scene;
        PMAScene io_next_scene;
        bool ib_remove_last_scene;
        
    public:
        PUBLIC_CLASS_ID;    
        static long il_time_millis;
        
        
        // Constructor y Destructor
        //
        TMAGame(void);
        virtual ~TMAGame();
        
        // Limpia el buffer de teclas y los flags de estados
        //
        static void releaseKeys(void);
        
        // Devuelve un puntero a la instancia del juego actual.
        //
        static TMAGame *getCurrentGame(void);          
        
        // Devuelve un color en formato RGB
        //
        static int getRGBColor(int red,int green,int blue);
        
        // Detiene la ejecución durante ciertos milisegundos
        //
        static void sleep(int ms);
        
        // Devuelve el tiempo actual en milisegundos
        //
        static long getCPUTime(void);
        
        // En este método especificarse la rutina de inicialización global 
        // del juego
        //
        virtual bool init(void) { return true; }
        
        // En este método especificarse la rutina de limpieza global 
        // del juego
        //
        virtual void cleanup(void);
                  
        // Este método se debe invocar para que comience el ciclo principal
        // del juego
        //
        void run(void);
        
        // Método para mostrar mensajes de Error
        //
        static void showAlert(const char *);      // Muestra una alerta.
        static void showLastAlert();        // Muestra la ultima que genero un error.
        void showFatalError(const char *);        // Muestra una alerta y cierra la aplicación.
         
        // Establece o Recupera el escenario del juego
        //
        bool setScene(PMAScene,const PMAObject params = NULL,bool remove_last=true);
        const PMAScene getScene(void) { return io_current_scene; }
         
    // ***********************************
    // Métodos de control sobre el entorno
    // *********************************** 
        
        // Incializa el modo grafico.
        //
        bool setGraphicsMode(int width,int height,int colordepth,bool fullscreen);
        
        // Establece el titulo de la ventana principal.
        //   
        void setWindowTitle(const char *);
        
        // Devuelve el tamaño ancho/alto de pantalla establecido
        //
        void getScreenSize(int& width,int& height);
        int getScreenWidth(void);
        int getScreenHeight(void);
        PMABitmap getScreenBuffer(void);
        PMABitmap getScreen(void);
        int getScreenBufferDepth(void);
        
        bool isFullScreen(void) { return !is_windowed_mode(); }
        
    // ***********************************
    // Métodos de control sobre el flujo
    // *********************************** 
    
        // Establece la velocidad de refresco del juego en Frames por Segundo
        // por defecto se asume 0 fps, lo que hace que exista la máxima 
        // velocidad posible.
        //
        void setGameFPS(int fps);

        // Este método se invoca en cada iteración del juego.
        // La idea es introducir aqui llamadas a calculos o controles.
        // Si existe una escena asignada, TMAScene, se invoca el doRun 
        // de la escena, en otro caso, se invoca el del juego.
        //
        virtual void doRun(void){};

        // Este método se invoca en cada iteración del juego para
        // que se dibuje lo que sea necesario.
        // Si existe una escena asignada, TMAScene, se invoca el doPaint
        // de la escena, en otro caso, se invoca el del juego.
        //
        virtual void doPaint(TMABitmap&) {};
        
        // Este método termina con la ejecución del juego.
        //
        static void endGame(void);

    // *******************************
    // Métodos de control sobre sonido
    // *******************************
    
        static bool isSoundAvailable(void) { return ib_soundAvailable; }
        static void setSoundAvailable(bool av) {  ib_soundAvailable=av;}       
    
};

// ****************************************
// Clase que encapsula una escena: TMAScene
// ****************************************
//
class TMAScene : public TMAObject
{
    REGISTER_CLASS protected:
                
        bool ib_initialized;
        int ii_Estado;
        
    public:
        PUBLIC_CLASS_ID;
        TMAScene();
        
        virtual bool doInit(const PMAObject params=NULL);
        
        // Este método se invoca en cada iteración del juego.
        // La idea es introducir aqui llamadas a calculos o controles.
        //
        virtual bool doRun(void){ return false; };
        
        // Este método se invoca en cada iteración del juego para
        // que se dibuje lo que sea necesario.
        //
        virtual void doPaint(TMABitmap&){};
        
        bool isInitialized(void) { return ib_initialized; }
};        

// **************************************
// Clase que encapsula un BITMAP: TMAGame
// **************************************
//
class TMABitmap : public TMAObject
{
    REGISTER_CLASS protected:
        
        friend class TMAGame;

        static PMAGame GMAGame;
        BITMAP *io_bmp;
        bool ib_destroy;
    
        TMABitmap(BITMAP *,bool shared=false);       
        
    public:
        PUBLIC_CLASS_ID;
        ~TMABitmap();

    // ***********************************************
    // Métodos de creación de BITMAPS/Buffers Graficos
    // ***********************************************
        
        // Crea un bitmap a partir de un archivo de imagen
        //
        static PMABitmap loadFromFile(const char *filename);
        
        // Obtiene una copia de un bitmap especificado
        //
        static PMABitmap create(PMABitmap);
        
        // Crea un bitmap de la misma profundidad que 
        // el entorno de tamaño especificado.
        // Puede crearse en memoria de video.
        //
        static PMABitmap create(int width,int height,bool videomem);
        
        // Crea un bitmap del tamaño y profundidad especificados.
        //
        static PMABitmap create(int width,int height,int depth);
        
        // Devuelve la referencia al BITMAP interno..
        //
        inline BITMAP *getInternalBitmapRef(void) { return io_bmp; }
                        
    // ****************************************
    // Métodos para la obtención de propiedades
    // ****************************************
        
        bool saveToFile(const char *filename);
        int getColorDepth(void);
        int getWidth(void);
        int getHeight(void);
        void getSize(int& width,int& height);

    // **************************************
    // Métodos operaciones sobre el TMABitmap
    // **************************************
        
        void clear(int red,int green,int blue);
        void clear(int color);
        void setTransparentColor(int color);
        void draw(const PMABitmap,int px,int py,bool transparent);
        void draw(const PMABitmap,int ox,int oy,int width,int height,int dx,int dy,bool transparent);
        void drawSprite(const PMABitmap,int px,int py,bool flipx=false,bool flipy=false);
        void drawSpriteAlpha(const PMABitmap,int px,int py,bool flipx=false,bool flipy=false,int alpha=255);
        void drawSpriteColorized(const PMABitmap,int px,int py,bool flipx=false,bool flipy=false,int r=255,int g=255,int b=255);
        void drawLine(int ox,int oy,int dx,int dy,int color);
        void drawRect(int ox,int oy,int dx,int dy,int color);
        void drawRectFilled(int ox,int oy,int dx,int dy,int color);
        void drawCircle(int cx,int cy,int radio,int color);
        void drawCircleFilled(int cx,int cy,int radio,int color);               
        void drawEllipse(int cx,int cy,int radiox,int radioy,int color);
        void drawEllipseFilled(int cx,int cy,int radiox,int radioy,int color);
        void paint(int px,int py,int color);        
        void setPixel(int px,int py,int color);
        int  getPixel(int px,int py);
        
        // ------------ Operaciones Alpha
        //
        void modifyAlphaChannel(void);
        void endModifyAlphaChannel(void);
        void setAlphaBlending(void);
        void drawAlphaBlending(const PMABitmap,int px,int py);
        void setAlpha(int alpha);
        void drawAlpha(const PMABitmap,int px,int py);
        
        // ------------ Operaciones de Resize y Filtrado
        //
        void bilinearStretch(const PMABitmap,int ox,int oy,int owidth,int oheight,
                            int dx,int dy,int dwidth,int dheight);
};

// *************************************************
// Clase que encapsula un BANCO de IMAGENES: TMAGame
// *************************************************
//
class TMAImageBank : public TMAObject
{
    REGISTER_CLASS protected:
        
        TMAImageBank(){};
        TMAVector vector;
        
    public:
        PUBLIC_CLASS_ID;
       
        // Crea un banco de imágenes de tamaño fijo a partir de un TMABitmap
        // La imagen original no se destruye, pero una vez creado el banco
        // tampoco se hace referencia a ella.
        //
        static PMAImageBank createStatic(TMABitmap&,int tx,int ty,int ax,int ay,bool separator=false);
        
        // Crea un banco de imágenes de tamaño fijo a partir de un archivo de imagen
        // 
        static PMAImageBank createStatic(const char *filename,int tx,int ty,int ax,int ay,bool separator=false);
        
        // Crea un banco de imágenes donde cada imagen puede ser de un tamaño, a partir
        // de un bitmap y un vector de objetos (TMARectangle)
        //
        static TMAImageBank *createDynamic(TMABitmap&,TMAVector& zonas);
        
        // Crea un banco de imágenes donde cada imagen puede ser de un tamaño, a partir
        // de un bitmap y un vector de objetos (TMARectangle)
        //
        static TMAImageBank *createDynamic(const char *filename,TMAVector& zonas);
        
        // Devuelve una imagen del banco de imágenes, que esta indexada
        // por el indice especificado.
        //
        const PMABitmap getBitmap(int index);
        
        // Devuelve el numero de entradas en el banco.
        //
        long size(void);
};

// *****************************************************
// Clase que encapsula un tipo de letra grafico: TMAFont
// *****************************************************
//
class TMAFont : public TMAObject
{
    REGISTER_CLASS protected:
        
        PMAImageBank io_bank;
        int width,height;
        int ii_spacex,ii_transcolor;
        const char *is_chars;
        
    public:
        PUBLIC_CLASS_ID;
        
        TMAFont();
        ~TMAFont();
        
        // Carga un tipo de letra.
        //
        bool load(const char *fontFile,int width,int height,int col,int row,
                  bool pixel_separator,int transcolor);
	        
        // Establece el espacio horizontal entre caracteres.
        //
        void setSpaceBetweenChars(int);
        
        // Establece el juego de caracteres
        //
        void setCharacters(const char *);
        
        // Devuelve la anchura de un texto
        //
        int getTextWidth(const char *);
        
        // Devuelve la altura de un texto
        //
        int getTextHeight(void);
        
        // Dibuja un texto sobre un bitmap.
        // Si X=-1, se centra el texto en horizontal, asumiendo que la anchura
        // de la pantalla se indica en el parametro "scrwidth"
        // El parametro offsety, puede apuntar a un array de enteros que se modificaran
        // la posición Y de cada letra. Debe contener tantos elementos como la anchura
        // de la pantalla.
        //
        void drawText(TMABitmap&,const char *text,int x, int y,int *offsety=NULL,
                      int scrwidth=0,bool blending=false);        
};

// *********************************************
// Clase que encapsula una melodia midi: TMAMidi
// *********************************************
//
class TMAMidi : public TMAObject
{
    REGISTER_CLASS protected:
        
        static MIDI* sp_current_midi;
        MIDI *pmidi;    
        void clear(void);
        
    public:
        PUBLIC_CLASS_ID;
        
        TMAMidi();
       ~TMAMidi();
       
        bool load(const char *filename);
        void play(bool loop=false);
        void stop(void);
        void pause(void);
        void resume(void);
};

// ***************************************
// Clase que encapsula un sonido: TMASound
// ***************************************
//
class TMASound : public TMAObject
{
    REGISTER_CLASS protected:
        
        SAMPLE *psound; 
        int ii_voice;   
        
    public:
        PUBLIC_CLASS_ID;
        
        TMASound();
       ~TMASound();
       
        bool load(const char *filename);
        void unload(void);
        void play(bool loop=false,int volume=128);
        void stop(void);
        bool isPlaying(void);
};


// ****************************************
// Clase que encapsula un sprite: TMASprite
// ****************************************
//
class TMASprite : public TMAObject
{
    REGISTER_CLASS protected:
 
        bool ib_valido;                 // Indica si es valido
        int  ii_Frame;                  // Fotograma actual
        int  ii_Retardo;                // Retardo de fotograma
        int  ii_RetardoPos;             // Contador de Retardo
        const int *ia_AnimSequence;     // Secuencia de animación actual
        int  ii_AnimSequenceLength;     // Secuencia de animación actual
        int  ii_EstadoAnim;             // Estado de la animación actual
        int  ii_Estado;                 // Estado general del Sprite
        int  ii_CurrentFotograma;       // Fotograma Actual
        bool ib_flip_x;                 // Sprite invertido X
        bool ib_flip_y;                 // Sprite invertido Y
        
        PMAImageBank io_images;   // Banco de Imágenes
        
    public:
        PUBLIC_CLASS_ID;
        TMARectangle position;        
        
        TMASprite(PMAImageBank);
        virtual ~TMASprite(){}
       
        bool isValid(void)      { return ib_valido;}
        void validate(void)     { ib_valido=true; }
        void invalidate() 		{ ib_valido=false; }
        void setFlipX(bool b)	{ ib_flip_x = b; }
        void setFlipY(bool b)	{ ib_flip_y = b; }
        bool isFlippedX(void)   { return ib_flip_x; }
        bool isFlippedY(void)   { return ib_flip_y; }
        
        virtual void animate(void);
        virtual bool isCollisioningWith(TMASprite& s);
        virtual void putAt(int x,int y) { position.x = x; position.y = y; }	
        virtual bool draw(TMABitmap&);
        
        void setAnimSequence(const int *,int length);
};

#endif
