// © Copyright1995, Joseph Bergin.  All rights reserved. 

#ifndef __SpreadSheet__
#define __SpreadSheet__

#include <iostream.h>
#include "TwoDimArray.h"
#include "Array.h"
#include "Association.h"
#include "Strngs.h"
#include "Boolean.h"

class SpreadSheet;
class SpreadSheetCell;

struct semanticRecord	// These are stored as operands in cell programs.  
{	int _kind;  // cells or numbers (or an empty flag.) 
	int _value; // only for numbers 
	SpreadSheetCell * _which; // only for cells
	int evaluate();
};

class SpreadSheetCell
{public:
	int evaluate(); // update this cell (and cells it depends on if necessary).
	String name();
	SpreadSheetCell();
private:
	void mark();
	void parse(); // parses rhs of an assignment command.
	void extendProgram(unsigned int);
	void parseexpression();
	void parseterm();
	void parsemof();
	void parseaot();
	void parsefactor();
	Boolean _hasValue;  // ifFalse: hasProgram
	Boolean _isEvaluated;
	Array< Association<int, semanticRecord> > _program;
		// Array of <command, operand> pairs.  
	int _programMax; // Current length of the _program array
	int _programLength; // Current number of instructions stored
	int _value;
	SpreadSheet* _sheet;  // copyconstructor and overloaded = need to set this. 
	unsigned int _row, _col; // Where this cell is in the Sheet.

friend class SpreadSheet;
//friend class TwoDimensionArray<SpreadSheetCell>; //Borland needs this
};

class SpreadSheet
{public:
	SpreadSheet(unsigned int columns = 26, unsigned int rows =24); //up to 26 COLUMNS only.
	void draw();// show evaluated values
	void dump();// show cell contents (values or programs).
	void evaluate();	// Update all the cells.
	Boolean parse();  // parses lhs of a command; // return false at end
	Boolean legalCell(unsigned int col, unsigned int row); // Are (row,col) legal?
private:
	TwoDimensionArray <SpreadSheetCell> _cells;
	unsigned int _rows;
	unsigned int _columns;
friend class SpreadSheetCell;
};

void shMain(SpreadSheet&); // The driver for the spreadsheet program. 

#endif

/*	The statements that may be entered are as follows:
<statement>		::= <assign> ";" | <command>
<assign>		::= "letter" "=" <expression>
<expression>	::= <term> <plusopterm>
<plusopterm>	::= "" | ("+" | "-") <term> <plusopterm>
<term>			::= <factor> <mulopfactor>
<mulopfactor>	::= "" | ("*" | "/") <factor> <mulopfactor>
<factor>		::= "number" | "letter" | "(" <expression? ")" 
					| "sum" "(" <range> ")" | "prod" "(" <range> ")" 
<range>			::=	"letter" ".." "letter"
<command>		::= "eval" | "print" | "dump" | "done" 

"letter"	is a single letter followed by an integer (both in legal range): e.g. a4.
"number"	is an unsigned integer.  To get -5 use 0-5.  There is no unary minus. Sorry.
*/


// example commands
//  A6 = 5;			// The letter names the column and the integer the row. 
//	B8 = A6 + 1;	// also elaborate expressions with operators +-*/ and parens.
//  A9 = sum(B1..C4);	// also prod(range)  where range == cell..cell
//  eval		// Evaluate the sheet
//  dump		// Show the values/programs stored in each nontrivial (0) cell. 
//  print		// Print the current values. (Usually done after "eval").  
//  done		// Terminate the run. 

/* Exercises:  (In approximate increasing order of difficulty )
	Add a unary minus.  Use ~ for this operator, not -.  ~5 means minus 5. 
	Add the modulo operator to the language. Use % for this operator.  12 % 5 should
		 yield 2. 
	Give the program a prettier interface. 
	Add an additional range function. Choose a good one.  
	Add an exponentiation operator. Use ^ for this operator.  2^5 should yield 32.
		The precidence level of ^ should be higher than that of * and /, but lower than 
		parens. Note that if you permit 2^5^3, that it should group from the right 
		as 2^(5^3).  The included operators all group from the left within precidence 
		levels. 	
	Add a logic operator similar to the C++ conditional expression operator ?:.  To the 
		left of the ? you need a comparison like a4<3.  To the right you need two 
		expressions separated by the colon.  
	What improvements can you make that will increase the total code size by no more than 
		one page?
*/
