#include "commun.h"

extern u8 moteur2d_gblCroche;
extern u32 moteur2d_masqueGbl;
extern s32 moteur2d_planReel;
extern u32 moteur2d_xSav;
extern unsigned short moteur2d_angleSav;

extern void ColleSonicAuPlafond(SONIC *s, unsigned int *pa, int angle);

static const u32 TypesPF[][2] =
{

        //0x1d0-0x1df (Unused)
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },
        { 0x0, 0x0 },

        //0x1e0-0x1e4 (Green Hill Zone)
        { 0x3f, 0x1 },
        { 0x40, 0x5b },
        { 0x41, 0x5c },
        { 0x58, 0x37 },
        { 0x57, 0x36 },

        //0x1e5-0x1e8 (Aquatic Ruin Zone)
        { 0x103a, 0x1 },
        { 0x103b, 0x1 },
        { 0x103c, 0x1 },
        { 0x103d, 0x1 },

        //0x1e9-0x1ea (Aquatic Ruin Zone)
        { 0x42e, 0x213 },
        { 0x42e, 0x201 },

        //0x1eb (Aquatic Ruin Zone)
        { 0x801, 0x201 },
};

u32 IWRAM_CODE PointDeCollision(unsigned int x, unsigned int y)
{
	unsigned int e;
//	register unsigned int masque = moteur2d_masqueGbl;
	e = Element(x, y);
	if (e & MASQUE_PASSE_BAS && !moteur2d_gblCroche)
		return 0;
	e &= ~MASQUE_SONIC;

	//Plate-forme normale?
	if (e == PASSE_BAS - 1)
		return moteur2d_gblCroche;
	if (e && e < PASSE_BAS)
		return e;

	//Inclinée?
	if (e >= TYPE_BAS && e < TYPE_SPEC)
	{
		if (e >= TYPE_HAUT && e < TYPE_SPEC)
		{
			if ((y & 15) < inclineH[e - TYPE_HAUT][x & 15])
				return e;
		}
		else
		{
			if ((y & 15) >= incline[e - TYPE_BAS][x & 15])
				return e;
		}
	}

	//Rien du tout
	return 0;
}

u32 IWRAM_CODE Element(u32 x, u32 y)
{
        u32 i;

        //RAJOUTE %72!
        //    Message(10,"%i %i",(y>>4)%96,(x>>4)%96);
        i = decompMap[(y >> 4) & 127][(x >> 4) & 127] & moteur2d_masqueGbl;
        if (i >= TYPE_SPEC && i < TYPE_SPEC + sizeof(TypesPF) / sizeof(TypesPF[0]))
                return TypesPF[i - TYPE_SPEC][moteur2d_planReel] & moteur2d_masqueGbl;
        else
                return i;
}

u32 IWRAM_CODE Map(u32 x, u32 y)
{
        u32 i;

        //RAJOUTE %72!
        //    Message(10,"%i %i",(y>>4)%96,(x>>4)%96);
        i = decompMap[y & 127][x & 127] & moteur2d_masqueGbl;
        if (i >= TYPE_SPEC && i < TYPE_SPEC + sizeof(TypesPF) / sizeof(TypesPF[0]))
                return TypesPF[i - TYPE_SPEC][moteur2d_planReel] & moteur2d_masqueGbl;
        else
                return i;
}


u32 IWRAM_CODE MoteurCollisions(SONIC *s, int vx, int vy)
{
	unsigned int L = s->largeur;
	unsigned int H = s->hauteur;
	unsigned int i = 0, j, e, ee;
	unsigned int pa = PA_BAS;
	//En fait cela ne sert absolument à rien (vérifier pour cBas mais j'en doute fort
	//Donc virez-les en cas d'optimisation...
	unsigned char cBas = 0, cDroite = 0, cGauche = 0;
//	unsigned short LM = (L & 15) ? (L - (L & 15) + P) : L, HM = (H & 15) ? (H - (H & 15) + P) : H;        //Largeur et hauteur min. un pattern pour au moins une itération
	u32 hauteurMax = (H + 15) >> 4;
	unsigned int croche;
	unsigned char dejaCroche = 0;
	unsigned int resultat = 0;
	int ancAngle = s->angle;
	u32 ancCroche = s->croche;
	unsigned short PRECISION;

	if (s->croche)
		PRECISION = 9;
	else
		PRECISION = 1;

	pa = calcPA(s);

	/*    //La hauteur du sprite dépend de son angle
	if (pa==PA_BAS || pa==PA_HAUT)
	L=24, H=32;
	else
	L=32, H=24;*/
	if (pa == PA_BAS)
		s->x += vx;
	else if (pa == PA_DROITE)
		s->y -= vx;
	else if (pa == PA_HAUT)
		s->x -= vx;
	else if (pa == PA_GAUCHE)
		s->y += vx;

	//Vérification pour éviter que (j<<4)+s->x ne soit plus bas que le fond du sprite.
	if (((hauteurMax - 1) << 4) + PRECISION >= H - (PRECISION + 1))
		hauteurMax--;

	for (j = 0; j <= hauteurMax; j++)
	{
		if (j == 0)
			i = PRECISION;
		//ATTENTION!!!!! Augmenté d'un pour la compatibilité en mode très rapide!
		else if (j == hauteurMax)
			i = H - (PRECISION + 1);
		else
			i = (j << 4) + PRECISION;

detec03:

		//Détection à gauche du personnage
		e = Element(s->x, s->y + i);
		if (e && e < PASSE_BAS - 2)
		{
			s->x++;
			s->colGauche = 1;
			cGauche = 1;
			if (pa == PA_BAS)
				s->vx = 0;
			else if (pa == PA_GAUCHE)
				s->angle = 270;
			goto detec03;
		}

detec04:

		//Détection à droite du personnage
		e = Element(s->x + L, s->y + i);
		if (e && e < PASSE_BAS - 2)
		{
			s->x--;
			cDroite = 1;
			s->colDroite = 1;
			if (pa == PA_BAS)
				s->vx = 0;
			else if (pa == PA_DROITE)
				s->angle = 90;
			goto detec04;
		}
	}

	if (pa == PA_BAS)
		s->y += vy;
	else if (pa == PA_DROITE)
		s->x += vy;
	else if (pa == PA_HAUT)
		s->y -= vy;
	else if (pa == PA_GAUCHE)
		s->x -= vy;

	for (j = 0; j < 3; j++)
	{
		// i: offset horizontal (on regarde en haut ou en bas)
		// WARNING!!! Largeur maximum: 32 pixels
		if (j == 0)
			i = 2;
		else if (j == 1)
			i = L - 2;
		else if (j == 2)
			i = L >> 1;			// L / 2

		//        for (i=2;i<L-2;i++)            {                    //Stable mais très lent
		//WORKAROUND QUI DEMANDE DU TEMPS MAIS QUI MARCHE A TOUS LES COUPS POUR LES PB D'ANGLES DES PF INCLINEES
		//C'EST CA QUI MERDE AVEC LE PASSAGE:
		//             |
		// -------------
		//             |
		//            /
		//------------

detec01:
		ee = Element(s->x + i, s->y + H);
		e = ee & (~MASQUE_SONIC);

		//Détection normale
		if (e && e < TYPE_BAS)
		{
			if (ee & MASQUE_PASSE_BAS)
			{
				//Ajout || pa!=PA_BAS pour les plate-formes du style:
				//----------- (passe-bas)
				//     |
				// S  /   (S=sonic)
				//----

				//ajouté vy+1 (au lieu de vy) pour des problèmes (très rares) de passage à travers
				if (s->vy < 0 || ((s->y + H) & 15) > vy || pa != PA_BAS)
				{
					goto detec02;
				}
				else
					dejaCroche = 1;                //Je ne gère vraiment pas ça bien. Il faudra forcer croche à se mettre à un dans ce cas.
			}

			if (e == PASSE_BAS - 1 && !s->croche && !cBas)
			{
				e = Element(s->x + i, s->y + H - vy) & (~MASQUE_SONIC);
				if (e >= TYPE_BAS && e < TYPE_HAUT)
				{
					if (((s->y + H - vy) & 15) <= incline[e - TYPE_BAS][(s->x + i) & 15])
					{
						s->y -= vy;
						goto incline;
					}
					else
						goto detec02;
				}
				else
					goto detec02;
			}

			//            ErreurFatale("Droit","%i %i",s->x+i,s->y+H);
			if (pa == PA_BAS)
			{
				s->y--;
				if (e != 2)
					s->angle = 0;
				s->pas_angle = (ee & MASQUE_PAS_ANGLE);
			}
			else if (pa == PA_DROITE)
				s->x--;
			else if (pa == PA_HAUT)
				s->y--;
			else if (pa == PA_GAUCHE)
				s->x++;
			cBas = 1;
			goto detec01;
		}

		if (e >= TYPE_BAS && e < TYPE_HAUT)
		{

			//HORRIBLE WORKAROUND!

			/*            if (((s->y+H+1)&15)>=incline[e-TYPE_BAS][(s->x+i)&15])       {
			s->angle=anglesPF[e-TYPE_BAS];
			}*/
			if (((s->y + H) & 15) >= incline[e - TYPE_BAS][(s->x + i) & 15])
			{
incline:
				if (ee & MASQUE_PASSE_BAS)
				{

					//Supprimer la deuxième condition si des passages à travers se reproduisent.
					//Remplacé > par >=
					if (s->vy < 0 || ((s->y + H) & 15) >= incline[e - TYPE_BAS][(s->x + i) & 15] + vy)
					{

						//                      Debug("%i %i>=%i+%i",s->vy,(s->y+H)&15,incline[e-TYPE_BAS][(s->x+i)&15],vy);
						goto pasbon_enhaut;
					}
					else
						dejaCroche = 1; //Je ne gère vraiment pas ça bien. Il faudra forcer croche à se mettre à un dans ce cas.*/
				}

				//                ErreurFatale("Incline","%i %i",s->x+i,s->y+H);
				if (pa == PA_BAS)
					s->y--;
				else if (pa == PA_DROITE)
					s->x--;
				else if (pa == PA_HAUT)
					s->y++;
				else if (pa == PA_GAUCHE)
					s->x++;
				s->pas_angle = (ee & MASQUE_PAS_ANGLE);
				s->angle = anglesPF[e - TYPE_BAS] & 0x1ff;
				if (anglesPF[e - TYPE_BAS] & 0x1000)
				{
					s->angle = anglesPFcomplexes[anglesPF[e - TYPE_BAS] & 0xfff][(s->x + i) & 15];
				}

				//Peut-être inutile (hotspot)?
				else if (incline[e - TYPE_BAS][(s->x + i) & 15] == 0)
				{
					if (moteur2d_angleSav == 0)        //Ajout
						s->angle = 0;
					else if (s->angle > 45 && s->angle < 315 && pa == PA_BAS && anglesPF[e - TYPE_BAS] & 0x200)
						s->angle = 0;
				}

				cBas = 1;
				goto detec01;
			}
		}

		//Add-on pour les tuyaux!
		if (e >= TYPE_HAUT && e < TYPE_SPEC)
		{
			if (((s->y + H) & 15) < inclineH[e - TYPE_HAUT][(s->x + i) & 15])
			{
				if (pa == PA_BAS)
					s->y++;
				else if (pa == PA_DROITE)
					s->x--;
				else if (pa == PA_HAUT)
					s->y++;
				else if (pa == PA_GAUCHE)
					s->x++;
				cBas = 1;
				s->pas_angle = (ee & MASQUE_PAS_ANGLE);
				s->angle = anglesPFH[e - TYPE_HAUT];
				goto detec01;
			}
		}

		/*
		DÉTECTION SUR LE HAUT DU PERSO
		*/
detec02:
		e = Element(s->x + i, s->y);

		//Détection normale
		if (e && e < PASSE_BAS - 2)
		{
			if (pa == PA_BAS)
			{
				s->y++;
				if (e != 2)
					s->colHaut = 1;
			}
			else if (pa == PA_DROITE)        //Peut-être s->y++...
				s->x--;
			else if (pa == PA_HAUT)
			{
				s->y++;
				s->angle = 180;
			}
			else if (pa == PA_GAUCHE)
				s->x++;
			if (pa != PA_BAS)
				cBas = 1;
			goto detec02;
		}

		//Add-on pour les tuyaux!
		if (e >= TYPE_BAS && e < TYPE_HAUT)
		{
			if (pa == PA_GAUCHE || pa == PA_DROITE)
			{
				if ((s->y & 15) >= incline[e - TYPE_BAS][(s->x + i) & 15])
				{
					cBas = 1;
					if (pa == PA_BAS)
						s->y--;
					else if (pa == PA_DROITE)
						s->x--;
					else if (pa == PA_HAUT)
						s->y++;
					else if (pa == PA_GAUCHE)
						s->x++;
					s->angle = anglesPF[e - TYPE_BAS];

					//On pourrait optimiser avec des tables
					if (s->angle > 45 && s->angle < 315 && incline[e - TYPE_BAS][(s->x + i) & 15] == 0)
						s->angle = 0;
					goto detec02;
				}
			}
		}

		if (e >= TYPE_HAUT && e < TYPE_SPEC)
		{
			if ((s->y & 15) < inclineH[e - TYPE_HAUT][(s->x + i) & 15])
			{
				if (!s->croche)
				{
					// Est-ce qu'on peut coller sonic au plafond? (il faut une certaine vitesse)
					if (s->vy < -vMUL(4))
						ColleSonicAuPlafond(s, &pa, anglesPFH[e - TYPE_HAUT]);
					else	// Sinon on le stoppe tout simplement
						s->vx = 0;
				}

				if (pa == PA_BAS)
				{
					s->y++;

					//ATTENTION!!!! COMMENTÉ POUR EVITER DE BLOQUER DANS LE COIN DES BOUCLES!
					//                    s->angle=0;
					if (!s->croche)
						s->colHaut = 1;
				}
				else if (pa == PA_DROITE)
				{
					s->x--;
					cBas = 1;
					s->angle = anglesPFH[e - TYPE_HAUT];
				}
				else if (pa == PA_HAUT)
				{
					s->y++;
					cBas = 1;
					s->angle = anglesPFH[e - TYPE_HAUT];
				}
				else if (pa == PA_GAUCHE)
				{
					s->x++;
					cBas = 1;
					s->angle = anglesPFH[e - TYPE_HAUT];
				}

				goto detec02;
			}
		}

pasbon_enhaut:
		;
	}

	/*    if (s->saut==1)
	cBas=0;*/
	croche = 0;

	/*
		---=== OPTIMISATION ===---

		Virer le test i=Element(...); et ne l'effectuer qu'une seule fois avant
		de lancer le moteur de collisions. Cela implique que le moteur ne dépassera
		pas 16 px/frame...
	*/

	//    i=s->croche?14:1;
	//Rajouté suite à un problème au niveau des bosses dans Aquatic Ruin (quand on attérit, s->croche n'est pas mis à un)
	i = (cBas | s->croche) ? 14 : 1;

	if (dejaCroche)
	{        //Petit workaround. En fait faudra pas trop abuser des plate-formes passe-bas et ça ira... ^^
		//Au vu de la simplicité de gestion de ce type de plate-formes, je pense qu'il s->y a plus simple comme méthode que celle qui se trouve ci-dessous
		croche = 1;
		s->vy = GRAVITE_DEPART;
	}

#pragma attention supprime_pour_detection_de_changement_de_plan_partout

	//    else    {
	//Over-workaround pour la détection spéciale des plate-formes
	moteur2d_gblCroche = s->croche | cBas;
	s->colSol = 0;
	if (pa == PA_BAS)
	{
		if (PointDeCollision(s->x + 2, s->y + H + i))
		{
			croche = 1;
			if (!PointDeCollision(s->x + L - 14, s->y + H + i))
				s->colSol = 1;
		}
		else if (PointDeCollision(s->x + L - 2, s->y + H + i))
		{
			croche = 1;
			if (!PointDeCollision(s->x + 14, s->y + H + i))
				s->colSol = -1;
		}

		i = Element(s->x + 16, s->y + H - 19);
	}
	else if (pa == PA_DROITE)
	{
		if (PointDeCollision(s->x + L + i, s->y + 2))
			croche = 1;
		if (PointDeCollision(s->x + L + i, s->y + H - 2))
			croche = 1;
		i = Element(s->x + L - 19, s->y + 16);
	}
	else if (pa == PA_HAUT)
	{
		if (PointDeCollision(s->x + 2, s->y - i))
			croche = 1;
		if (PointDeCollision(s->x + L - 2, s->y - i))
			croche = 1;
		i = Element(s->x + 16, s->y + 19);
	}
	else if (pa == PA_GAUCHE)
	{
		if (PointDeCollision(s->x - i, s->y + 2))
			croche = 1;
		if (PointDeCollision(s->x - i, s->y + H - 2))
			croche = 1;
		i = Element(s->x + 19, s->y + 16);
	}

	// Effets spéciaux (la liberté est laissée au développeur du niveau en cours)
	if (i >= 0x1F0 && i <= 0x1FF)
	{
		if (gblLI->ei.fctTypesSpeciaux)
			gblLI->ei.fctTypesSpeciaux(s, i);
	}

	// En saut: aucune collision possible
	if (s->saut)
		s->croche = croche = cBas = 0;

	// Sonic vient de tomber sur le sol
	if (!s->colBas && cBas && s->vy > 0)
	{
		// Il faut transformer sa vitesse verticale (énergie potentielle)
		s->colBas = 1;
		if (s->decole >= MIN_DECOLE)
			Sonic_metEnBoule(s, 0);
		s->decole = 0;
		s->vx += vDIV(-s->vy * sinus[s->angle]);
		// ATTENTION: Enlevé; il se peut que je sois trop bourré, faire plus de tests (en particulier les bosses)
//		s->vangle = 0;
	}

	/*	Ceci permet de faire décoller Sonic dans le cas où une trop grande différence
		d'angle survient. Par exemple, il est au plat puis prend une descente:
		___________
		           \
		            \________
		Supprimé car à cause du croche à 1, la gravité augmentée donne un mauvais effet à Sonic
	*/
/*	if (differenceAngle(s->angle, ancAngle) > 45 && ancCroche && croche)
	{
		s->angle = ancAngle;
		croche = 0;
	}*/

	if (!croche)
	{
		if (s->angle)
		{
			//WORKAROUND pour éviter les décalages au décollage (à droite ou à gauche)
#pragma workaround_xsav
			if (pa == PA_DROITE || pa == PA_GAUCHE)
			{

				//               if (vx>=4)
				//                  s->x-=vx-4;
				s->x = moteur2d_xSav;
			}

			//Workaround! (saut=1) pour éviter qu'on colle à une plate-forme alors qu'on vient d'en décoller
			s->saut = 1;
			s->vy = -vDIV(s->vx * sinus[s->angle]);
			s->vx = vDIV(s->vx * cosinus[s->angle]);

			//RECENT: Remplacé sans raison valable
			//            s->croche=0;
			croche = 0;
			resultat = 1;
			s->decole = 1;
		}
		else if (s->croche)

			//WORKAROUND. La gravité doit être 256 sur les plate-formes et nulle lorsque Sonic les quitte.
			s->vy = 1;

		//        if (pa==PA_HAUT)
		//            s->vx=-s->vx;
		s->angle = 0;
		s->colBas = 0;
	}

	//Else?
	s->croche = croche;
	s->col2D = croche;

	//En pleine montée, il ne devrait pas pouvoir se crocher...
	//Devrait boguer lorsque sonic devra "coller" au plafond lors d'un saut (spring yard).
	if (s->saut > 0 && s->saut <= 2)
		s->croche = 0;
	return resultat;
}

