/* -----------------------------------------------------------
  $VER: calc.math.c 1.02 (29.02.2000)

  maths core for calculator project

  (C) Copyright 2000 Matthew J Fletcher - All Rights Reserved.
  amimjf@connectfree.co.uk - www.amimjf.connectfree.co.uk
  ------------------------------------------------------------ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <dos/dos.h>
#include <exec/types.h>
#include <exec/exec.h>
#include <libraries/gadtools.h>
#include <graphics/gfxmacros.h>
#include <graphics/gfxbase.h>

#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>

#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>

#include "Calc.h"

#define MAX 255 /* max char precision */

/* buffers for maths core preserved betwen calls */
long double result=0; /* final total */
char number1[MAX];    /* first number */
char op;              /* math op */
char number2[MAX];    /* second number */
int i=0,y=0;
int flag=0,sflag=0;  /* control flags */

/* tape history */
extern int UseTape;
/* maths mode */
extern int mode;
/* whats on screen */
extern char buffer[100];

double do_math(char inchar)
{ /* maths core for calculator */
char *t1,*t2;
static long double realnumber1,realnumber2,realresult;

/* if previous result exists & op is first gad pressed */
if ( ((inchar == '+' ) || (inchar == '-' ) || (inchar == '/' ) ||
     (inchar == '*' )) && (result != 0)
   )
   /* second or more calc */
   {
    op = inchar; /* store */
    // printf("operation (%c)\n",op);
    draw_op(op);
    flag =1;  /* now only fill second number */
    sflag =1; /* calc, uses stored number as number1 */

    /* copy previous result to number1 store */
    realnumber1 = result;
   }

/* if input is number and op is blank store */
if ( ((isdigit(inchar) != 0) || (inchar =='.')) && (flag ==0))
   /* make sure we are first number */
   {
    /* first distroy, old result, cos user did NOT, want */
    /* to perform an operation on it (they started to type */
    /* a new number !!) */
    result=0;

    number1[i] = inchar; /* add next input */
    i++;
    // printf("1stored (%c), number1 (%s)\n",inchar, number1);
    clear_display();
    draw_display(number1,NULL);
   }

/* if input is number and op is NOT blank store */
if (((isdigit(inchar) != 0) || (inchar =='.')) && (flag ==1))
   /* make sure we are second number */
   {
    number2[y] = inchar; /* add next input */
    y++;
    // printf("2stored (%c), number2 (%s)\n",inchar, number2);
    clear_display();
    draw_display(number2,NULL);
   }


/* if we get a operations store */
if ( (inchar == '+' ) || (inchar == '-' ) || (inchar == '/' ) ||
     (inchar == '*' ) || (inchar == '(' ) || (inchar == ')' ) ||
     (inchar == '<' ) && (sflag == 0) /* skip if op allready parsed */
   ) /* its an operation */

   {
    op = inchar; /* store */
    // printf("operation (%c)\n",op);
    clear_display();
    draw_op(op);
    draw_display(NULL,NULL); /* a refresh basicly */
    flag =1; /* now only fill second number */
   }


/* if = pressed */
if (inchar == '=')
    {
    /* we work out result imedieatly */

    /* convert to numbers */
    if (sflag ==0) realnumber1 = strtod(number1, &t1);
    /* or not if one is available */
    if (sflag ==1) realnumber1 = result;

    realnumber2 = strtod(number2, &t2);
    /* second number always input */

    if (UseTape ==1)
        printf("%Lg %c %Lg\n ",realnumber1,op,realnumber2);

    switch (op)
    {
    case '+':
    realresult = (realnumber1 + realnumber2);
    break;

    case '-':
    realresult = (realnumber1 - realnumber2);
    break;

    case '/':
    realresult = (realnumber1 / realnumber2);
    break;

    case '*':
    realresult = (realnumber1 * realnumber2);
    break;

    default:
    printf("Something broke, couldent get operand ! got (%c)\n",op);
    break;
    };

    /* copy to store */
    result = realresult;

    /* output conversion depends on mode selected */
    /* ALL maths done internally as decimal */

    switch (mode)
    {
    case 0: /* DEC */
    /* copy data for display */
    sprintf(buffer, "%Ld",realresult);
    /* tape display */
    if (UseTape ==1)
        printf("=%s %Ld%s\n",BOLD,realresult,NORMAL);
    break;
    case 1: /* FLT */
    sprintf(buffer, "%Lg",realresult);
    if (UseTape ==1)
        printf("=%s %Lg%s\n",BOLD,realresult,NORMAL);
    break;
    case 2: /* HEX */
    sprintf(buffer, "%Lx",realresult);
    if (UseTape ==1)
        printf("=%s %Lx%s\n",BOLD,realresult,NORMAL);
    break;
    case 3: /* OCT */
    sprintf(buffer, "%Lo",realresult);
    if (UseTape ==1)
        printf("=%s %Lo%s\n",BOLD,realresult,NORMAL);
    break;
    case 4: /* BIN */
    sprintf(buffer, "%Ld",realresult);
    /* display return from atob (int) */
    if (UseTape ==1)
        printf("=%s %Ld%s\n",BOLD,atob(buffer),NORMAL);
    break;
    case 5: /* EXPO */
    sprintf(buffer, "%Le",realresult);
    if (UseTape ==1)
        printf("=%s %.Le%s\n",BOLD,realresult,NORMAL);
    break;
    };

    /* check for overload */
    if (stricmp(buffer,"NaN")==0)
        strcpy(buffer,"Overload !!");

    /* clean our mess */
    clear_buffers();
    clear_display();
    draw_display(buffer,NULL);
    /* we are safe to call again now */

    } /* end calculation */


} /* end do_math */


int draw_display(char *inchar, int inmode)
{ /* re-draw display */
static int smode;

    /* store untill, user changes */
    if (inmode != NULL)
    smode = inmode;
    mode = inmode; /* global */

    /* NULL for no change */

    /* print maths mode */
    switch (smode)
    {
    case 0: /* DEC */
    GT_SetGadgetAttrs(CalcGadgets[25], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"DEC", TAG_END);
    break;
    case 1: /* FLT */
    GT_SetGadgetAttrs(CalcGadgets[25], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"FLT", TAG_END);
    break;
    case 2: /* HEX */
    GT_SetGadgetAttrs(CalcGadgets[25], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"HEX", TAG_END);
    break;
    case 3: /* OCT */
    GT_SetGadgetAttrs(CalcGadgets[25], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"OCT", TAG_END);
    break;
    case 4: /* BIN */
    GT_SetGadgetAttrs(CalcGadgets[25], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"BIN", TAG_END);
    break;
    case 5: /* EXPO */
    GT_SetGadgetAttrs(CalcGadgets[25], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"EXPO", TAG_END);
    break;
    };

    /* print display */
    if (inchar != NULL)
    GT_SetGadgetAttrs(CalcGadgets[24], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)inchar, TAG_END);

    /* NULL passed, if no change required */
    /* note we dont buffer the display buffer!! */
    /* so it needs to be re-passed, if you want */
    /* a display and/or an update */

}  /* end draw_display */


void clear_display(void)
{
/* just draw a filed rectangle over display */
/* re-calc each time for font size change */
BYTE BorderLeft = CalcWnd->BorderLeft;
BYTE BorderTop = CalcWnd->BorderTop;
BYTE BorderRight = CalcWnd->BorderRight;
BYTE BorderBottom = CalcWnd->BorderBottom;

    //SetAPen(CalcWnd->RPort,4); /* dark grey */
    //RectFill(CalcWnd->RPort,11,13,210,27);

    GT_SetGadgetAttrs(CalcGadgets[24], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)" ", TAG_END);
    GT_SetGadgetAttrs(CalcGadgets[25], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)" ", TAG_END);

} /* end clear display */


void clear_buffers(void)
{
int x=0;
/* if erase selected or CA, we flush all internal */
/* data stores, so we can start again */

    i=0,y=0;
    flag=0;  /* pointers */
    sflag=0;

    for (x=0; x != MAX; x++) /* stores */
        { number1[x] =0;
          number2[x] =0;
        }
    /* we are safe to call again now */

} /* end clear buffers */


void draw_op(char op)
{ /* place operation on display */

    switch (op)
    { /* print op type */
    case '+':
    GT_SetGadgetAttrs(CalcGadgets[26], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"+", TAG_END);
    break;
    case '-':
    GT_SetGadgetAttrs(CalcGadgets[26], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"-", TAG_END);
    break;
    case '/':
    GT_SetGadgetAttrs(CalcGadgets[26], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"/", TAG_END);
    break;
    case '*':
    GT_SetGadgetAttrs(CalcGadgets[26], CalcWnd, NULL,
                      GTTX_Text, (STRPTR)"*", TAG_END);
    break;
    };

} /* end print op */


int atob(char *str)
/* ASCII-to-binary conversion */
{
int val;

    for (val = 0; *str; str++)
    {
    val *= 2;

    if (*str == '1')
        val++;
        else if (*str != '0')
        return(-1);
    }
    return(val);
}
