/* Turbo C - (C) Copyright 1987,1988 by Borland International */

#include <math.h>
#include <alloc.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <mem.h>
#include <stdio.h>
#include <conio.h>
#include "mcalc.h"

int alloctext(int col, int row, char *s)
/* Allocates space for a text cell */
{
 int size;
 CELLPTR cellptr;

 if (memleft < (size = textcellsize(s)))
  return(FALSE);
 memleft -= size;
 cellptr = (CELLPTR)(malloc(strlen(s) + 2));
 cellptr->attrib = TEXT;
 strcpy(cellptr->v.text, s);
 cell[col][row] = cellptr;
 return(TRUE);
} /* alloctext */

int allocvalue(int col, int row, double amt)
/* Allocates space for a value cell */
{
 CELLPTR cellptr;

 if (memleft < valuecellsize)
  return(FALSE);
 memleft -= valuecellsize;
 cellptr = (CELLPTR)(malloc(sizeof(double) + 1));
 cellptr->attrib = VALUE;
 cellptr->v.value = amt;
 cell[col][row] = cellptr;
 return(TRUE);
} /* allocvalue */

int allocformula(int col, int row, char *s, double amt)
/* Allocates space for a formula cell */
{
 int size;
 CELLPTR cellptr;

 if (memleft < (size = formulacellsize(s)))
  return(FALSE);
 memleft -= size;
 cellptr = (CELLPTR)(malloc(strlen(s) + sizeof(double) + 2));
 cellptr->attrib = FORMULA;
 strcpy(cellptr->v.f.formula, s);
 cellptr->v.f.fvalue = amt;
 cell[col][row] = cellptr;
 return(TRUE);
} /* allocformula */

void deletecell(int col, int row, int display)
/* Deletes a cell */
{
 CELLPTR cellptr = cell[col][row];

 if (cellptr == NULL)
  return;
 switch (cellptr->attrib)
 {
  case TEXT :
   memleft += textcellsize(cellptr->v.text);
   clearoflags(col + 1, row, display);
   break;
  case VALUE :
   memleft += valuecellsize;
   break;
  case FORMULA :
   memleft += formulacellsize(cellptr->v.f.formula);
   break;
 } /* switch */
 format[col][row] &= ~OVERWRITE;
 free(cell[col][row]);
 cell[col][row] = NULL;
 if (col == lastcol)
  setlastcol();
 if (row == lastrow)
  setlastrow();
 updateoflags(col, row, display);
 changed = TRUE;
} /* deletecell */

void printfreemem(void)
/* Prints the amount of free memory */
{
 writef(strlen(MSGMEMORY) + 2, 1, MEMORYCOLOR, 6, "%6ld", memleft);
} /* printfreemem */

int rowwidth(int row)
/* Returns the width in spaces of row */
{
 return((row == 0) ? 1 : (int)log10(row + 1) + 1);
} /* rowwidth */

int formulastart(char **input, int *col, int *row)
/* Returns TRUE if the string is the start of a formula, FALSE otherwise.
   Also returns the column and row of the formula.
*/
{
 int len, maxlen = rowwidth(MAXROWS);
 char *start, numstring[10];

 if (!isalpha(**input))
  return(FALSE);
 *col = *((*input)++) - 'A';
 if (isalpha(**input))
 {
  *col *= 26;
  *col += *((*input)++) - 'A' + 26;
 }
 if (*col >= MAXCOLS)
  return(FALSE);
 start = *input;
 for (len = 0; len < maxlen; len++)
 {
  if (!isdigit(*((*input)++)))
  {
   (*input)--;
   break;
  }
 }
 if (len == 0)
  return(FALSE);
 strncpy(numstring, start, len);
 numstring[len] = 0;
 *row = atoi(numstring) - 1;
 if ((*row >= MAXROWS) || (*row == -1))
  return(FALSE);
 return(TRUE);
} /* formulastart */

void errormsg(char *s)
/* Prints an error message at the bottom of the screen */
{
 printf("%c", 7);      /* Beeps the speaker */
 writef(1, 25, ERRORCOLOR, 79, "%s  %s", s, MSGKEYPRESS);
 gotoxy(strlen(s) + strlen(MSGKEYPRESS) + 3, 25);
 getkey();
 gotoxy(1, 25);
 writef(1, 25, WHITE, 79, "");
} /* errormsg */

void fixformula(int col, int row, int action, int place)
/* Modifies a formula when its column or row designations need to change. */
{
 char *colstart, *rowstart, s[6], newformula[MAXINPUT + 1],
      *curpos = newformula;
 int fcol, frow;
 CELLPTR cellptr = cell[col][row];
 double value;

 strcpy(newformula, cellptr->v.f.formula);
 while (*curpos != 0)
 {
  if (formulastart(&curpos, &fcol, &frow))
  {
   rowstart = curpos - rowwidth(frow);
   colstart = rowstart - ((fcol > 25) ? 2 : 1);
   switch (action)
   {
    case COLADD :
     if (fcol < place)
      break;
     if (fcol == 25)
     {
      if (strlen(newformula) == MAXINPUT)
      {
       deletecell(col, row, NOUPDATE);
       alloctext(col, row, newformula);
       return;
      }
      movmem(colstart, colstart + 1, strlen(colstart) + 1);
     }
     colstring(fcol + 1, s);
     movmem(s, colstart, strlen(s));
     break;
    case ROWADD :
     if (frow < place)
      break;
     if (rowwidth(frow + 1) != rowwidth(frow))
     {
      if (strlen(newformula) == MAXINPUT)
      {
       deletecell(col, row, NOUPDATE);
       alloctext(col, row, newformula);
       return;
      }
      movmem(rowstart, rowstart + 1, strlen(rowstart) + 1);
     }
     sprintf(s, "%d", frow + 2);
     movmem(s, rowstart, strlen(s));
     break;
    case COLDEL :
     if (fcol <= place)
      break;
     if (fcol == 26)
      movmem(colstart + 1, colstart, strlen(colstart) + 1);
     colstring(fcol - 1, s);
     movmem(s, colstart, strlen(s));
     break;
    case ROWDEL :
     if (frow <= place)
      break;
     if (rowwidth(frow) != rowwidth(frow - 1))
      movmem(rowstart + 1, rowstart, strlen(rowstart) + 1);
     sprintf(s, "%d", frow);
     movmem(s, rowstart, strlen(s));
     break;
   } /* switch */
  }
  else
   curpos++;
 }
 if (strlen(newformula) != strlen(cellptr->v.f.formula))
 {
  value = cellptr->v.f.fvalue;
  deletecell(col, row, NOUPDATE);
  allocformula(col, row, newformula, value);
 }
 else
  strcpy(cellptr->v.f.formula, newformula);
} /* fixformula */

void colstring(int col, char *colstr)
/* Changes a column number to a string */
{
 setmem(colstr, 3, 0);
 if (col < 26)
  colstr[0] = col + 'A';
 else
 {
  colstr[0] = (col / 26) - 1 + 'A';
  colstr[1] = (col % 26) + 'A';
 }
} /* colstring */

void centercolstring(int col, char *colstr)
/* Changes a column to a centered string */
{
 char s[3];
 int spaces1, spaces2;

 colstring(col, s);
 spaces1 = (colwidth[col] - strlen(s)) >> 1;
 spaces2 = colwidth[col] - strlen(s) - spaces1;
 sprintf(colstr, "%*s%s%*s", spaces1, "", s, spaces2, "");
} /* centercolstring */

void setleftcol(void)
/* Sets the value of leftcol based on the value of rightcol */
{
 int total = 80, col = 0;

 while ((total >= LEFTMARGIN) && (rightcol - col >= 0))
 {
  colstart[SCREENCOLS - col - 1] = total - colwidth[rightcol - col];
  total -= colwidth[rightcol - col++];
 }
 if (total >= LEFTMARGIN)
  col++;
 movmem(&colstart[SCREENCOLS - col + 1], colstart, col - 1);
 leftcol = rightcol - col + 2;
 total = colstart[0] - LEFTMARGIN;
 if (total != 0)
 {
  for (col = leftcol; col <= rightcol; col++)
   colstart[col - leftcol] -= total;
 }
 printcol();
} /* setleftcol */

void setrightcol(void)
/* Sets the value of rightcol based on the value of leftcol */
{
 int total = LEFTMARGIN, col = 0;

 do
 {
  colstart[col] = total;
  total += colwidth[leftcol + col++];
 }
 while ((total <= 80) && (leftcol + col <= MAXCOLS));
 rightcol = leftcol + col - 2;
 printcol();
} /* setrightcol */

void settoprow(void)
/* Figures out the value of toprow based on the value of bottomrow */
{
 if (bottomrow - SCREENROWS < -1)
  bottomrow = 19;
 toprow = bottomrow - 19;
 printrow();
} /* settoprow */

void setbottomrow(void)
/* Figures out the value of bottomrow based on the value of toprow */
{
 if (toprow + SCREENROWS > MAXROWS)
  toprow = MAXROWS - 20;
 bottomrow = toprow + 19;
 printrow();
} /* setbottomrow */

void setlastcol(void)
/* Sets the value of lastcol based on the current value */
{
 register int row, col;

 for (col = lastcol; col >= 0; col--)
 {
  for (row = 0; row <= lastrow; row++)
  {
   if (cell[col][row] != NULL)
   {
    lastcol = col;
    return;
   }
  }
 }
 lastcol = 0;
} /* setlastcol */

void setlastrow(void)
/* Sets the value of lastrow based on the current value */
{
 register int row, col;

 for (row = lastrow; row >= 0; row--)
 {
  for (col = 0; col <= lastcol; col++)
  {
   if (cell[col][row] != NULL)
   {
    lastrow = row;
    return;
   }
  }
 }
 lastrow = 0;
} /* setlastrow */

void act(char *s)
/* Acts on a particular input */
{
 int attrib, allocated;
 double value;

 deletecell(curcol, currow, UPDATE);
 value = parse(s, &attrib);
 switch(attrib)
 {
  case TEXT :
   allocated = alloctext(curcol, currow, s);
   if (allocated)
    displaycell(curcol, currow, NOHIGHLIGHT, NOUPDATE);
   break;
  case VALUE :
   allocated = allocvalue(curcol, currow, value);
   break;
  case FORMULA :
   allocated = allocformula(curcol, currow, s, value);
   break;
 } /* switch */
 if (allocated)
 {
  format[curcol][currow] &= ~OVERWRITE;
  clearoflags(curcol + 1, currow, UPDATE);
  if (attrib == TEXT)
    setoflags(curcol, currow, UPDATE);
  if (curcol > lastcol)
   lastcol = curcol;
  if (currow > lastrow)
   lastrow = currow;
  if (autocalc)
   recalc();
 }
 else
  errormsg(MSGLOMEM);
 printfreemem();
} /* act */

int setoflags(int col, int row, int display)
/* Sets the overwrite flag on cells starting at (col + 1, row) - returns
   the number of the column after the last column set.
*/
{
 int len;

 len = strlen(cell[col][row]->v.text) - colwidth[col];
 while ((++col < MAXCOLS) && (len > 0) && (cell[col][row] == NULL))
 {
  format[col][row] |= OVERWRITE;
  len -= colwidth[col];
  if (display && (col >= leftcol) && (col <= rightcol))
   displaycell(col, row, NOHIGHLIGHT, NOUPDATE);
 }
 return(col);
} /* setoflags */

void clearoflags(int col, int row, int display)
/* Clears the overwrite flag on cells starting at (col, row) */
{
 while ((format[col][row] >= OVERWRITE) && (col < MAXCOLS) &&
        (cell[col][row] == NULL))
 {
  format[col][row] &= ~OVERWRITE;
  if (display && (col >= leftcol) && (col <= rightcol))
   displaycell(col, row, NOHIGHLIGHT, NOUPDATE);
  col++;
 }
} /* clearoflags */

void updateoflags(int col, int row, int display)
/* Starting in col, moves back to the last TEXT cell and updates all flags */
{
 while ((cell[col][row] == NULL) && (col-- > 0));
 if ((cell[col][row] != NULL) && (cell[col][row]->attrib == TEXT) && 
     (col >= 0))
  setoflags(col, row, display);
} /* updateoflags */

void textstring(char *instring, char *outstring, int col, int fvalue,
                int formatting)
/* Sets the string representation of text */
{
 char *just, *ljust = "%-*s", *rjust = "%*s";

 if ((fvalue & RJUSTIFY) && (formatting))
  just = rjust;
 else
  just = ljust;
 sprintf(outstring, just, colwidth[col], instring);
 if (formatting)
  outstring[colwidth[col]] = 0;
} /* textstring */

void valuestring(CELLPTR cellptr, double value, char *vstring, int col,
                 int fvalue, int *color, int formatting)
/* Sets the string representation of a value */
{
 char s[81];
 char *fstring;
 int width, pos;

 if (value == HUGE_VAL)
 {
  strcpy(vstring, MSGERROR);
  *color = ERRORCOLOR;
 }
 else
 {
  if (formatting)
  {
   sprintf(vstring, "%1.*f", fvalue & 15, cellptr->v.value);
   if (fvalue & COMMAS)
   {
    pos = strcspn(vstring, ".");
    while (pos > 3)
    {
     pos -= 3;
     if (vstring[pos - 1] != '-')
     {
      movmem(&vstring[pos], &vstring[pos + 1], strlen(vstring) - pos + 1);
      vstring[pos] = ',';
     }
    }
   }
   if (fvalue & DOLLAR)
   {
    if (vstring[0] == '-')
    {
     fstring = " $";
     width = colwidth[col] - 2;
    }
    else
    {
     fstring = " $ ";
     width = colwidth[col] - 3;
    }
   }
   else
   {
    fstring = "";
    width = colwidth[col];
   }
   strcpy(s, vstring);
   if (fvalue & RJUSTIFY)
   {
    if (strlen(vstring) > width)
     vstring[width] = 0;
    else
     sprintf(vstring, "%*s", width, s);
   }
   else
    sprintf(vstring, "%-*s", width, s);
   movmem(vstring, &vstring[strlen(fstring)], strlen(vstring) + 1);
   strncpy(vstring, fstring, strlen(fstring));
  }
  else
   sprintf(vstring, "%.*f", MAXPLACES, value);
  *color = VALUECOLOR;
 }
} /* valuestring */

char *cellstring(int col, int row, int *color, int formatting)
/* Creates an output string for the data in the cell in (col, row), and
   also returns the color of the cell */
{
 CELLPTR cellptr = cell[col][row];
 int newcol, formatvalue;
 static char s[81], temp[MAXCOLWIDTH + 1];
 char *p;
 double value;

 if (cellptr == NULL)
 {
  if (!formatting || (format[col][row] < OVERWRITE))
  {
   sprintf(s, "%*s", colwidth[col], "");
   *color = BLANKCOLOR;
  }
  else
  {
   newcol = col;
   while (cell[--newcol][row] == NULL);
   p = cell[newcol][row]->v.text;
   while (newcol < col)
    p += colwidth[newcol++];
   strncpy(temp, p, colwidth[col]);
   temp[colwidth[col]] = 0;
   sprintf(s, "%s%*s", temp, colwidth[col] - strlen(temp), "");
   *color = TEXTCOLOR;
  }
 }
 else
 {
  formatvalue = format[col][row];
  switch (cellptr->attrib)
  {
   case TEXT :
    textstring(cellptr->v.text, s, col, formatvalue, formatting);
    *color = TEXTCOLOR;
    break;
   case FORMULA :
    if (formdisplay)
    {
     textstring(cellptr->v.f.formula, s, col, formatvalue, formatting);
     *color = FORMULACOLOR;
     break;
    }
    else
     value = cellptr->v.f.fvalue;
   case VALUE :
    if (cellptr->attrib == VALUE)
     value = cellptr->v.value;
    valuestring(cellptr, value, s, col, formatvalue, color,
                formatting);
    break;
  } /* switch */
 }
 return(s);
} /* cellstring */

void writeprompt(char *prompt)
/* Prints a prompt on the screen */
{
 writef(1, 24, PROMPTCOLOR, 80, prompt);
} /* writeprompt */

void swap(int *val1, int *val2)
/* Swaps the first and second values */
{
 int temp;

 temp = *val1;
 *val1 = *val2;
 *val2 = temp;
} /* swap */

void checkforsave(void)
/* If the spreadsheet has been changed, will ask the user if they want to
   save it.
*/
{
 int save;

 if (changed && getyesno(&save, MSGSAVESHEET) && (save == 'Y'))
  savesheet();
} /* checkforsave */

void initvars(void)
/* Initializes various global variables */
{
 leftcol = toprow = curcol = currow = lastcol = lastrow = 0;
 setmem(colwidth, sizeof(colwidth), DEFAULTWIDTH);
 setmem(cell, sizeof(cell), 0);
 setmem(format, sizeof(format), DEFAULTFORMAT);
} /* initvars */

int getcommand(char *msgstr, char *comstr)
/* Reads in a command and acts on it */
{
 int ch, counter, len = strlen(msgstr);

 scroll(UP, 0, 1, 24, 80, 24, WHITE);
 for (counter = 0; counter < len; counter++)
 {
  if (isupper(msgstr[counter]))
   writef(counter + 1, 24, COMMANDCOLOR, 1, "%c", msgstr[counter]);
  else
   writef(counter + 1, 24, LOWCOMMANDCOLOR, 1, "%c", msgstr[counter]);
 }
 do
  ch = toupper(getkey());
 while ((strchr(comstr, ch) == NULL) && (ch != ESC));
 clearinput();
 return((ch == ESC) ? -1 : strlen(comstr) - strlen(strchr(comstr, ch)));
} /* getcommand */


