/***********************************************************
 *
 *  process.c
 *
 *  process.c verarbeitet die Tastendrücke vom "Calc".
 *
 *  Peter Gober, 06-Nov-88
 *               14-Oct-88 Fehler korrigiert
 *
 ***********************************************************/


#include <errno.h>
#include <math.h>
                                             

#define PI 3.141592653589793
/* #define DEBUG */


/*
 *  Aus calc.h, aber wir wollen nicht immer intuition.h lesen müssen
 */

#define DISPCHARS   21

#define SYSDEZ 0
#define SYSHEX 1
#define SYSOKT 2
#define SYSBIN 3

#define WINKDEG 0
#define WINKRAD 1
#define WINKGRD 2

extern char dispbuf[];


/*
 *  Ein paar Einstellungen, die von 'wo anders geändert werden
 *  (siehe calc.h)
 */

int anz = 0, stell = -1, zahlsys = 0, wink = 0;
int fkeypress = 0;


/*
 *  Einige Flags
 */

int inpmode = 0;                        /* TRUE, wenn gerade eine Zahl
                        eingeben wird und noch kein Operand eingegeben ist */

int opentered = 0;                      /* TRUE; falls Rechenzeichen schon
                                           eingegeben wurde */

/*
 *  Der Eingabepuffer, nimmt eingegebene Zeichen auf während inpmode TRUE ist
 */

char inpbuf[DISPCHARS+1] = '\0';
int inpbufptr = 0;


/*
 *  Der Stack (nimmt frühere Operanden und Operatoren auf, die noch nicht
 *  bearbeitet wurden; oberstes Element ist "x") und Codierung des Operators
 */

#define STACKDEPTH 20

struct
{
   double x;
   int op;
} stack[STACKDEPTH] = { { 0, 0 } };
int stackptr = 0;

#define POT    0xf0                     /* Codes für op */
#define MULT   0xe0
#define DIV    0xe1
#define PLUS   0xd0
#define MIN    0xd1
#define AND    0xc0
#define XOR    0xb0
#define OR     0xa0
#define BRAC   0x10                     /* Klammer */
#define END    0x00
#define PRI(a) (a & 0xf0)               /* maskiert nur Priorität aus */


/*
 *  Speicher des Taschenrechners
 */

double r = 0.0;


process(key)
int key;
{
   register char *s;
   char *strchr();
   double inpnum(), rad2q(), q2rad();

   switch(key)
   {
      case 00: fkeypress = !fkeypress;  /* f-Taste */
               break;

      case 01: /*
                *  ln
                */

               errno = 0;
               if (!fkeypress)
                  stack[stackptr].x = log(stack[stackptr].x);
               else
                  stack[stackptr].x = exp(stack[stackptr].x);
               if (errno)
                  error("Bereich");

               inpmode = 0;
               opentered = 0;
               break;

      case 02: /*
                *  lg
                */

               errno = 0;
               if (!fkeypress)
                  stack[stackptr].x = log10(stack[stackptr].x);
               else
                  stack[stackptr].x = pow(10.0,stack[stackptr].x);
               if (errno)
                  error("Bereich");

               inpmode = 0;
               opentered = 0;
               break;

      case 03: workstack(POT);         /* y^x */
               inpmode = 0;
               break;

      case 04: /*
                *  lg
                */

               errno = 0;
               if (!fkeypress)
                  stack[stackptr].x = sqrt(stack[stackptr].x);
               else
                  stack[stackptr].x = stack[stackptr].x * stack[stackptr].x;
               if (errno)
                  error("Bereich");

               inpmode = 0;
               opentered = 0;
               break;

      case 10: /*
                *  sin
                */
               if (zahlsys != SYSHEX)
               {
                  errno = 0;
                  if (!fkeypress)
                     stack[stackptr].x = sin(q2rad(stack[stackptr].x));
                  else
                     stack[stackptr].x = rad2q(asin(stack[stackptr].x));
                  if (errno)
                     error("Bereich");

                  inpmode = 0;
                  opentered = 0;
               }
               else
               {
                  addinp('a');
               }
               break;

      case 11: /*
                *  cos
                */

               if (zahlsys != SYSHEX)
               {
                  errno = 0;
                  if (!fkeypress)
                     stack[stackptr].x = cos(q2rad(stack[stackptr].x));
                  else
                     stack[stackptr].x = rad2q(acos(stack[stackptr].x));
                  if (errno)
                     error("Bereich");

                  inpmode = 0;
                  opentered = 0;
               }
               else
               {
                  addinp('b');
               }
               break;

      case 12: /*
                *  tan
                */

               if (zahlsys != SYSHEX)
               {
                  errno = 0;
                  if (!fkeypress)
                     stack[stackptr].x = tan(q2rad(stack[stackptr].x));
                  else
                     stack[stackptr].x = rad2q(atan(stack[stackptr].x));
                  if (errno)
                     error("Bereich");

                  inpmode = 0;
                  opentered = 0;
               }
               else
               {
                  addinp('c');
               }
               break;

      case 13: /*
                *  ¶
                */

               if (zahlsys != SYSHEX)
               {
                  if (!inpmode)
                  {
                     if (opentered)
                     {
                        incstackptr();
                        opentered = 0;
                      }
                  }
                  stack[stackptr].x = PI;
                  opentered = 0;
                  inpmode = 0;
               }
               else
               {
                  addinp('d');
               }
               break;

      case 14: /*
                *  x<>y
                */

               if (zahlsys != SYSHEX)
               {
                  inpmode = 0;
                  opentered = 0;

                  if (stackptr > 0 && stack[stackptr-1].op != BRAC)
                                        /* geht nur, wenn da keine ( ist! */
                  {
                     double a;
                     a = stack[stackptr-1].x;
                     stack[stackptr-1].x = stack[stackptr].x;
                     stack[stackptr].x = a;
                  }
               }
               else
               {
                  addinp('e');
               }
               break;

      case 20: /*
                *  1/x
                */

               if (zahlsys != SYSHEX)
               {
                  inpmode = 0;
                  opentered = 0;
                  if (stack[stackptr].x == 0.0)
                     error("1/0");
                  else
                     stack[stackptr].x = 1.0 / stack[stackptr].x;
               }
               else
               {
                  addinp('f');
               }
               break;

      case 21: if (!fkeypress)
               {
                  /*
                   *  +/-
                   */

                  if (!inpmode)
                  {
                     stack[stackptr].x = -stack[stackptr].x;
                  }
                  else
                  {
                     s = strchr(inpbuf, 'e');
                                        /* Position des es */

                     if (!s)
                     {
                        /*
                         *  Kein Exponent, es geht um's Vorzeichen der
                         *  Mantisse
                         */
                        if (inpbuf[0] == '-')
                        {
                           /*
                            *  Minus wegmachen
                            */
                           movmem(inpbuf+1, inpbuf, inpbufptr+1);
                           inpbufptr--;
                        }
                        else
                        {
                           if (inpbufptr >= DISPCHARS)
                              DisplayBeep(0L);
                           else
                           {
                              /*
                               *  Minus hinmachen
                               */
                              movmem(inpbuf, inpbuf+1, inpbufptr+2);
                              inpbufptr++;
                              inpbuf[0] = '-';
                           }
                        }
                     }
                     else
                     {
                        /*
                         *  Minus ist für den Exponent
                         */
                        if (*(s+1) == '-')
                        {
                           /*
                            *  Minus wegmachen
                            */
                           movmem(s+2, s+1, (int) ((inpbufptr+inpbuf)-s));
                           inpbufptr--;
                        }
                        else
                        {
                           if (inpbufptr >= DISPCHARS)
                              DisplayBeep(0L);
                           else
                           {
                              /*
                               *  Minus hinmachen
                               */
                              movmem(s+1, s+2, inpbuf+1,
                                     (int) ((inpbufptr+inpbuf)-s)+1);
                              inpbufptr++;
                              *(s+1) = '-';
                           }
                        }
                     }
                  }
               }
               else
               {
                  /*
                   *  AND
                   */
                  workstack(AND);
                  inpmode = 0;
               }
               break;

      case 22: if (!fkeypress)
               {
                  /*
                   *  (
                   */

                  if (stack[stackptr-1].op != BRAC)
                  {
                     /*
                      *  Nur, falls letzte Eingabe nicht schon eine ( war
                      */
                     if (!opentered)
                     {
                        /*
                         *  Kein Operator vor (?  Dann fangen wir von vorne an
                         */

                        stackptr = 0;
                        stack[0].x = 0.0;
                     }
                     else
                        incstackptr();
                  }
                  stack[stackptr].op = BRAC;
                                        /* BRAC auf Stack ablegen */
                  incstackptr();

                  stack[stackptr].x = 0.0;
                  opentered = 0;
                  inpmode = 0;
               }
               else
               {
                  /*
                   *  OR
                   */
                  workstack(OR);
                  inpmode = 0;
               }
               break;

      case 23: if (!fkeypress)
               {
                  /*
                   *  )
                   */

                  workstack(BRAC);   /* Stack bis ( abarbeiten */

                  opentered = 0;
                  inpmode = 0;
               }
               else
               {
                  /*
                   *  XOR
                   */
                  workstack(XOR);
                  inpmode = 0;
               }
               break;

      case 24: if (!fkeypress)
               {
                  /*
                   *  <-
                   */

                  if (inpmode)
                  {
                     if (inpbufptr == 1) /* wie C */
                     {
                        inpmode = 0;
                        stack[stackptr].x = 0.0;
                     }
                     else               /* letzte Stelle löschen */
                     {
                        inpbufptr--;
                        inpbuf[inpbufptr] = '\0';
                     }
                  }
                  else
                  {
                     stackptr = 0;      /* wie C */
                     opentered = 0;
                     stack[stackptr].x = 0.0;
                  }
               }
               else
               {
                  /*
                   *  NOT
                   */
                  inpmode = 0;
                  opentered = 0;
                  stack[stackptr].x = (float) ~ (int) stack[stackptr].x;
                                        /* Tut mir leid, anders geht es
                                           nicht */
               }
               break;

      case 61: addinp('0'); break;      /* Zahlentasten */
      case 51: addinp('1'); break;
      case 52: addinp('2'); break;
      case 53: addinp('3'); break;
      case 41: addinp('4'); break;
      case 42: addinp('5'); break;
      case 43: addinp('6'); break;
      case 31: addinp('7'); break;
      case 32: addinp('8'); break;
      case 33: addinp('9'); break;

      case 62: /*
                *  .
                */

               if (inpmode && (strchr(inpbuf, '.') || strchr(inpbuf, 'e')))
               {
                  /*
                   *  Äh, wir haben da schon einen Punkt!
                   */

                  DisplayBeep(0L);
               }
               else
               {
                  addinp('.');
               }
               break;

      case 60: if (inpmode)             /* C */
               {
                  /*
                   *  Wenn wir im inpmode sind, nur die gerade eingegebene
                   *  Zahl löschen (entspricht CE bei einigen Rechnern)
                   */
                  inpmode = 0;
               }
               else
               {
                  /*
                   *  sonst ganze Rechnung löschen
                   */
                  stackptr = 0;
                  opentered = 0;
               }
               stack[stackptr].x = 0.0;
               break;

      case 64: workstack(PLUS);         /* + */
               inpmode = 0;
               break;

      case 54: workstack(MIN);          /* - */
               inpmode = 0;
               break;

      case 44: workstack(MULT);         /* × */
               inpmode = 0;
               break;

      case 34: workstack(DIV);          /* ÷ */
               inpmode = 0;
               break;

      case 63: workstack(END);          /* = */
               opentered = 0;
               inpmode = 0;
               break;

      case 40: if (!fkeypress)
               {
                  /*
                   *  M+
                   */
                  r += stack[stackptr].x;
                  opentered = 0;
                  inpmode = 0;
               }
               else
               {
                  /*
                   *  CM
                   */
                  r = 0.0;
               }
               break;

      case 50: /*
                *  RM
                */
               if (!inpmode)
               {
                  if (opentered)
                  {
                     incstackptr();
                     opentered = 0;
                  }
               }
               stack[stackptr].x = r;
               opentered = 0;
               inpmode = 0;
               break;

      case 30: /*
                *  EXP
                */

               if (strchr(inpbuf, 'e') || inpbufptr >= DISPCHARS)
                  DisplayBeep(0L);
               else
               {
                  if (!inpmode)
                  {
                     inpmode = 1;
                     if (opentered)
                     {
                        incstackptr();
                        opentered = 0;
                     }
                     inpbufptr = 1;
                     inpbuf[0] = '1';   /* 1, falls noch keine Mantisse da  */
                  }
                  inpbuf[inpbufptr] = 'e';
                  inpbufptr++;
                  inpbuf[inpbufptr] = '\0';
               }
               break;
   }

   /*
    *  x neu ausrechnen
    */

   if (inpmode)
      stack[stackptr].x = inpnum();

   if (stack[stackptr].x > HUGE_VAL)
   {
      error("Überlauf");
      stack[stackptr].x = 0.0;
      inpmode = 0;
   }

   /*
    *  wenn f gedrückt war, f-Taste wieder "lösen"
    */

   if (fkeypress && key != 0)
   {
      fkeypress = 0;
      updatefkey();                     /* geänderten Zustand auch zeigen */
   }

   /*
    *  Anzeige
    */

   if (inpmode)
   {
      /*
       *  während inpmode TRUE ist, wird der inpbuf linksbündig angezeigt
       */
      sprintf(dispbuf, "%-*s", DISPCHARS, inpbuf);
      display();
   }
   else
   {
      /*
       *  sonst x im eingestellten Format anzeigen
       */

      dispnum();
   }

#ifdef DEBUG
   {
      register int i;
      for (i = 0; i <= stackptr; i++)
      {
         printf("%2d: %10g %2x\n", i, stack[i].x, stack[i].op);
      }
      printf("inpmode=%d, opentered=%d, inpptr=%d\n\n",inpmode, opentered,
         inpbufptr);
   }
#endif

   return(0);
}


/*
 *  inpnum() gibt die Zahl als float zurück, die sich durch Interpretation
 *  des inpbuf im eingestellten Zahlensystem ergibt.
 */

double inpnum()
{
   int a;
   double rc;
   register char *adr;
   double atof();

   switch(zahlsys)
   {
      case SYSDEZ:
         rc = atof(inpbuf);
         break;
      case SYSHEX:
         sscanf(inpbuf, "%x", &a);
         rc = (double) a;
         break;
      case SYSOKT:
         sscanf(inpbuf, "%o", &a);
         rc = (double) a;
         break;
      case SYSBIN:
         adr = inpbuf;
         a = 0;
         do
         {
            a *= 2;
            if (*adr == '1')
               a++;
            adr++;
         }
         while(*adr == '1' || *adr == '0');
         rc = (double) a;
         break;
   }
   return(rc);
}

/*
 *  workstack() akzeptiert zunächst ein Rechenzeichen als aktuellen
 *  Operator und arbeitet am Stack herum: Ist die Priorität des
 *  Rechenzeichens auf dem Stack größer oder gleich der gerade eingegebenen,
 *  so wird die Rechnung vom Stack erst gemacht, sonst aufhören. Auch
 *  aufhören, wenn da eine Klammer ist, diese BRAC wird durch ein neu
 *  einzugebenes Rechenzeichen ersetzt (deshalb wird oben bei ")" opentered
 *  = FALSE gesetzt). END macht alle Klammern zu, hat deshalb niedrigste
 *  Priorität.
 */

workstack(op)
int op;
{
   register int flag = 0;               /* Abbruch nach Klammer */

   opentered = 1;

   stack[stackptr].op = op;

   while (stackptr > 0  && !flag &&
          PRI(stack[stackptr-1].op) >= PRI(stack[stackptr].op))
   {
      switch(stack[stackptr-1].op)
      {
         case AND:
            stack[stackptr].x = (float)
               ((int) stack[stackptr-1].x & (int) stack[stackptr].x);
         case OR:
            stack[stackptr].x = (float)
               ((int) stack[stackptr-1].x | (int) stack[stackptr].x);
         case XOR:
            stack[stackptr].x = (float)
               ((int) stack[stackptr-1].x ^ (int) stack[stackptr].x);
         case PLUS:
            stack[stackptr].x = stack[stackptr-1].x + stack[stackptr].x;
            break;
         case MIN:
            stack[stackptr].x = stack[stackptr-1].x - stack[stackptr].x;
            break;
         case MULT:
            stack[stackptr].x = stack[stackptr-1].x * stack[stackptr].x;
            break;
         case DIV:
            if (stack[stackptr].x == 0.0)
               error("/0");
            else
               stack[stackptr].x = stack[stackptr-1].x / stack[stackptr].x;
            break;
         case POT:
            errno = 0;
            stack[stackptr].x = pow(stack[stackptr-1].x,stack[stackptr].x);
            if (errno)
               error("Bereich");
            break;
         case BRAC:
            flag = 1;
            break;
      }
      stack[stackptr-1].op = stack[stackptr].op;
      stack[stackptr-1].x = stack[stackptr].x;
      stackptr--;
   }
   return(0);
}


/*
 *  incstackptr() erhöht den Stackpointer
 */

incstackptr()
{
   stackptr++;
   if (stackptr >= STACKDEPTH)
   {
      error("Stacküberlauf");
      stackptr = 0;
   }
   return(0);
}


/*
 *  Umrechnungen für die verschiedenen Winkelmodi
 */

double rad2q(x)
register double x;
{
   switch(wink)
   {
      case WINKRAD: break;
      case WINKDEG: x = x / PI * 180.0; break;
      case WINKGRD: x = x / PI * 200.0; break;
   }
   return(x);
}

double q2rad(x)
register double x;
{
   switch(wink)
   {
      case WINKRAD: break;
      case WINKDEG: x = x / 180.0 * PI; break;
      case WINKGRD: x = x / 200.0 * PI; break;
   }
   return(x);
}


/*
 *  addinp(c) schaltet, falls noch nicht geschehen, den inpmode ein und
 *  fügt das Zeichen c an den inpbuf an
 */

addinp(c)
register char c;
{
    /*
     *  einige Kontrollen
     */

    if (inpmode &&
        (zahlsys == SYSHEX && inpbufptr >= 4  ||
         zahlsys == SYSOKT && inpbufptr >= 8  ||
         zahlsys == SYSBIN && inpbufptr >= 16 ||
         inpbufptr >= DISPCHARS)
       )
    {
       /*
        *  falsche Eingabe
        */
       DisplayBeep(0L);      /* Bildschirm aufflackern lassen */
    }
    else
    {
       if (!inpmode)         /* Sind wir schon im inpmode? */
       {
          inpmode = 1;
          inpbufptr = 0;
          inpbuf[0] = '\0';
          if (opentered)
          {
            /*
             *  wenn bereits ein Operator eingegeben wurde,
             *  Stack shiften, sonst Zahl überschreiben
             */
            incstackptr();
            opentered = 0;
          }
       }

       inpbuf[inpbufptr] = c;
       inpbufptr++;
       inpbuf[inpbufptr] = '\0';
   }
   return(0);
}
