#ifndef _TSPRITEGLAURUNG_CPP_
#define _TSPRITEGLAURUNG_CPP_

#include <math.h>
#include "../main/glaurung.h"
#include "sprites.h"

REGISTER_CLASS_ID(TSpriteGlaurung);

int TSpriteGlaurung::ia_SaltoLargo[240];
int TSpriteGlaurung::ia_SaltoCorto[240];
int TSpriteGlaurung::ia_SaltoRecto[240];
int TSpriteGlaurung::ia_AnimMuerte[8]={96,97,98,99,100,101,102,103};
PMAScreenMap TSpriteGlaurung::io_Screen=NULL;
TMARandom TSpriteGlaurung::miRND;
TGlaurungGame *TSpriteGlaurung::miGame=NULL;
PSpriteBurladero TSpriteGlaurung::ispr_Burladero=NULL;

TSpriteGlaurung::TSpriteGlaurung(PMAImageBank bank):TMASprite(bank)
{
    spr_id=-1;
    MemPos_flipx=false;
    MemPos_flipy=false;
    ia_Salto=NULL;
    ii_Salto=0; ii_SaltoVel=0;
    ib_Salto=false;
    ii_SaltoPX=0,ii_SaltoPY=0;
    ib_muriendo=false;
    ii_tilesx=0; ii_tilesy=0;
    if (miGame==NULL) miGame = (TGlaurungGame*)TMAGame::getCurrentGame();
}

void TSpriteGlaurung::makeJumpArray(void)
{
	// Calculamos la tabla de senos para la 
	// onda vertical del salto.
	//
	int i;
	double dxc = (3.1415926535) / 240;
    double dxl = (3.1415926535/2) / 80;
    double dxi = (3.1415926535/2) / 40;
	double valc=0,vall=0,vali=0;

	for(i=0;i<240;i++)
	{
        // Calculo del arco del salto largo.
        //
        if (i<=80)            
        {
         ia_SaltoLargo[i]=(int)(_RadioSaltoLargo*sin(vall));
         vall+=dxl;
        }

        // Calculo del Arco medio del salto largo
        //
        if (i>80 && i<=160)
        {
            ia_SaltoLargo[i]=_RadioSaltoLargo+(int)(10*sin(vali));
            vali+=dxi;
        }
        
        // Copia por la derecha del arco del salto largo
        //
        if (i>160)
        {
            ia_SaltoLargo[i]=ia_SaltoLargo[120-(i-120)];
        }

        // Calculo del arco del salto corto y del salto sin direccion
        //
        ia_SaltoCorto[i]=(int)(_RadioSaltoCorto*sin(valc));
        ia_SaltoRecto[i]=(int)(_RadioSaltoLargo*sin(valc));
        valc+=dxc;
    }
}
	
void TSpriteGlaurung::initialize(void)
{
    makeJumpArray();
}

int TSpriteGlaurung::getDistance(TSpriteGlaurung& spr)
{
    int dx = spr.position.x - position.x;
	int dy = spr.position.y - position.y;
	return (int)sqrt( dx*dx + dy*dy );
}

void TSpriteGlaurung::savePosition(void)
{
	rect_MemPos.setBounds(position);
	MemPos_flipx = ib_flip_x;
	MemPos_flipy = ib_flip_y;
}

void TSpriteGlaurung::restorePosition(void)
{
	position.setBounds(rect_MemPos);
	ib_flip_x = MemPos_flipx;
	ib_flip_y = MemPos_flipy;
}

// Indica si hay techo por encima de una cuadricula
//
bool TSpriteGlaurung::isTop(int px,int py)
{
	for (int i=0;i<ii_tilesx;i++)
	{
        if (py-ii_tilesy>=0)
        {
		    if (io_Screen->getMask(px+i,py-ii_tilesy) == 1) return true;
        }
	}
	return false;
}

// Indica si hay suelo por debajo de una cuadricula
//
bool TSpriteGlaurung::isDown(int px,int py)
{
	for (int i=0;i<ii_tilesx;i++)
	{
		if (io_Screen->getMask(px+i,py+1) == 1) return true;
	}
	return false;
}

// Indica si es posible caminar a la derecha
//
bool TSpriteGlaurung::canWalkRight(int px,int py)
{
    int lMask,i;
    for (i=0;i<ii_tilesy;i++)
    {
        lMask=io_Screen->getMask(px+ii_tilesx,py-i);
        if (!evalMaskRight(lMask)) return false;
    }
	return true;
}

// Por defecto se puede caminar a la derecha si no chocamos
// con un muro o con una puerta o con el burladero.
//
bool TSpriteGlaurung::evalMaskRight(int mask)
{
	return (!(mask == 1 || mask == 7 || mask == 3));
}

// Indica si es posible caminar hacia la izquierda
//
bool TSpriteGlaurung::canWalkLeft(int px,int py)
{		
	for (int i=0;i<ii_tilesy;i++)
	{
		if (io_Screen->getMask(px-1,py-i) == 1) return false;
	}
	return true;
}

// Comprueba si podemos dejarnos caer.
//
bool TSpriteGlaurung::canFallDown(int px,int py)
{
    int i;

    if (ib_flip_x)
    {
        // Ajustamos coordenadas, caso que este mirando a la izquierda.
        //
        if (isDown(px,py))
        {
            for(i=0;(i<4) && (isDown(px,py));i++)
            {
                if (canWalkLeft(px,py)) px--;
            }

            // No es posible dejar caer.
            //
            if (isDown(px,py)) return false;
        }
    }
    else
    {
        // Ajustamos coordenadas, caso que este mirando a la derecha.
        //
        if (isDown(px,py))
        {
            for(i=0;(i<4) && (isDown(px,py));i++)
            {
                if (canWalkRight(px,py)) px++;
            }

            // No es posible dejar caer.
            //
            if (isDown(px,py)) return false;
        }
    }

    // Ahora que sabemos que podemos caer, porque no debe
    // haber suelo bajo el sprite, comprobamos si podemos dejarnos caer.
    //
    while (py<=23)
    {
        if (isDown(px,py)) return true;
        py++;
    }
    return false;
}

// Comprueba si puede efectuarse un salto hacia la izquierda
// y si despues de realizar el salto, nos quedamos a cubierto.
//
bool TSpriteGlaurung::canJumpLeft(int px,int py,int vel,int *arco,bool completo) 
{ 
	int cx=0,cy=0,dx;
	
	for (dx=0;dx<240;dx+=vel)
	{
		cx = px - (dx/_TileAX);
		cy = py - (arco[dx]/_TileAY);

		if (cx<1) return false;

		//if (!completo && cy<10) return false;
		
		if (completo || (!completo && dx<=120))
		{
            if (isTop(cx,cy)) return false;

			if (!canWalkLeft(cx,cy))
            {
                // Para que se considere un salto, tiene que ser posible
                // saltar al menos una mínima distancia.
                //
                if (dx<180) return false;
                break;
            }
            
            if (dx>=120)
            {
                if (isDown(cx,cy)) return true;
            }
        }
	}

	// Comprobamos que despues de saltar, quedamos a cubierto o bien 
    // al caer, lo hacemos sobre suelo.
	// Si el salto se hizo completo, debe ser caer sobre suelo..
    //
    if ( (dx==240 || dx==(240-vel)) && (!isDown(cx,cy)) ) return false;

    while (cy<=23)
    {
	    if (isDown(cx,cy)) return true;
        cy++;
    }
		
	// El salto no es posible
	//
	return false;
}

bool TSpriteGlaurung::canJumpSmallLeft(int px,int py,int vel) 
{
	return canJumpLeft(px,py,vel,ia_SaltoCorto,true);
}

bool TSpriteGlaurung::canJumpBigLeft(int px,int py,int vel)
{
	return canJumpLeft(px,py,vel,ia_SaltoLargo,false);
}

// Comprueba si puede efectuarse un salto hacia la derecha
// y si despues de realizar el salto, nos quedamos a cubierto.
//
bool TSpriteGlaurung::canJumpRight(int px,int py,int vel,int *arco,bool completo) 
{ 
	int cx=0,cy=0,dx;
	
	for (dx=0;dx<240;dx+=vel)
	{
		cx = px + (dx / _TileAX);
		cy = py - (arco[dx] / _TileAY);

		if (cx<1) return false;

		//if (!completo && cy<10) return false;

		if (completo || (!completo && dx<=120))
		{
            if (isTop(cx,cy)) return false;

            if (!canWalkRight(cx,cy)) 
            {
                // Para que se considere un salto, tiene que ser posible
                // saltar al menos una mínima distancia.
                //
                if (dx<180) return false;
                break;
            }

            if (dx>=120)
            {
                if (isDown(cx,cy)) return true;
            }
		}
	}

	// Comprobamos que despues de saltar, quedamos a cubierto o bien 
    // al caer, lo hacemos sobre suelo.
	// Si el salto se hizo completo, debe ser caer sobre suelo..
    //
    if ( (dx==240 || dx==(240-vel)) && (!isDown(cx,cy)) ) return false;
    
    while (cy<=23)
    {
	    if (isDown(cx,cy)) return true;
        cy++;
    }
	// El salto no es posible
	//
	return false; 
}

bool TSpriteGlaurung::canJumpSmallRight(int px,int py,int vel) 
{
	return canJumpRight(px,py,vel,ia_SaltoCorto,true);
}

bool TSpriteGlaurung::canJumpBigRight(int px,int py,int vel) 
{
	return canJumpRight(px,py,vel,ia_SaltoLargo,false);
}

// --------------------- Realización de Salto

void TSpriteGlaurung::jumpRight(int vel,bool salto_bajo)
{
	ia_Salto=(salto_bajo)?ia_SaltoCorto:ia_SaltoLargo;
	ii_Salto=0;
	ib_Salto=true;
	ii_SaltoVel=vel;
	ib_flip_x=false;
	ii_SaltoPX = position.x;
	ii_SaltoPY = position.y;
}

void TSpriteGlaurung::jumpLeft(int vel,bool salto_bajo)
{
	ia_Salto=(salto_bajo)?ia_SaltoCorto:ia_SaltoLargo;
	ii_Salto=0;
	ib_Salto=true;
	ii_SaltoVel=-vel;
	ib_flip_x=true;
	ii_SaltoPX = position.x;
	ii_SaltoPY = position.y;
}

void TSpriteGlaurung::jump(void)
{
	int px,py;

    px = getHotspotX();
    py = getHotspotY();

	// Calculamos la nueva posición del personaje.
	//
	if (ii_Salto<240)
	{
        // Caso que comience el descenso y toquemos suelo.
        //
        if (ii_Salto>=120)
        {
            if (isDown(px,py)) 
            {
                ii_Salto=240;
                return;
            }
        }

        if (ib_flip_x)
        {
            if (!canWalkLeft(px,py)) 
            {
                ii_Salto=240;
                return;
            }
        }
        else
        {
            if (!canWalkRight(px,py))
            {
                ii_Salto=240;
                return;
            }
        }
		position.x += ii_SaltoVel;
		position.y = ii_SaltoPY - ia_Salto[ii_Salto];
		ii_Salto+=abs(ii_SaltoVel);
		return;
	}

	// El salto se debe haber acabado, comprobamos otras cosas.
	// Comprobamos si existe algo bajo el personaje
	//    
    if (isDown(px,py) || (py>=23) )
    {
    	position.y = ((py+1)*_TileAY) - position.height;
		ib_Salto=false;
		return;
    }
    
	position.y+=abs(ii_SaltoVel);		
}

/*
void TSpriteGlaurung::setBurladero(TSpriteBurladero s)
{
	ispr_Burladero = s;
}
*/

int *TSpriteGlaurung::getKillSequence(int& length)
{
    length = 8;
	return ia_AnimMuerte;
}

bool TSpriteGlaurung::kill(void)
{
	if (!ib_muriendo)
	{
		ii_Frame=0;
		ib_muriendo = true;
		ii_Retardo=3;
		ia_AnimSequence = getKillSequence(ii_AnimSequenceLength);
		ib_flip_x = false;
		if (isInstanceOf(CLASS_ID(TSpriteBrujoDragon)))
		{		
		  if (miGame->isSoundAvailable()) miGame->isnd_ApareceBrujoDragon.play(false);
        }
		else 
        {
            if (miGame->isSoundAvailable()) miGame->isnd_MuerteEnemigo.play(false);
        }
	}
	else
	{
        if ((ii_Frame+1)==ii_AnimSequenceLength)
		{
			ia_Salto = NULL;
			ii_Retardo=2;
			invalidate();
            return false;
		}		
	}
    return true;
}

// Detecta una colision con otro Sprite
//
bool TSpriteGlaurung::collisionsWith(TSpriteGlaurung& spr)
{
    if (ib_muriendo) return false;
    return position.intersects(spr.position);
}

#endif
