#include "commun.h"

void IWRAM_CODE DecompresseDonnees(int plan,
								   int departX,
								   int departY,
								   int tailleX,
								   int tailleY,
								   int cibleX,
								   int cibleY,
								   void *map,
								   int largeurMap)
{
	int yy, dx, j, val, lastdx;
	int x, y, i;
	unsigned int plusX, plusY;
	unsigned short *ptr, *ptr2;
	unsigned short(*myMap)[largeurMap] = (void *) map;

	if (departX < 0)
	{
		plusX = -departX;
		departX = 0;
		tailleX -= plusX;
	}
	else
		plusX = 0;

	if (departY < 0)
	{
		plusY = -departY;
		departY = 0;
		tailleY -= plusY;
	}
	else
		plusY = 0;

	plusX += cibleX;
	plusY += cibleY;

	/*
	OPTIMISER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

	Le memset prend plus de 80% du temps de décompression requis ;)
	2 fois ~5 % de CPU à gagner.
	*/

	//        memset(Map,2,sizeof(Map));
	val = 0;

	/*
	Voici mon algorithme de décompression révolutionnaire :)

	Je l'ai nommé "CALC"; ce nom tripant signifie "compression à la con".
	Nan, en fait c'est mauvais, ça compresse pas grand chose, mais c'est plutôt rapide
	à la décompression, et c'est mieux que rien, enfin je pense... ;)
	*/
	for (y = 0; y < tailleY; y++)
	{
		yy = y + departY;

		//                for (dx=0;dx<departX;dx++)      {
		// Ici il faut parcourir la map à la recherche d'éventuelles informations compressées...
		//                Exec(VerifBouge,plan,xx,yy);
		ptr = (unsigned short *) (indexTemp[plan][yy]);
		dx = indexVal[plan][yy];

		// Le pointeur de ligne courant est trop en avant ("à droite") par rapport à la gauche de l'écran
		// On va le déplacer vers la gauche jusqu'à ce qu'il soit correct
		while (dx > departX)
		{
			// Balise "répète x fois le bloc y" (3 blocs)
			if (*(ptr - 3) == 0xffff)
			{
				dx -= *(ptr - 1) + 1;
				ptr -= 3;
			}
			// Balise "répète x fois le bloc 0" (2 blocs)
			else if (*(ptr - 2) == 0xfffe)
			{
				dx -= *(ptr - 1) + 1;
				ptr -= 2;
			}
			// Cas normal: saute un bloc
			else
			{
				dx--;
				ptr--;
			}
		}

		// Maintenant on est revenu à un cas "normal": le bloc courant est "à gauche" de la position à laquelle décompresser
		// La décompression ne commence pas encore; on doit juste voir ce qui est avant le bloc tout à gauche de l'écran,
		// parce qu'il se pourrait par exemple qu'il indique une série de 30 blocs contigus, et dans ce cas on va devoir remplir
		// la partie gauche de l'écran avec cette valeur de bloc.
		while (dx < departX)
		{
			// Garde les infos du dernier bloc effectué, voir plus bas
			ptr2 = ptr;
			lastdx = dx;

			// Lit le bloc courant
			i = *ptr++;

			// Répétition du bloc 0 commencée
			if (i == 0xfffe)
			{
				val = 0;
				dx += *ptr++;
			}
			// Répétition d'un bloc arbitraire commencée
			else if (i == 0xffff)
			{
				val = *ptr++;
				dx += *ptr++;
			}
			// Fin de ligne
			else if (i == 0xfffd)
			{
				x = 0;
				// On ne doit jamais passer l'indicateur de fin de ligne vu qu'on ne sait pas où était le dernier bloc donc impossible de revenir en arrière
				indexTemp[plan][yy] = ptr2;
				indexVal[plan][yy] = lastdx;
				goto decompTerminee;
			}

			// Avance d'une unité
			// En fait, la structure du code peut paraître étrange, mais il faut savoir que dans le fichier compressé, les
			// offsets de saut sont toujours soustraits d'une unité. Ainsi, "3 fois le bloc zéro" signifie en fait 4 fois.
			dx++;
		}

		// Sauvegarde l'adresse du dernier bloc décompressé (ainsi que sa position), permet d'accélérer les décompression contiguës
		indexTemp[plan][yy] = ptr;
		indexVal[plan][yy] = dx;

		//Là on va contrôler si on est plus loin que le début de l'écran pour remplir les
		//premiers éléments avec la dernière valeur...
		if (dx > departX)
		{
			// Assurons-nous qu'on n'est pas plus loin que la droite du rectangle demandé quand même
			if (dx - departX > tailleX)
				dx -= (dx - departX) - tailleX;
			// Remplit la map avec le bloc donné
			for (x = 0; x < dx - departX; x++)
				myMap[y + plusY][x + plusX] = val;
		}

		for (x = dx - departX; x < tailleX; x++)
		{
			i = *ptr++;
			if (i == 0xfffe)
			{
				j = x;
				x += *ptr++;
				if (x >= tailleX)
					x = tailleX - 1;
				for (; j <= x; j++)
					myMap[y + plusY][j + plusX] = 0;
			}
			else if (i == 0xffff)
			{
				j = x;
				val = *ptr++;
				x += *ptr++;
				if (x >= tailleX)
					x = tailleX - 1;
				for (; j <= x; j++)
					myMap[y + plusY][j + plusX] = val;
			}
			else if (i == 0xfffd)
				break;
			else if (i == 0xfffc)
				return;
			else
				myMap[y + plusY][x + plusX] = i;
		}

decompTerminee:
		for (; x < tailleX; x++)
			myMap[y + plusY][x + plusX] = 0;
	}
}
