#include "commun.h"

const unsigned char couleurs_texte[]=       {
    0,246,250,51,252,251,255,214,253,203,254,0,0,0,0,1
};

#define MAX_FONTES 10
typedef struct      {
    const u32 *fonte, *masque;
    const u8 *tailles;
    u16 hauteur, largeur;
} FONTE;
u32 nFontes=0;
FONTE fontes[MAX_FONTES];

//Attention: Ne chargez que des fontes 8 bits avec cette fonction!
void ChargeFonte(const char *nom)         {
    void *donnees;

    donnees=SymFind(nom,NULL);
    
    if (!donnees)
        return;
    
    largeur_texte=*(short*)donnees;
    donnees+=sizeof(short);
    hauteur_texte=*(short*)donnees;
    donnees+=sizeof(short);

    tailles=donnees;
    donnees+=224;
    fonte=donnees;
    masque=(donnees+largeur_texte*4*hauteur_texte*224);

    if (nFontes>=MAX_FONTES)                            //Trop de fontes déjà chargées
        return;
    LitFonteSt(fontes[nFontes]);
    nFontes++;
}

#define MAX_STR     100
#define MAX_ARGS    10
unsigned char *AnalyseCommande(unsigned char *ptr, unsigned char *chaine, int *args, signed char *flags, u32 x)         {
    int aa,z,zz;
    int fmt;

	zz=0;
	for (z=0;z<MAX_ARGS;z++)
	   args[z]=0, flags[z]=1;

	do	{
		memset(chaine,0,MAX_STR);
		aa=0;
		if (*ptr=='-')	{						//L'utilisateur va rentrer un argument relatif à la position du curseur
			fmt=-1;
			ptr++;
		}
		else if (*ptr=='+')	  {
			fmt=1;
			ptr++;
		}
		else if (zz==1 || zz==3)                //Arguments y toujours relatifs!
            fmt=1;
        else
            fmt=0;

        if (*ptr>='0' && *ptr<='9')     {
    		z=0;									//Longueur de la chaîne de l'argument
    		while (*ptr>=48 && *ptr<=57 && z<MAX_STR)     		//Tant que c'est bien des nombres
    			chaine[z++]=*ptr++;
   			chaine[z]=0;
			args[zz]=atoi(chaine);			        //Chaîne -> entier pour l'argument en cours...
			flags[zz++]=fmt;
        }
        else if (*ptr==',')         {
			args[zz]=0;
            flags[zz++]=1;
        }
        else
            break;
        if (*ptr==',')
            ptr++;
        else
            break;
	} while (zz<MAX_ARGS);		    //Tant qu'une virgule est disponible (séparatrice d'arguments)

	//Le prochain argument est une chaîne
    memset(chaine,0,MAX_STR);
    
	z=0;

	while (*ptr!='#' && *ptr!='|' && z<MAX_STR)		{	//Maximum 300 caractères
		chaine[z++]=*ptr++;								//On remplit la chaîne
	}
	
	chaine[z]=0;

	//Les quatre premiers arguments sont gérés de manière à pouvoir être relatifs ou absolus avec comme maximum et minimum 0 et 240/128 et comme défaut a/b.
    if (flags[0])               args[0]=x+(flags[0]>0?args[0]:-args[0]);
    if (flags[1]==-1)           args[1]=-args[1];
    if (flags[2])               args[2]=x+(flags[2]>0?args[2]:-args[2]);
    if (flags[3]==-1)           args[3]=-args[3];
    
	return ptr;
}

//Analyse un code de format (précédé par un ©) et le stocke dans la structure de format fm.
unsigned char *GetFormat(FORMAT_TEXTE *fm, unsigned char *ptr)      {
    unsigned char fmt, val;
    fmt=*ptr++;
    val=(*ptr++)-'0';
    if (fmt=='F' && val<nFontes)         {
        ChangeFonteSt(fontes[val]);
        fm->fonte=val;
    }
    else if (fmt=='G' && val<=3)         {
        fm->gras=val;
    }
    else if (fmt=='C')      {
        if (*(ptr-1)=='+')       {
            val=(*ptr++)-'0';
            if (val>=49)
                val-=39;
            fm->couleur=(val&15)<<4;
            val=(*ptr++)-'0';
            if (val>=49)
                val-=39;
            fm->couleur|=(val&15);
        }
        else    {
            if (val>=49)
                val-=39;
            fm->couleur=couleurs_texte[val&15];
        }
    }
    else if (fmt=='I' && val<=1)
        fm->italique=val;
    else if (fmt=='S' && val<=4)
        fm->souligne=val;
    return ptr;
}

//Hauteur totale du texte
int hauteur_totale=0;
//Les objets du texte (images)
OBJET_TEXTE *objetsTxt;
//Nombre d'objets
int nbObjetsTxt;

//Ajoute une image (objet) au texte
void AjouteImage(int x, int y, const char *nom_fichier)           {
    unsigned char *bitmap;

    bitmap=SymFind(nom_fichier,NULL);
    
    objetsTxt[nbObjetsTxt].type=1;
    objetsTxt[nbObjetsTxt].donnees=bitmap+4;
    objetsTxt[nbObjetsTxt].x=x;
    objetsTxt[nbObjetsTxt].y=y;
    objetsTxt[nbObjetsTxt].l=*(unsigned short*)bitmap;
    objetsTxt[nbObjetsTxt].h=*(unsigned short*)(bitmap+2);

    //Vérifie que la largeur est multiple de 4, et que la taille est valable
    if ((objetsTxt[nbObjetsTxt].l&3)==0 && objetsTxt[nbObjetsTxt].l>0 && objetsTxt[nbObjetsTxt].l<=240 && nbObjetsTxt<MAX_OBJETS-1)
        nbObjetsTxt++;
}

//Macros pour simplifier
#define HAUTEUR_TEXTE (hauteur_texte+offset_y+((fm.souligne+1)&~1))
#define STOCKE_INFOS  (x_dernierespace=larg_max, ptr_dernierespace=ptr)

/*int my_atoi(char *chn)	{
	int i;
	int n;
	int v;
	char *deb;
	deb=chn;
	while(*chn>='0' && *chn<='9')
		chn++;
	chn--;
	i=1;
	n=0;
	while(chn>=deb)	{
		v=*(chn)-48;
		n+=i*v;
		i*=10;
		chn--;
	}
	return n;
}*/

void DessineClipBitmap(OBJET_TEXTE *im, s32 scrlY, s32 clip_y0, s32 clip_y1)          {
    s32 y=im->y-scrlY;
    u32 j;
    u8 *src=(u8*)im->donnees;
    int h=im->h;

    if (y<clip_y0)          {
        src+=(clip_y0-y)*im->l;
        h-=(clip_y0-y);
        y=clip_y0;
    }
    if (y+h>clip_y1)       {
        h-=(y+h)-clip_y1;
    }
    if (h<=0)
        return;

   for (j=0;j<h;j++)         {
       Mode4_EcritPlusDe4Pixels(im->x,y+j,im->l>>2,(u32*)src);
       src+=im->l;
   }
}

//Analyse le texte et retourne un pointeur sur les lignes de celui-ci.
LIGNE_TEXTE *AnalyseTexte(unsigned char *donnees_texte)     {
    unsigned char *ptr=donnees_texte;       //Pointeur sur le texte
    LIGNE_TEXTE *lignes;                    //Les infos sur toutes les lignes
    LIGNE_TEXTE *lt;                        //Pointeur sur la structure pour plus de simplicité
    int ligne_courante;                     //Ligne courante
    u32 x,y;                                //Positions (absolues)
    unsigned char c;                        //Caractère à analyser
    const int marge_x=0;                    //Marge sélectionnée (en x)
    u32 hauteur=0;                          //Hauteur de la ligne courante
    u32 x_dernierespace;
    unsigned char *ptr_dernierespace;
    FORMAT_TEXTE fm;
    int i, haut_sup, offset_y=0;
    u32 larg_max;
    char alignement;

    lignes=malloc(sizeof(LIGNE_TEXTE)*MAX_LIGNES);          //Allocation mémoire
    
    ligne_courante=0;                       //Première ligne
    x=y=larg_max=0;
    haut_sup=0;
    fm.fonte=0, fm.gras=0, fm.couleur=couleurs_texte[1], fm.italique=0, fm.souligne=0;
    alignement=ALIGN_GAUCHE;

    //Ok c'est pas beau mais il faut quand-même économiser les ressources. Un appel de
    //fonction avec 68000 paramètres serait d'autant plus moche que ça.
    //De plus, je préfère travailler avec les variables locales, ça doit être une habitude
    //provenant de la TI, car GCC ajoutait un code de 4 octets de relocation pour chaque
    //accès à une variable globale.
stocke_ligne:
    if (ligne_courante>0)           {               //Pas la première ligne
        if (hauteur==0)
            hauteur=HAUTEUR_TEXTE;                  //Pour trouver la plus grande taille de la ligne
        y+=hauteur;                                 //Ajoute la hauteur calculée (maximum de la ligne en fait)
        lignes[ligne_courante-1].l=larg_max;        //On stocke la largeur de celle-ci
        lignes[ligne_courante-1].alignement=alignement;         //Et l'alignement
    }
    
    if (ligne_courante>=MAX_LIGNES-1)               //Erreur
        goto fini;
        
    lt=&lignes[ligne_courante++];                   //La ligne courante
    lt->donnees=ptr;                                //Pointeur sur le texte
    lt->x=marge_x;                                  //Marge en x
    lt->y=y;                                        //Position de la ligne dans l'espace (...)
    lt->l=0;                                        //Largeur de la ligne
    lt->fmt=fm;
    lt->alignement=alignement;

    //(Ré)initialise les variables pour le début de la ligne
    x=marge_x;
    offset_y=0;
    hauteur=0;                                      //Pour trouver la plus grande taille de la ligne
    larg_max=0;

    x_dernierespace=0, ptr_dernierespace=NULL;

    while(1)        {
        if (x>larg_max)
            larg_max=x;
        c=*ptr++;

        //Fin de la chaîne
        if (c==0 || FEOF(ptr))
            break;

        //Retour à la ligne
        if (c=='\r')       {
            //Charset Windows! \r\n. Vous devrez probablement retirer cette ligne si vous utilisez un
            //autre OS!
            ptr++;
            hauteur+=haut_sup;
            haut_sup=0;
            //Prochaine ligne
            goto stocke_ligne;
        }

        //Format de caractère
        if (c=='©')     {
            ptr--;
            STOCKE_INFOS;
            ptr++;
            i=GetFormat(&fm,ptr)-ptr;
            ptr+=i;
        }
        //Commande spéciale
        else if (c=='#')        {
            unsigned char str[MAX_STR], cmd;
            signed char flags[MAX_ARGS];
            int args[MAX_ARGS];
            ptr--;
            STOCKE_INFOS;
            ptr++;
            do          {
                //Le prochain caractère indique quelle sera la commande
                cmd=*ptr++;
                ptr=AnalyseCommande(ptr,str,args,flags,x);
                //A= Alignement
                if (cmd=='A')         {
                    if (args[0]>=0 && args[0]<=2)
                        alignement=args[0];
                }
                else if (cmd=='F')          {
                    ChargeFonte(str);
                    ChangeFonteSt(fontes[fm.fonte]);
                }
                else if (cmd=='P')          {
                    if (args[0]>=0 && args[0]<240)          {
                        AjouteImage(args[0],y+args[1],str);
                        //Positions égales = image intégrée au texte (habillage "Aligné sur le texte" de MS Word)
                        //Sinon = habillage "Devant le texte".
                        if (args[0]==x && args[1]==0)           {
                            nbObjetsTxt--;
                            x+=objetsTxt[nbObjetsTxt].l;
                            if (x>LARGEUR_LCD)
                                goto fin_ligne;
                            if (objetsTxt[nbObjetsTxt].h>hauteur)
                                hauteur=objetsTxt[nbObjetsTxt].h;
                        }
                    }
                }
                else if (cmd=='C')              {
                    x=args[0];
                    offset_y=args[1];
                }
                else if (cmd=='H')
                    haut_sup=args[0];
                if (x>larg_max)
                    larg_max=x;
            } while(*ptr++=='|');
        }
        else if (c=='\t')       {
            STOCKE_INFOS;
            //Tabulation: 20 pixels
            x=(x/20)*20+20;
            goto caractPose;
        }
        //Caractère imprimable
        else if (c>=32)   {
            //Si c'est un espace, on sauve les infos
            if (c==32)
                STOCKE_INFOS;

            //On veut garder la plus grande hauteur de la ligne
            //2*((fm.souligne+1)/2)
            i=HAUTEUR_TEXTE;
            if (i>hauteur && x>marge_x)
                hauteur=i;

            //On ajoute la largeur
            x+=tailles[c-32];
caractPose:
            //Il va falloir vérifier si ce caractère a fait déborder la ligne sur sa
            //largeur...
            if (x>=LARGEUR_LCD)     {
fin_ligne:
                //Ce caractère n'est pas inclus dans la ligne puisqu'il l'a faite débo  rder
                larg_max=x_dernierespace;
                if (ptr_dernierespace)                  //En cas d'erreur
                    ptr=ptr_dernierespace;
                else
                    ptr--;
                //Prochaine ligne
                goto stocke_ligne;
            }
        }
    }

    lignes[ligne_courante-1].l=larg_max;                //La largeur de la ligne
    lignes[ligne_courante-1].alignement=alignement;     //Et l'alignement

    lignes[ligne_courante].donnees=NULL;                //Signifie la fin
    lignes[ligne_courante].y=y;

fini:
    hauteur_totale=y+hauteur+haut_sup;
    return lignes;
}

int gblClip_y0, gblClip_y1;

void DessineLigne(LIGNE_TEXTE *ligne, s32 y)       {
    u32 x, baseX;
    unsigned char *ptr=ligne->donnees, *fin=(ligne+1)->donnees, c;
    int i, offset_y=0;
    FORMAT_TEXTE fm=ligne->fmt;
    void (*routineDessCar)();

    if (ligne->alignement==ALIGN_DROITE)
        x=240-ligne->l;
    else if (ligne->alignement==ALIGN_CENTRE)
        x=120-(ligne->l>>1);
    else                                            //ALIGN_GAUCHE si inconnu
        x=ligne->x;
    if (x<0)            x=0;

    baseX=x;
    routineDessCar=(void*)(fm.italique?DessCarItalique256:DessCar256);
    
    if (fin==NULL)                          //Pas de fin définie? -> pas de limite
        fin=(unsigned char*)~0;
    
    ChangeFonteSt(fontes[fm.fonte]);
    
    while(ptr<fin)       {
        c=*ptr++;
        if (c=='\r' || c==0 || FEOF(ptr))
            break;
        else if (c=='©')     {
            ptr=GetFormat(&fm,ptr);
            routineDessCar=(void*)(fm.italique?DessCarItalique256:DessCar256);
        }
        else if (c=='#')        {
            unsigned char str[MAX_STR], cmd;
            signed char flags[MAX_ARGS];
            int args[MAX_ARGS];
            do          {
                //Le prochain caractère indique quelle sera la commande
                cmd=*ptr++;
                ptr=AnalyseCommande(ptr,str,args,flags,x);
                if (cmd=='P')           {
                    //Voir plus haut (analyse)
                    if (args[0]==x && args[1]==0)           {
                        AjouteImage(args[0],y+args[1],str);
                        nbObjetsTxt--;
                        DessineClipBitmap(&objetsTxt[nbObjetsTxt],0,gblClip_y0,gblClip_y1);
                        x+=objetsTxt[nbObjetsTxt].l;
                    }
                }
                else if (cmd=='C')      {
                    if (flags[0])
                        x=args[0];
                    else
                        x=baseX+args[0];
                    offset_y=args[1];
                }
            }
            while(*ptr++=='|');
        }
        else if (c=='\t')       {
            x=(x/20)*20+20;
        }
        else        {
            routineDessCar(x,y+offset_y,c,fm.couleur);
            if (fm.gras==1)
                routineDessCar(x+offset_y+1,y,c,fm.couleur);
            else if (fm.gras==2)
                routineDessCar(x,y+offset_y+1,c,fm.couleur);
            else if (fm.gras==3)
                routineDessCar(x+1,y+offset_y+1,c,fm.couleur);
            //Ecriture pixel par pixel de la ligne en-dessous. Vraiment très lent!!!
            if (fm.souligne && y+offset_y+hauteur_texte>=0 && y+offset_y+hauteur_texte<160)        {
                for (i=0;i<tailles[c-32];i++)
                    Exec(Mode4_EcritPixel,x+i,y+offset_y+hauteur_texte,fm.couleur);
            }
            x+=tailles[c-32];
        }
    }
}

void DefileHaut(int nombre)       {
    int zero=0;
    MemMove16bits(MemVideo,(void*)MemVideo+240*nombre,120*(160-nombre));
    MemSet(&zero,(void*)MemVideo+240*(160-nombre),120*nombre,DMA_16im);
}

void DefileBas(int nombre)       {
    int zero=0;
    MemMove16bits((void*)MemVideo+240*nombre,MemVideo,120*(160-nombre));
    MemSet(&zero,(void*)MemVideo,120*nombre,DMA_16im);
}

void LTMain(unsigned char *fichier)       {
    LIGNE_TEXTE *lignes;
    int defil;
    int i, appui_l=1, appui_r=1, amplitude=0;
    int descente=0;
    int y, rafraichissement=1;
    int clip_y0, clip_y1;
    int zero=0;
    unsigned short *pal;
    unsigned char *donnees_texte;

    FadeIn();
    
    //Vidage complet de la mémoire vidéo
    MemSet(&zero,CharBaseBlock(0),16384,DMA_32im);

    pal=SymFind("palette.pal",NULL);

    if (pal)
        CopieDMA(pal+1,Palette,256,DMA_16im);

    //Préparation du matériel graphique
    REG_DISPCNT = (VIDEO_MODE4 | VIDEO_BG2_ENABLED);

    nFontes=0;
    //Chargez vos fontes par défaut ici. Par exemple:
//    ChargeFonte("arial.fnt");
    ChangeFonte256(Verdana);
    LitFonteSt(fontes[nFontes]);
    nFontes++;
    
    //Avant de commencer, s'assurer que la fonte est bien la 0
    ChangeFonteSt(fontes[0]);

    donnees_texte=SymFind(fichier,&gblCurFile);
    if (!donnees_texte)         {
        DessTexte256(0,0,"Fichier texte non-trouvé:",0xfc);
        DessTexte256(0,12,fichier,0xfe);
        DessTexte256(0,30,"Appuyez sur Start pour continuer.",0xff);
        JoueSon("erreur.son");
        FadeOut();
        while(!(REG_TOUCHES&TOUCHE_START));
        JoueSon("curs3.son");
        FadeIn();
        return;
    }

    objetsTxt=malloc(sizeof(OBJET_TEXTE)*MAX_OBJETS);
    MemSet(&zero,objetsTxt,(sizeof(OBJET_TEXTE)*MAX_OBJETS)>>2,DMA_32im);
    nbObjetsTxt=0;

    lignes=AnalyseTexte(donnees_texte);
    defil=0;
    i=0;

redraw:
    if (rafraichissement<=1)
        clip_y0=0;
    else
        clip_y0=160-amplitude;
    if (rafraichissement>=1)
        clip_y1=160;
    else
        clip_y1=amplitude;

    //Sauvegarde des zones de clipping pour pouvoir y accéder depuis DessineLigne
    gblClip_y0=clip_y0, gblClip_y1=clip_y1;
    //Dessin des objets
    for (i=0;i<nbObjetsTxt;i++)       {
        if (!objetsTxt[i].type)
            continue;
        if (objetsTxt[i].y+objetsTxt[i].h-defil>clip_y0 || objetsTxt[i].y-defil<clip_y1)
            DessineClipBitmap(&objetsTxt[i],defil,clip_y0,clip_y1);
    }

    i=0;

    if (rafraichissement<=1)        {
        while(lignes[i].y-defil<clip_y0 && lignes[i].donnees)
            i++;
        if (i>0)
            i--;
    }
    else        {
        while(lignes[i].y-defil<clip_y0 && lignes[i].donnees)
            i++;
        if (i>0)
            i--;
    }

    while(1)            {
        y=lignes[i].y-defil;                            //Calcule la position relative au défilement

        if (y>=clip_y1)
            break;
        if (lignes[i].donnees==NULL)                    //Fin du texte
            break;

        DessineLigne(&lignes[i],y);
        i++;

//        if (rafraichissement!=1)
//            break;
    }

    while(1)        {
        if (fade_actif)
            FadeOut();
        
        if (UtilCPU()<1050)
            AttendVSync();

        InitTimers();
        
        if (REG_TOUCHES&TOUCHE_B)
            break;

        if (!descente)      {
            if (REG_TOUCHES&TOUCHE_HAUT && defil>0)        {
                DefileBas(1);
                defil--;
                amplitude=1;
                rafraichissement=0;
                goto redraw;
            }
            if (REG_TOUCHES&TOUCHE_BAS && defil<hauteur_totale-160)     {
                DefileHaut(1);
                defil++;
                amplitude=1;
                rafraichissement=2;
                goto redraw;
            }
        }


        if ((defil<=0 && descente<0) || (descente>0 && defil>=hauteur_totale-160))
            descente=0;

        if (descente<0)        {
            if (defil-8<0)      {
                amplitude=defil;
                descente=0;
            }
            else            {
                descente++;
                amplitude=8;
            }
            DefileBas(amplitude);
            defil-=amplitude;
            rafraichissement=0;
            goto redraw;
        }
        
        if (descente>0)     {
            if (defil+8>=hauteur_totale-160)      {
                amplitude=-defil+(hauteur_totale-160);
                descente=0;
            }
            else            {
                descente--;
                amplitude=8;
            }
            DefileHaut(amplitude);
            defil+=amplitude;
            rafraichissement=2;
            goto redraw;
        }
            
        if (REG_TOUCHES&TOUCHE_R)       {
            if (!appui_r)       {
                appui_r=1;
                descente=16;
            }
        }
        else
            appui_r=0;

        if (REG_TOUCHES&TOUCHE_L)       {
            if (!appui_l)       {
                appui_l=1;
                descente=-16;
            }
        }
        else
            appui_l=0;
    }

    JoueSon("curs3.son");
    FadeIn();
    free(lignes);
    free(objetsTxt);
}




