//Un tableau en cache utilisé pour la map. On ne peut pas directement accéder à l'écran car s'il est en
//train d'être redessiné à ce moment, le résultat ne sera pas très beau car une partie de l'affichage
//utilisera les premiers paramètres et une autre les nouveaux. C'est pourquoi on effectue toutes les
//copies durant une période appelée VBLANK, où le contrôleur graphique "remonte" de la dernière ligne de
//l'affichage jusqu'à la première. La VBLANK fait dure environ 68 lignes de LCD. Lorsqu'on effectue des
//des opérations durant les 160 lignes (hauteur du LCD) restantes, on est donc obligé de les mettre en
//cache d'abord, avant de copier le résultat à la fin pendant la VBLANK.
unsigned short IN_EWRAM tableau[22][32];

/**********************************************************************************************************
                                        Moteur de gestion de mosaïques
    Le problème est la limitation de la mémoire vidéo. Normalement on est censé charger toutes les
    mosaïques qu'on serait susceptible d'utiliser d'abord en VRAM (mémoire vidéo) et ensuite les utiliser
    directement.
    Mais faisons le calcul. Notre map (256 couleurs, 8 bits), pour chaque mosaïque (8x8 pixels), utilise
    64 octets. Or, il y a 64 ko de VRAM pour les arrière-plans, donc 1024 mosaïques si la mémoire vidéo
    n'était utilisée que pour cela. Mais il faut encore compter qu'une partie sera utilisée pour stocker
    les maps.
    Et notre map, aussi petite qu'elle soit, utilise déjà 823 tiles (mosaïques). En
    imaginant une application utilisant une carte un peu plus compliquée, on voit que la limite est vite
    atteinte.

    Ce moteur de tiles évolué permet d'afficher sur une même carte un nombre "indéfini" de tiles dans une
    map, et sur un nombre "indéfini" de plans différents.
    Forcément, la première idée qui vient à l'esprit serait de charger dynamiquement les tiles dont on a
    besoin. Cela impliquerait qu'on associe à chaque tile sur l'écran un emplacement différent en VRAM.
    Il s'agit bien de la méthode utilisée par les programmeurs commerciaux mais cela consomme énormément
    de mémoire, et si on veut couvrir un seul plan entier en mode 256 couleurs (comme dans le cas du
    mien), il faudrait 31x21 tiles = 651 tiles, soit 65% de la mémoire.
    Ma méthode consiste à associer les tiles virtuelles (pas en VRAM et pas limitées en nombre) aux tiles
    réelles (qui se trouvent en VRAM). Chaque tile est analysée, puis on regarde si elle se trouve déjà
    en VRAM. Si ce n'est pas le cas on la charge, sinon on l'utilise directement. Ainsi les tiles
    identiques peuvent tout de même être réutilisées, économisant de la mémoire vidéo. Les tiles sont
    entassées à la demande jusqu'à saturation de la VRAM (nombre de tiles en VRAM > MAX_TILES décidé).
    A ce moment-là, il faut rechercher les tiles qui ont été chargées mais qui ont disparues entre-temps
    pour pouvoir les réutiliser. C'est assez simple, un parcours de la map et une re-validation des tiles
    qui s'y trouvent encore permet de libérer toutes celles qui n'ont pas été signalées "présentes".

    Ma méthode utilise moins de mémoire, et dépend de MAX_TILES qui est arbitraire (enfin réglé de manière
    à être sûr qu'il n'y aura jamais plus de tiles que ça sur le même écran). Avec 512 tiles, cela
    représente un peu plus de 50%, mais on pourrait le réduire à 256 par exemple, 512 c'était juste pour
    prévoir large. De plus, elle fonctionne sur un nombre indéfini de plans. En contrepartie, elle
    consomme pas mal de CPU en plus.
**********************************************************************************************************/

#define MAX_TILES 512                       //Nombre de tiles 8x8 maximal affichable en même temps
#define MAX_PHYSICAL_TILES 4096             //Nombre maximum possible de tiles dans la map (il faut bien fixer une valeur)
unsigned short IN_EWRAM utilTiles[MAX_PHYSICAL_TILES];
unsigned short IN_EWRAM numTiles[2000];      //C'est trop. MAX_TILES+16*11 suffirait largement, je suppose.
unsigned short IN_EWRAM copieTiles[MAX_PHYSICAL_TILES];
unsigned short n_tiles;                     //Nombre de tiles utilisées
#define TAILLEX_ECRAN 31                    //Nombre de mosaïques à remplir sur l'écran
#define TAILLEY_ECRAN 21                    //Horizontalement et verticalement
int gblDepX, gblDepY;

//Un écran, ne nécessitant pas de contenir de buffer, mais juste ses coordonnées (défilement en x,y)
typedef struct      {
    int x,y;
} ECRAN;

//Initialise les variables globales pour le système de gestion des mosaïques en VRAM.
void InitTileSystem()       {
    memset(utilTiles,0,sizeof(utilTiles));
    memset(numTiles,0,sizeof(numTiles));
    memset(copieTiles,0,sizeof(copieTiles));
    n_tiles=1;
}

//Initialise les coordonnées d'une structure ECRAN.
void InitEcran(ECRAN *ecr)      {
    ecr->x=ecr->y=0;
}

int depassement=0;                       //Uniquement pour la détection d'erreurs. Il faut que l'on sache que si le jeu plante, il faudra prévoir plus de tiles à disposition ou (en l'occurence) réduire le nombre de tiles utilisées par un monde. En imaginant que chaque tile est différente, cela nécessiterait 176 tiles, mais avec 128 tiles, je remplis déjà la VRAM à moitié, reste le fond, le texte, ... faut pas exagérer!

//Copie la tile virtuelle sur la tile physique
void CopieTile(unsigned short tileVirtuelle, unsigned short tile)       {
    if (tile<MAX_TILES && tile>0)                       //Tests facultatifs, mais là pour éviter qu'en cas de débordement de tile le désastre soit trop grand...
        CopieDMA((void*)plan_tiles+tileVirtuelle*64, (void*)CharBaseBlock(0)+64*tile, 32, DMA_16im);
}

//Optimise les tiles (enlèves celles qui ne sont plus utilisées)
void OptTiles()     {
    unsigned int i,j;
    unsigned short val;
        /****************************************************************************
            Vidage et optimisation de la VRAM:
            Cette partie va déterminer quelles tiles doivent êtres retirées de la
            mémoire vidéo. L'algo est le suivant:
            -Recherche des tiles communes entre les tiles déjà existantes et celles
             que l'on veut pour afficher l'image courante.
            -Les tiles qui n'existent pas déjà se voient allouer un bloc en mémoire
             vidéo.
            -Les tiles pour lequel on a alloué un bloc mais qui ne l'utilisent plus
             (notamment parce qu'elles ne sont plus visible à l'écran) sont libérées
             et pourront ensuite être remplacées par les nouvelles tiles.
        ****************************************************************************/

        if (depassement)                //Déjà eu lieu
            return;

        n_tiles=1;                      //Il n'y a plus qu'une seule tile à présent
        depassement=1;

        //"Dévalide" les tiles utilisées la dernière fois. On va les "revalider" ensuite. Si certaines d'entre elles n'est pas validée, alors elles seront jartées de la mémoire vidéo.
        for (i=1;i<MAX_TILES;i++)     {
            copieTiles[numTiles[i]]=0;
        }

        //Etape de validation
        for (j=0;j<TAILLEY_ECRAN;j++)     {
            for (i=0;i<TAILLEX_ECRAN;i++)     {
                val=plan_map[j+gblDepY][i+gblDepX]&2047;        //&2047 car les bits qui suivent sont des flags qui n'ont rien à faire là-dedans
                copieTiles[val]=1;              //La tile existe?
            }
        }

        //Ici, on va vérifier si chacune des tiles énumérées plus haut existe bien sur l'écran courant. Si ce n'est pas le cas, elle sera sortie de la VRAM.
        for (i=1;i<MAX_TILES;i++)     {
            if (copieTiles[numTiles[i]]==0)     {
                utilTiles[numTiles[i]]=0;                   //Relation entre tile virtuelle et réelle -> fichue
                numTiles[i]=0;                              //Libérée
            }
        }
}

//Recherche une tile réelle disponible et y copie la tile virtuelle "val"
int IN_IWRAM ChercheTile(int val)      {
    while(numTiles[n_tiles]!=0)
        n_tiles++;

    //Pas que pour le débogage!
    if (n_tiles>=MAX_TILES)        {
        //Pour le débogage
        if (depassement)       {                            //Pour le débogage seulement: détection de boucle infinie (un dépassement a déjà eu lieu)
//                Alerte("Max de tiles");
            return 0;
        }
        return 1;
    }

    //Stocke les références entre les deux
    utilTiles[val]=n_tiles;
    numTiles[n_tiles]=val;

    //Copie la mosaïque
    Exec(CopieTile,val,n_tiles);
    n_tiles++;
    return 0;
}

//Copie un bloc de 16x16 pixels sur une map de 8x8
void IN_IWRAM CopieBloc_8(int x, int y, unsigned short bloc)      {
    register unsigned short val=bloc;                   //Pitêtre optimiser ça...

    if (bloc==0)                        //Optimisation pour bloc nul (attention, il doit pour cela être toujours vide, vérifiez le tileset généré!)
        goto ici;

rechercheTile:
    if (n_tiles>=MAX_TILES)         {       //Trop de tiles -> recours à l'optimisation
        Exec(OptTiles);
        val=bloc;
    }

    if (utilTiles[val]==0)     {            //Existe pas encore?
        if (ChercheTile(val))               //On la met et on revérifie si une optimisation est nécessaire
            goto rechercheTile;
    }

    bloc=utilTiles[val];                    //C'est bon, on a le numéro de notre tile physique correspondante.

ici:
    //Stockage dans le tableau qui sera copié à la VBLANK.
    tableau[y][x]=bloc;
}

//Copie un morceau de map en fonction des paramètres. Reste à déplacer ça dans la mémoire vidéo à la fin
void IN_IWRAM CopieMap(int departX, int departY,/* int tailleX, int tailleY,*/ int tailleMapX, int tailleMapY)        {
    unsigned int x,y;
    unsigned int i;
    unsigned short *ptr;
    const int tailleX=31, tailleY=21;                       //Taille de l'écran + 1 pour le défilement éventuel

    gblDepX=departX, gblDepY=departY;
	for (y=0;y<tailleY;y++)        {
    	ptr=(unsigned short *)plan_map[y+departY]+departX;
        for (x=0;x<tailleX;x++)     {
            i=*ptr++;
            CopieBloc_8(x,y,i&2047);
        }
    }
}

//Efface un screenblock entier (2048 octets). Les screenblocks contiennent les données des maps.
void EffaceScreenBlock(u32 screenBlock)     {
    int zero=0;
    MemSet(&zero,ScreenBaseBlock(screenBlock),(0x0800)>>2,DMA_32im);
}






