/************************************************************************
**************************************************************************
**                                                                      **
**  JayCalc.c                                                           **
**  ---------                                                           **
**                                                                      **
**  Auteur : Eric Le Saux                                               **
**                                                                      **
**           Club Amiga Montreal (CAM)                                  **
**           C.P. 195 STATION "N"                                       **
**           Montreal (Quebec)                                          **
**           H2X 3M2, CANADA                                            **
**                                                                      **
**  Date   : 20 decembre 1987                                           **
**                                                                      **
**  Statut : DOMAINE PUBLIC - Distribution non-commerciale seulement    **
**           PUBLIC DOMAIN  - Freely redistributable                    **
**                                                                      **
**************************************************************************
 ************************************************************************/

/*** Fichiers d'entetes *************************************************/

#include <stdio.h>
#include <ctype.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <JayCalc.h>

/*** Fonctions et variables externes ************************************/

extern struct Library		*OpenLibrary ();
extern struct TextFont		*OpenFont    ();
extern struct Window		*OpenWindow  ();
extern struct IntuiMessage	*GetMsg ();

extern int Enable_Abort;

/*** Variables globales pour le maintient du systeme ********************/

struct IntuitionBase 	*IntuitionBase;
struct GfxBase       	*GfxBase;

struct Window 		*Fenetre;

struct RastPort		*RP;

struct TextAttr 	 FonteAttr = { (UBYTE *) "topaz.font", 8, 0, 0 };
struct TextFont 	*Fonte;

/*** Variables globales pour la logique du programme ********************/

#define HEX 0
#define DEC 1
UBYTE ModeControle  = HEX;	/* Mode HEXadecimal ou DECimal */

UBYTE LFControlByte = 0;	/* Valeur de la byte de ctrl du Blitter */

UBYTE RegionsAllumees = 0;	/* Regions du diag. de Venn allumees */

UBYTE TempBuff[EQUBUFFLEN];	/* Buffer temporaire */

BOOL Parasites = TRUE;		/* Quand l'affichage est corrompu. */

BOOL ParErr = FALSE;		/* Erreurs de parentheses ? */

BOOL Insertion = FALSE;		/* TRUE : le Keypad insere a BufferPos  */
	                        /* FALSE: on ecrit a la fin de la ligne */

/*************************************************************************
**************************************************************************
**
**  EvalueEquation ()
**
**  Traverse une string contenant l'equation logique a evaluer.
**  Les symboles errones sont ignores.
**
**  Les OR excessifs sont elimines, ainsi que les symboles redondants
**  dans les operations AND.
**
**  Les touches du keypad arrivent une par une.  Comme les OR ne sont
**  pas permis en fin d'equation, ils sont elimines.  Le booleen NOR
**  est TRUE quand on veut eliminer ce symbole en fin de ligne.
**
*/


UBYTE EvalueEquation (EquPtr, CorrPtr, NOR)
UBYTE *EquPtr;
UBYTE *CorrPtr;
BOOL   NOR;
{
	UBYTE  LF = 0x00;	/* Element neutre pour OR  */
	UBYTE  AndByte = 0xFF;	/* Element neutre pour AND */

	UBYTE  SousEqu[EQUBUFFLEN];  /* Pour l'appel recursif */
	UBYTE  SousCorr[EQUBUFFLEN]; /* Pour l'appel recursif */
	UBYTE  *SEPtr;               /* Pointe dans SousEqu   */
	UBYTE  NbParOuv;	     /* Nb de parentheses ouvertes */

	UBYTE  Symboles = 0x00;	/* Masques des symboles du AND courant */
	UBYTE  SymbMasque;	/* Masque pour le symbole */
	UBYTE  SymbVal;		/* Valeur du symbole considere */

	BOOL   Valide = FALSE;	/* Devient TRUE au premier caractere reconnu */

	ParErr = FALSE;		/* Devient TRUE aux erreurs de parentheses */

      /*=== Conversion avec correction ===*/

	while (*CorrPtr++ = *EquPtr)
	{

	   switch (*EquPtr++)
	   {
	      case KeyA_ID  : SymbVal = 0xF0; SymbMasque = 0x01; break;
	      case KeyNA_ID : SymbVal = 0x0F; SymbMasque = 0x02; break;
	      case KeyB_ID  : SymbVal = 0xCC; SymbMasque = 0x04; break;
	      case KeyNB_ID : SymbVal = 0x33; SymbMasque = 0x08; break;
	      case KeyC_ID  : SymbVal = 0xAA; SymbMasque = 0x10; break;
	      case KeyNC_ID : SymbVal = 0x55; SymbMasque = 0x20; break;
	      case ParFer_ID: CorrPtr--;
	                      continue;

	      case ParOuv_ID: SymbMasque = 0x00;

	                    /*=== Copie de string entre () de meme niveau ===*/

	                      SEPtr = SousEqu;
	                      NbParOuv = 1;
	                      FOREVER
	                      {
	                         *SEPtr = *EquPtr++;
	                         if (*SEPtr == '\0') break;
	                         if (*SEPtr == ParOuv_ID) NbParOuv++;
	                         if (*SEPtr == ParFer_ID)
	                         {
	                            NbParOuv--;
	                            if (NbParOuv==0) break;
	                         }
	                         SEPtr++;
	                      }

	                    /*=== On recure si le dernier car est ParFer_ID ===*/

	                      if (*SEPtr == ParFer_ID)
	                      {
	                         *SEPtr = '\0';
	                         SymbVal = EvalueEquation (SousEqu,SousCorr,NOR);

	                         /* Erreur ? Alors on sort ! */
	                         if (ParErr) return 0;

	                         SEPtr = SousCorr;
	                         while (*CorrPtr++ = *SEPtr++);
	                         *(CorrPtr-1) = ParFer_ID;
	                         *CorrPtr = '\0';
	                      }
	                      else /*=== Sinon c'est une erreur ===*/
	                      {
	                         ParErr = TRUE;
	                         return 0;
	                      }
	                      break;

	      case KeyOR_ID : if (AndByte != 0xFF)
	                      {
	                         LF |= AndByte;
	                         AndByte = 0xFF;
	                         Symboles= 0x00;
	                      }
	                      else /*=== OU excedentaire ===*/
	                      {
	                         CorrPtr--;
	                      }
	                      continue;

	      default       : CorrPtr--; /*=== Symbole non-reconnu ===*/
	                      continue;

	   } /* SWITCH */

	   if (Symboles & SymbMasque)
	   {
	      CorrPtr--;
	      continue;
	   }
	   else
	   {
	      Symboles |= SymbMasque;
	      AndByte  &= SymbVal;
	   }

	   if (AndByte != 0xFF) Valide = TRUE; /*= Au moins 1 bon element =*/


	} /* WHILE */


      /*=== On elimine le OR excendentaire, s'il y en a. ===*/

	if ( (*(CorrPtr-2)==KeyOR_ID) && NOR ) *(CorrPtr-2) = '\0';

      /*=== Si l'equation n'est pas valide, on remet tout a zero. ===*/

	if (!Valide)
	{
	   LF = 0;
	   AndByte = 0;
	}

      /*=== Si la AndByte == 0xFF, le dernier caractere est un OR. ===*/

	if (AndByte == 0xFF) return (LF);
	                else return (LF |= AndByte);
}


/*************************************************************************
**************************************************************************
**
**  ModifieStringGadget ()
**
**  Modifie le buffer d'un string gadget.
**
**
**  Que les paroles de -=RJ=- nous impregnent :
**
**  "Note that to modify the imagery of a gadget, you must first remove
**   that gadget from the Window's Gadget list, using RemoveGadget() (or
**   RemoveGList()).  After changing the Image, Border, Text (including
**   Text for a String Gadget), the gadget is replaced in the Gadget List
**   (using AddGadget() or AddGList()).  Adding gadgets does not cause
**   them to be displayed (refreshed), so this function, or RefreshGList()
**   is typically called."
**
**  Le premier argument pointe au StringGadget a modifier.
**  Le deuxieme a la nouvelle valeur du string gadget.
**  Le troisieme argument specifie le type de cadrage desire lorsque
**  le buffer depasse la capacite d'affichage.  Lorsque le buffer peut
**  etre completement affiche, on centre.
**  CurPos est la nouvelle position du curseur dans le buffer.
**
*/

void ModifieStringGadget (SGadget, SrcPtr, Droite, CurPos)
struct Gadget *SGadget;
UBYTE *SrcPtr;
BOOL  Droite;
SHORT CurPos;
{
	STATIC USHORT PositionGadg; /* Position dans la liste des gadgets */
	STATIC UBYTE  *DestPtr;	    /* Pointe a son buffer */
	STATIC SHORT  BufLen;	    /* Longueur du nouveau buffer */

	STATIC struct StringInfo *S;/* Pointeur au StringInfo du Gadget */

      /*=== On note l'adresse de la struture StringInfo ===*/

	S = (struct StringInfo *)SGadget->SpecialInfo;

      /*=== On enleve le gadget ===*/

	PositionGadg = RemoveGadget (Fenetre, SGadget);

      /*=== On modifie son buffer ===*/

	BufLen = 0;
	DestPtr = S->Buffer;
	while (*DestPtr++ = *SrcPtr++) BufLen++;

      /*=== Choix du cadrage ===*/

	SGadget->Activation &= ~(STRINGCENTER|STRINGRIGHT);
	if (BufLen<S->DispCount)
	{
	   SGadget->Activation |= STRINGCENTER;
	   S->DispPos = -1;
	}
	else if (Droite)
	{
	   SGadget->Activation |= STRINGRIGHT;
	   S->DispPos = BufLen-S->DispCount;
	}
	else
	{
	   S->DispPos = 0;
	}

	S->BufferPos = CurPos;

      /*=== On replace le gadget ===*/

	AddGadget (Fenetre, SGadget, (ULONG) PositionGadg);
	RefreshGList (SGadget, Fenetre, NULL, 1L);
}



/*************************************************************************
**************************************************************************
**
**  Scroll ()
**
**  Deplace l'equation a droite ou a gauche.
**
*/

void Scroll (Cote)
UBYTE Cote;
{
	STATIC USHORT Position;
	struct StringInfo *S = (struct StringInfo *) EquGadg.SpecialInfo;

	if (!(EquGadg.Activation & STRINGCENTER))
	{
	   /*=== On peut inserer ===*/

	      Insertion = TRUE;

	   /*=== On enleve le string gadget ===*/

	      Position = RemoveGadget (Fenetre, &EquGadg);

	   /*=== On scroll ===*/

	      if ( (Cote == ScrolG_ID) && (S->DispPos > 0) )
	      {
	         S->DispPos--;
	         S->BufferPos--;
	      }

	      if ( (Cote == ScrolD_ID) && ( (S->DispPos+S->DispCount) <= S->NumChars ) )
	      {
	         S->DispPos++;
	         S->BufferPos++;
	      }

	   /*=== On replace le gadget ===*/

	      AddGadget (Fenetre, &EquGadg, (ULONG) Position);
	      RefreshGList (&EquGadg, Fenetre, NULL, 1L);
	      ActivateGadget (&EquGadg, Fenetre, NULL);
	}
}



/*************************************************************************
**************************************************************************
**
**  AfficheVenn ()
**
**  Colore le diagramme de Venn selon le code LF courant:
**
**        bit 0 : abc     bit 4 : Abc
**        bit 1 : abC     bit 5 : AbC
**        bit 2 : aBc     bit 6 : ABc
**        bit 3 : aBC     bit 7 : ABC
**
**  Par question d'efficacite, l'etat des regions est change directement
**  a l'aide d'une operation du blitter, plutot que par la modification
**  de la liste de gadgets du diagramme de Venn.
**
**  NOTE: le champ 'UserData' des gadgets pointe aux bitmaps qui les
**        representent.
**
*/

void AfficheVenn ()
{
	STATIC SHORT Index;

	struct Gadget *Liste = &VennGadgList;	/* Pour traverser les gadgets */
	UBYTE  LF = LFControlByte;		/* Pour analyser ses bits */
	UBYTE  RA = RegionsAllumees;		/* Pour analyser ses bits */

	for (Index=0; Index<8; Index++)
	{
	   if ( (LF & 0x01)^(RA & 0x01) ) 
	      BltBitMapRastPort
	      (
	       Liste->UserData,			/* BitMap source */
	       0L, 0L,				/* SrcX, SrcY */
	       Fenetre->RPort,			/* BitMap destination */
	       (LONG)(Liste->LeftEdge),		/* DestX */
	       (LONG)(Liste->TopEdge),		/* DestY */
	       (LONG)(Liste->Width),		/* Nb Pixels en X */
               (LONG)(Liste->Height),		/* Nb Pixels en Y */
	       (LONG)0x66			/* Minterms: B^C */
	      );

	   LF >>= 1;
	   RA >>= 1;

	   Liste = Liste->NextGadget;		/* Au suivant ! */
	}

      /*=== Les regions allumees correspondent maintenant au code LF ===*/

	RegionsAllumees = LFControlByte;
}



/*************************************************************************
**************************************************************************
**
**  Normalise ()
**
**  Affiche l'equation normalisee de la LFControlByte courante.
**
*/

UBYTE *MinTermes[8] = {(UBYTE *)"abc", (UBYTE *)"abC", (UBYTE *)"aBc",
	               (UBYTE *)"aBC", (UBYTE *)"Abc", (UBYTE *)"AbC",
	               (UBYTE *)"ABc", (UBYTE *)"ABC"};
 
void Normalise ()
{
	STATIC SHORT Index;
	STATIC UBYTE *Dest, *MinPtr;

      /*=== Il n'y a plus d'erreur de parentheses ===*/

	ParErr = FALSE;

      /*=== Ce n'est pas de l'affichage parasite ===*/

	Parasites = FALSE;

      /*=== On ne peut plus inserer du Keypad ===*/

	Insertion = FALSE;

      /*=== On formule l'equation normalisee ===*/

	Dest=TempBuff;
	*Dest++ = ' ';
	*Dest   = '\0';

	for (Index=0; Index<8; Index++)
	{
	   if (LFControlByte & (1<<Index))
	   {
	      Dest--;
	      if (*Dest != ' ') *Dest++ = KeyOR_ID;
	      MinPtr = MinTermes[Index];
	      while (*Dest++ = *MinPtr++);
	   }
	}

      /*=== On affiche la nouvelle equation ===*/

	if (LFControlByte == 0)
	{
	   ModifieStringGadget (&EquGadg, NOP_STRING, FALSE, 0);
	   Parasites = TRUE;
	}
	else ModifieStringGadget (&EquGadg, TempBuff, FALSE, 0);
}



/*************************************************************************
**************************************************************************
**
**  CAB ()
**
**  Converti de ASCII vers UBYTE, selon le mode (HEX ou DEC).
**  L'argument est une string a convertir.
**
*/

UBYTE CAB (Ptr)
UBYTE *Ptr;
{
	STATIC UBYTE Bin;

	Bin = 0;

	while (*Ptr)
	{
	   if (islower(*Ptr)) *Ptr = _toupper (*Ptr);

	   if ( (*Ptr>='0') && (*Ptr<='9') )
	   {
	      Bin *= (ModeControle==HEX)?0x10:10;
	      Bin += ( *Ptr - '0' );
	   }

	   if ( (ModeControle==HEX) && (*Ptr>='A') && (*Ptr<='F') )
	   {
	      Bin *= 0x10;
	      Bin += ( *Ptr - 'A' + 10 );
	   }

	   Ptr++;
	}

	return Bin;
}



/*************************************************************************
**************************************************************************
**
**  CBA ()
**
**  Converti de UBYTE vers ASCII selon le Mode.
**  Dans le mode hexadecimal, on met les lettres en majuscules.
**
*/

void CBA (LF, Buff)
UBYTE LF;
UBYTE *Buff;
{
	if (ModeControle == HEX)
	{
	   sprintf (Buff, "0x%02x", LF);
	   if (isalpha(*(Buff+2))) *(Buff+2) = _toupper(*(Buff+2));
	   if (isalpha(*(Buff+3))) *(Buff+3) = _toupper(*(Buff+3));
	}
	else /* DEC */
	   sprintf (Buff, "%u", LF);
}



/*************************************************************************
**************************************************************************
**
**  AfficheLF ()
**
**  Affiche le resultat courant.
**  Sert aussi a afficher un message d'erreur quand l'equation
**  est erronee (au niveau des parentheses).
**
*/

void AfficheLF ()
{
	if ( ParErr )
	{
	   ModifieStringGadget (&LFGadg, "(?)", NULL, 0);
	}
	else
	{
	   CBA (LFControlByte, TempBuff);
	   ModifieStringGadget (&LFGadg, TempBuff, NULL, 0);
	}
}



/*************************************************************************
**************************************************************************
**
**  Equ ()
**
**  L'usager vient d'entrer une equation.
**
*/

void Equ ()
{
      /*=== On evalue l'equation, avec correction ===*/

	LFControlByte = EvalueEquation (EquGadgSInfo.Buffer, TempBuff, TRUE);

      /*=== Si l'equation comporte une erreur de parenthese, ===*/
      /*=== on ne modifie modifie pas son affichage.         ===*/

	if (!ParErr) ModifieStringGadget (&EquGadg, TempBuff, FALSE, 0);

      /*=== On affiche le resultat ===*/

	AfficheLF   ();
	AfficheVenn ();
}



/*************************************************************************
**************************************************************************
**
**  InsEqu ()
**
**  On entre en mode d'insertion.  Nettoyage si Parasites.
**
*/

void InsEqu ()
{
      /*=== On permet l'insertion ===*/

	Insertion = TRUE;

      /*=== On nettoie l'equation si elle contient des parasites ===*/

	if (Parasites) ModifieStringGadget (&EquGadg, "", FALSE, 0);
}



/*************************************************************************
**************************************************************************
**
**  LF ()
**
**  L'usager vient d'entrer une valeur LF.
**
*/

void LF ()
{
      /*=== On converti en binaire ===*/

	LFControlByte = (UBYTE) CAB (LFGadgSInfo.Buffer);

      /*=== On affiche ===*/

	Normalise   ();
	AfficheLF   ();
	AfficheVenn ();
}



/*************************************************************************
**************************************************************************
**
**  Diag ()
**
**  L'usager opere a travers le diagramme de Venn.
**  On fait afficher le LF correspondant.
**
**  Les ID des gadgets (regions) correspondent aux positions des bits
**  dans le minterm.
**
*/

void Diag (Region)
SHORT Region;
{
      /*=== On modifie le minterm. ===*/

	RegionsAllumees = LFControlByte ^= ((UBYTE)1<<(UBYTE)Region);

      /*=== On normalise l'affichage ===*/

	if (LFControlByte == 0)
	{
	   ModifieStringGadget (&EquGadg, NOP_STRING, FALSE, 0);
	   Parasites = TRUE;
	   ParErr    = FALSE;
	}
	else Normalise ();

      /*=== On affiche le resultat ===*/

	AfficheLF   ();
}



/*************************************************************************
**************************************************************************
**
**  Keypad ()
**
**  Insere la touche appuyee au bout de l'equation.
**
*/

void Keypad (Touche)
UBYTE Touche;
{
	STATIC UBYTE *Src, *Dest;
	STATIC UBYTE TempBuff2[EQUBUFFLEN];
	STATIC SHORT Index;
	STATIC SHORT NouPos;
	STATIC SHORT NouLen;

      /*=== Si c'est la premiere action, on enleve la presentation.   ===*/
      /*=== 'Parasites' est mis a FALSE dans la fonction du serveur.  ===*/

	if (Parasites)
	{
	   EquGadgSInfo.Buffer[0] = '\0';
	   Parasites = FALSE;
	}

      /*=== On traite la touche ===*/

	if (Touche == Mode_ID)		/*=== On change de mode ===*/
	   {
	      ModeControle = (ModeControle==HEX)?DEC:HEX;
	      CBA (LFControlByte, TempBuff);
	      ModifieStringGadget (&LFGadg, TempBuff, NULL, 0);
	   }
	else if (Touche == KeyRst_ID)	/*=== On met tout a zero ===*/
	   {
	      LFControlByte = 0;
	      ModifieStringGadget (&EquGadg, NOP_STRING, NULL, 0);
	      Parasites = TRUE;
	      ParErr    = FALSE;
	      Insertion = FALSE;
	      AfficheLF   ();
	      AfficheVenn ();
	   }
	else if (Touche == KeyEqu_ID)	/*=== On normalise l'equation ===*/
	   {
	      Normalise ();
	   }
	else /*=== C'est un terme a ajouter a l'equation ===*/
	   {
	      if ( strlen(EquGadgSInfo.Buffer) < (EQUBUFFLEN-2) )
	      /* -2 a cause d'un bug d'affichage des string gadgets */
	      {
	         /*=== On ajoute le symbole a l'equation ===*/

	           Src  = EquGadgSInfo.Buffer;
	           Dest = TempBuff2;

	           if (Insertion)
	           {
	              for (Index=0; Index<EquGadgSInfo.BufferPos; Index++)
	                 *Dest++ = *Src++;
	              if (Touche != Back_ID)
	                 *Dest++ = Touche;
	              else if (EquGadgSInfo.BufferPos > 0)
	                 Dest--;
	           }

	           while (*Dest++ = *Src++);

	           if (!Insertion)
	           {
	              *(Dest-1) = Touche;
	              *Dest = '\0';
	           }

	         /*=== On evalue l'equation ===*/

	           LFControlByte = EvalueEquation (TempBuff2, TempBuff, FALSE);

	         /*=== Affichage des resultats ===*/

	           if (ParErr)
	           {
	              Src = TempBuff2;
	              NouLen = strlen (TempBuff2);
	           }
	           else
	           {
	              Src = TempBuff;
	              NouLen = strlen (TempBuff);
	           }

	           NouPos = EquGadgSInfo.BufferPos + (NouLen - EquGadgSInfo.NumChars);

	           ModifieStringGadget (&EquGadg, Src,  TRUE, NouPos);

	           AfficheLF   ();
	           AfficheVenn ();

	         /*=== On active le gadget de l'equation, si Insertion. ===*/

	           if (Insertion) ActivateGadget (&EquGadg, Fenetre, NULL);
	      }
	      else
	      {
	         /*=== Pas de place, on le signale a l'usager. ===*/

	           DisplayBeep (NULL);
	      }
	   }
}



/*************************************************************************
**************************************************************************
**
**  TraiteGadget ()
**
**  Serveur pour traiter les commandes de l'usager.
**  L'argument est un pointeur au gadget appuye.
**
*/

void TraiteGadget (Classe, GadgAdresse)
ULONG  Classe;
struct Gadget *GadgAdresse;
{

        switch (GadgAdresse->GadgetID)
	{

	 /*=== Keypad ===*/

	   case Mode_ID   :	Keypad (Mode_ID);		break;
	   case KeyA_ID   :	Keypad (KeyA_ID);		break;
	   case KeyNA_ID  :	Keypad (KeyNA_ID);		break;
	   case KeyB_ID   :	Keypad (KeyB_ID);		break;
	   case KeyNB_ID  :	Keypad (KeyNB_ID);		break;
	   case KeyC_ID   :	Keypad (KeyC_ID);		break;
	   case KeyNC_ID  :	Keypad (KeyNC_ID);		break;
	   case KeyOR_ID  :	Keypad (KeyOR_ID);		break;
	   case KeyEqu_ID :	Keypad (KeyEqu_ID);		break;
	   case KeyRst_ID :	Keypad (KeyRst_ID);		break;
	   case ParOuv_ID :	Keypad (ParOuv_ID);		break;
	   case ParFer_ID :	Keypad (ParFer_ID);		break;
	   case Back_ID   :	Keypad (Back_ID);		break;

	   case ScrolD_ID :	Scroll (ScrolD_ID);		break;
	   case ScrolG_ID :	Scroll (ScrolG_ID);		break;

	 /*=== Diagramme de Venn ===*/

	   case Venn0_ID  :	Diag (Venn0_ID);		break;
	   case VennA_ID  :	Diag (VennA_ID);		break;
	   case VennB_ID  :	Diag (VennB_ID);		break;
	   case VennC_ID  :	Diag (VennC_ID);		break;
	   case VennAB_ID :	Diag (VennAB_ID);		break;
	   case VennAC_ID :	Diag (VennAC_ID);		break;
	   case VennBC_ID :	Diag (VennBC_ID);		break;
	   case VennABC_ID:	Diag (VennABC_ID);		break;

	 /*=== Gadgets texte ===*/

	   case LFGadg_ID :	LF ();
	                        break;

	   case EquGadg_ID:	if (Classe == GADGETDOWN)
	                           InsEqu ();
	                        else
	                           Equ ();
	                        break;

	   default        :     break; /* i.e. MENUDOWN sans Insertion */
	}
}



/*************************************************************************
**************************************************************************
**
**  Sortie ()
**
**  Ferme ce qui a ete ouvert.
**
*/

void Sortie ()
{
	if (Fenetre)       CloseWindow  (Fenetre);
	if (IntuitionBase) CloseLibrary (IntuitionBase);
	if (GfxBase)       CloseLibrary (GfxBase);
	if (Fonte)	   CloseFont	(Fonte);

	_exit (0L);
}



/*************************************************************************
**************************************************************************
**
**  Erreur ()
**
**  Affiche un message d'erreur a travers un requester.
**  Laisse le choix entre l'abandon de la procedure et
**  un autre essai.
**
*/

#define ErrGfxLib   0
#define ErrIntuiLib 1
#define ErrOpenWin  2
#define ErrFonte    3

struct IntuiText Message = { 2, 1, JAM1, 17, 10, NULL, NULL, NULL };
struct IntuiText Essai   = { 2, 1, JAM1, 5, 5, NULL, (UBYTE *) "Tentative", NULL };
struct IntuiText Abandon = { 2, 1, JAM1, 5, 5, NULL, (UBYTE *) "Abandon", NULL };

void Erreur (Err)
SHORT Err;
{
	SHORT EssaieEncore;

	switch (Err)
	{
	   case ErrGfxLib   : Message.IText = (UBYTE *) "Erreur: OpenLibrary: GfxLib";
	                      break;
	   case ErrIntuiLib : Message.IText = (UBYTE *) "Erreur: OpenLibrary: IntuiLib)";
	                      break;
	   case ErrOpenWin  : Message.IText = (UBYTE *) "Erreur: OpenWindow";
	                      break;
	   case ErrFonte    : Message.IText = (UBYTE *) "Erreur: OpenFont: Topaz 8";
	                      break;
	   default          : Message.IText = (UBYTE *) "Erreur inconnue !";
	                      break;
	}

	EssaieEncore = AutoRequest (Fenetre, &Message, &Essai, &Abandon,
	                            NULL, NULL, 300L, 60L);

	if (EssaieEncore) return;

	Sortie ();
}



/*************************************************************************
**************************************************************************
**
**  Initialise ()
**
*/

void Initialise ()
{
   /*=== La librairie Intuition ===*/
   /*=== Si on n'a pas la bonne version, on ouvre celle presente pour ===*/
   /*=== appeler la fonction AutoRequest ().                          ===*/

	FOREVER
	{
	   IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 33L); /* v1.2+ */
	   if (!IntuitionBase)
	   {
	      IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 0L);
	      if (!IntuitionBase) Sortie ();   /* Plus le choix ! */
	      Erreur (ErrIntuiLib);
	      Sortie ();
	   }
	   else break;
	}

   /*=== La librairie graphique ===*/

	FOREVER
	{
	   GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 0L);
	   if (!GfxBase) Erreur (ErrGfxLib);
	            else break;
	}

   /*=== La ROMFonte par defaut ===*/

	FOREVER
	{
	   Fonte = OpenFont (&FonteAttr);
	   if (!Fonte) Erreur (ErrFonte);
	          else break;
	}

   /*=== On ouvre la fenetre ===*/

	FOREVER
	{
	   Fenetre = OpenWindow (&FenetreData);
	   if (!Fenetre) Erreur (ErrOpenWin);
	            else break;
	}

   /*=== On pointe le RastPort de la fenetre ===*/

	RP = Fenetre->RPort;

   /*=== On assigne la fonte ===*/

	SetFont (RP, Fonte);

   /*=== On dessine la calculatrice ===*/

	DrawImage (RP, &Dessin, 0L, 0L);

   /*=== On rafraichit l'imagerie des gadgets ===*/

	RefreshGList (&EquGadg,  Fenetre, NULL, 2L); /* Les strings */
	RefreshGList (&ModeGadg, Fenetre, NULL, 1L); /* Le mode */
}



/*************************************************************************
**************************************************************************
**
**  _Abort ()
**
**  Remplace la fonction du systeme qui trappe les CTRL-C.
**
*/

void _Abort ()
{
	Sortie ();
}



/*************************************************************************
**************************************************************************
**************************************************************************
***
***  main ()
***
***  Depart du CLI :
***
***     - Si on a un argument, on assume que c'est une equation
***       a resoudre immediatement, sans passer par la calculatrice.
***
***     - Sans arguments, on ouvre la calculatrice interactive.
***
***  Depart du WorkBench :
***
***     - On ouvre la calculatrice interactive.
***
**/

main (NbArgs, Args)
SHORT NbArgs;
UBYTE **Args;
{
	UBYTE	*EquPtr = *(Args+1);  /* Premier argument (equation) */

	struct IntuiMessage *Msg;     /* Pointeur pour prendre les messages */
	ULONG Classe;                 /* Pour noter la classe du message */
	APTR  Adresse;                /* Pour noter l'addresse du gadget */

	ULONG Eve;		      /* Masque pour l'evenement signale */

   /*=== On permet l'abandon du programme ===*/

	Enable_Abort = 1;

   /*=== Si on a des arguments, on repond au CLI, et on sort. ===*/

	if (NbArgs > 1)
	{
	   LFControlByte = EvalueEquation (EquPtr, TempBuff, TRUE);
	   printf ("LF : hex(0x%02x), dec(%u)\n",LFControlByte, LFControlByte);
	   printf ("Equation corrigee : %s\n", TempBuff);
	   _exit (0L);
	}

   /*=== On ouvre la calculatrice ===*/

	Initialise ();

	FOREVER
	{

	/*=== On attends un evenement: IntuiMessage ou CTRL_C. ===*/

	   Eve = Wait (1L<<Fenetre->UserPort->mp_SigBit | SIGBREAKF_CTRL_C);

	   if (Eve & SIGBREAKF_CTRL_C)
	      Sortie ();
	   else
	      Msg = GetMsg (Fenetre->UserPort);

	/*=== On note les informations pertinentes du message ===*/

	   Classe  = Msg->Class;
	   Adresse = Msg->IAddress;

	/*=== On informe Intuition qu'on a lu le message ===*/

	   ReplyMsg (Msg);

	/*=== On branche sur la bonne voie.  Si c'est le premier msg, ===*/
	/*=== on fait un nettoyage de l'affichage.                    ===*/

	   if (Classe == CLOSEWINDOW)
	      Sortie ();
	   else
	      TraiteGadget (Classe, Adresse);
	}
}
