/*	SC	A Spreadsheet Calculator
 *		Main driver
 *
 *		James Gosling, September 1982
 *
 */



#include <curses.h>
#include "sc.h"

int linelim = -1;

error (fmt,a,b,c,d,e) {
    move (1,0);
    clrtoeol ();
    printw (fmt,a,b,c,d,e);
}

int seenerr;

yyerror (err)
char *err; {
    if (seenerr) return;
    seenerr++;
    move (1,0);
    clrtoeol ();
    printw ("%s: %.*s<=%s",err,linelim,line,line+linelim);
}

struct ent *lookat(row,col){
    register struct ent **p = &tbl[row][col];
    if (*p==0) {
	*p = (struct ent *) malloc (sizeof (struct ent));
	if (row>maxrow) maxrow = row;
	if (col>maxcol) maxcol = col;
	(*p)->label = 0;
	(*p)->flags = 0;
	(*p)->row = row;
	(*p)->col = col;
	(*p)->expr = 0;
    }
    return *p;
}

update () {
    register    row,
                col;
    register struct ent **p;
    static  lastmx,
            lastmy;
    int     maxcol;
    int     rows = LINES - 2;
    int     cols;
    if (curcol < stcol)
	stcol = curcol, FullUpdate++;
    if (currow < strow)
	strow = currow, FullUpdate++;
    while (1) {
	register    i;
	for (i = stcol, cols = 0, col = 0; col < COLS - 4 && i < MAXCOLS; i++)
	    col += fwidth[i], cols++;
	if (curcol >= stcol + cols)
	    stcol++, FullUpdate++;
	else
	    break;
    }
    if (currow >= strow + rows)
	strow = currow - rows + 1, FullUpdate++;
    if (FullUpdate) {
	move (2, 0);
	clrtobot ();
    }
    maxcol = stcol + cols - 1;
    for (row = strow + rows - 1; row >= strow; row--) {
	register    c = 0;
	for (p = &tbl[row][col = stcol]; col <= maxcol; col++, p++) {
	    if (*p && ((*p) -> flags & is_changed || FullUpdate)) {
		register    r = row - strow + 2;
		char   *s;
		move (r, c);
		(*p) -> flags &= ~is_changed;
		if ((*p) -> flags & is_valid)
		    printw ("%*.*f", fwidth[col], precision[col], (*p) -> v);
		if (s = (*p) -> label)
		    mvaddstr (r,
			    (*p) -> flags & is_leftflush
			    ? c : c - strlen (s) + fwidth[col],
			    s);
	    }
	    c += fwidth[col];
	}
    }
    mvaddstr (lastmy, lastmx, " ");
    lastmy = currow - strow + 2;
    lastmx = 0;
    for (col = stcol; col <= curcol;)
	lastmx += fwidth[col++];
    mvaddstr (lastmy, lastmx, "<");
    move (0, 0);
    clrtoeol ();
    if (linelim >= 0) {
	addstr (">> ");
	addstr (line);
    }
    else
	move (lastmy, lastmx);
    FullUpdate = 0;
}

#define ctl(c) ('c'&037)

main (argc, argv)
char  **argv; {
    int     running = 1;
    register char   c;
    int     edistate = -1;
    int     arg = 1;
    int     narg;
    int     nedistate = -1;
    {
	register    i;
	for (i = 0; i < MAXCOLS; i++) {
	    fwidth[i] = 10;
	    precision[i] = 2;
	}
    }
    initscr ();
    clear ();
    raw ();
    noecho ();
    error ("Welcome to the Spreadsheet Calculator, type '?' for help.");
    if (argc > 1)
	readfile (argv[1]);
    while (running) {
	nedistate = -1;
	narg = 1;
	if (edistate < 0 && linelim < 0 && (changed || FullUpdate))
	    EvalAll (), changed = 0;
	update ();
	refresh ();
	move (1, 0);
	clrtoeol ();
	fflush (stdout);
	seenerr = 0;
	if ((c = getchar ()) < ' ')
	    switch (c) {
		default: 
		    error ("No such command  (^%c)", c + 0100);
		    break;
		case ctl (b): 
		    while (--arg>=0) if (curcol)
			curcol--;
		    break;
		case ctl (c): 
		    running = 0;
		    break;
		case ctl (f): 
		    while (--arg>=0) if (curcol < MAXCOLS - 1)
			curcol++;
		    break;
		case ctl (g): 
		    linelim = -1;
		    break;
		case ctl (h): 
		    while (--arg>=0) if (linelim > 0)
			line[--linelim] = 0;
		    break;
		case ctl (j): 
		    if (currow >= MAXCOLS - 1 || maxcol >= MAXCOLS - 1) {
			error ("The table can't be any bigger");
			break;
		    }
		    currow++;
		    openrow (currow);
		    for (curcol = 0; curcol <= maxcol; curcol++) {
			register struct ent *p = tbl[currow - 1][curcol];
			if (p) {
			    register struct ent *n;
			    n = lookat (currow, curcol);
			    n -> v = p -> v;
			    n -> flags = p -> flags;
			    n -> expr = copye (p -> expr, 1, 0);
			    n -> label = 0;
			    if (p -> label) {
				n -> label = (char *) malloc (strlen (p -> label) + 1);
				strcpy (n -> label, p -> label);
			    }
			}
		    }
		    for (curcol = 0; curcol <= maxcol; curcol++) {
			register struct ent *p = tbl[currow][curcol];
			if (p && (p -> flags & is_valid) && !p -> expr)
			    break;
		    }
		    if (curcol > maxcol)
			curcol = 0;
		    break;
		case ctl (l): 
		    FullUpdate++;
		    break;
		case ctl (m): 
		    if (linelim < 0)
			line[linelim = 0] = 0;
		    else {
			linelim = 0;
			yyparse ();
			linelim = -1;
		    }
		    break;
		case ctl (n): 
		    while (--arg>=0) if (currow < MAXROWS - 1)
			currow++;
		    break;
		case ctl (p): 
		    while (--arg>=0) if (currow)
			currow--;
		    break;
		case ctl (q): 
		    break;	/* ignore flow control */
		case ctl (s): 
		    break;	/* ignore flow control */
		case ctl (u): 
		    narg = arg * 4;
		    nedistate = 1;
		    break;
		case ctl (v): 
		    if (linelim > 0) {
			sprintf (line + linelim, "r%dc%d", currow, curcol);
			linelim = strlen (line);
		    }
		    break;
	    }
	else
	    if ('0' <= c && c <= '9' && (linelim < 0 || edistate >= 0)) {
		if (edistate != 0)
		    arg = 0;
		nedistate = 0;
		narg = arg * 10 + (c - '0');
	    }
	    else
		if (linelim >= 0) {
		    line[linelim++] = c;
		    line[linelim] = 0;
		}
		else
		    switch (c) {
			case '.': 
			    nedistate = 1;
			    break;
			case '=': 
			    sprintf (line, "let r%dc%d = ", currow, curcol);
			    linelim = strlen (line);
			    break;
			case '?': 
			    help ();
			    break;
			case '"': 
			    sprintf (line, "label r%dc%d = \"", currow, curcol);
			    linelim = strlen (line);
			    break;
			case '<': 
			    sprintf (line, "leftstring r%dc%d = \"",
				    currow, curcol);
			    linelim = strlen (line);
			    break;
			case '>': 
			    sprintf (line, "rightstring r%dc%d = \"",
				    currow, curcol);
			    linelim = strlen (line);
			    break;
			case 'e': 
			    editv (currow, curcol);
			    break;
			case 'f': 
			    sprintf (line, "format [for column] %d [is] ",
					curcol);
			    linelim = strlen (line);
			    break;
			case 'p': 
			    sprintf (line, "put [database into] \"");
			    linelim = strlen (line);
			    break;
			case 'g': 
			    sprintf (line, "get [database from] \"");
			    linelim = strlen (line);
			    break;
			case 'w': 
			    sprintf (line, "write [listing to] \"");
			    linelim = strlen (line);
			    break;
			case 'r': 
			    while (--arg>=0) openrow (currow);
			    break;
			case 'd': 
			    while (--arg>=0) closerow (currow);
			    break;
			case 'c': 
			    while (--arg>=0) opencol (curcol);
			    break;
			case 'D': 
			    while (--arg>=0) closecol (curcol);
			    break;
		    }
	edistate = nedistate;
	arg = narg;
    }
    move (LINES - 1, 0);
    refresh ();
    noraw ();
    echo ();
    endwin ();
}

writefile (fname)
char *fname; {
    register FILE *f = fopen (fname, "w");
    register struct ent **p;
    register r, c;
    if (f==0) {
	error ("Can't create %s", fname);
	return;
    }
    fprintf (f, "# This data file was generated by the Spreadsheet Calculator.\
\n# You almost certainly shouldn't edit it.\n\n");
    for (c=0; c<MAXCOLS; c++)
	if (fwidth[c] != 10 || precision[c] != 2)
	    fprintf (f, "format %d %d %d\n",c,fwidth[c],precision[c]);
    for (r=0; r<=maxrow; r++) {
	p = &tbl[r][0];
	for (c=0; c<=maxcol; c++, p++)
	    if (*p) {
		if ((*p)->label)
		    fprintf (f, "%sstring r%dc%d = \"%s\"\n",
				(*p)->flags&is_leftflush ? "left" : "right",
				r,c,(*p)->label);
		if ((*p)->flags&is_valid) {
		    editv (r, c);
		    fprintf (f, "%s\n",line);
		}
	    }
    }
    fclose (f);
}

readfile (fname)
char *fname; {
    register FILE *f = fopen (fname, "r");
    if (f==0) {
	error ("Can't read %s", fname);
	return;
    }
    erasedb ();
    while (fgets(line,sizeof line,f)) {
	linelim = 0;
	if (line[0] != '#') yyparse ();
    }
    fclose (f);
    linelim = -1;
}

erasedb () {
    register r, c;
    for (c = 0; c<maxcol; c++) {
	fwidth[c] = 10;
	precision[c] = 2;
    }
    for (r = 0; r<maxrow; r++) {
	register struct ent **p = &tbl[r][0];
	for (c=maxcol; --c>=0; p++)
	    if (*p) {
		if ((*p)->expr) efree ((*p) -> expr);
		if ((*p)->label) free ((*p) -> label);
		free (*p);
		*p = 0;
	    }
    }
    maxrow = 0;
    maxcol = 0;
    FullUpdate++;
}

openrow (rs) {
    register    r;
    register struct ent **p;
    register    c;
    if (maxcol >= MAXCOLS - 1) {
	error ("The table can't be any bigger");
	return;
    }
    for (r = ++maxrow; r > rs; r--)
	for (c = maxcol + 1, p = &tbl[r][0]; --c >= 0; p++)
	    if (p[0] = p[-MAXCOLS])
		p[0] -> row++;
    p = &tbl[rs][0];
    for (c = maxcol + 1; --c >= 0;)
	*p++ = 0;
    FullUpdate++;
}

closerow (r)
register r; {
    register struct ent **p;
    register c;
    while (r<maxrow) {
	for (c = maxcol+1, p = &tbl[r][0]; --c>=0; p++)
	    if (p[0] = p[MAXCOLS])
		p[0]->row--;
	r++;
    }
    p = &tbl[maxrow][0];
    for (c=maxcol+1; --c>=0; ) *p++ = 0;
    maxrow--;
    FullUpdate++;
}

opencol (cs) {
    register r;
    register struct ent **p;
    register c;
    register lim = maxcol-cs+1;
    for (r=0; r<=maxrow; r++) {
	p = &tbl[r][maxcol+1];
	for (c=lim; --c>=0; p--)
	    if (p[0] = p[-1])
		p[0]->col++;
	p[0] = 0;
    }
    maxcol++;
    FullUpdate++;
}

closecol (cs) {
    register r;
    register struct ent **p;
    register c;
    register lim = maxcol-cs;
    for (r=0; r<=maxrow; r++) {
	p = &tbl[r][cs];
	for (c=lim; --c>=0; p++)
	    if (p[0] = p[1])
		p[0]->col++;
	p[0] = 0;
    }
    maxcol--;
    FullUpdate++;
}

