#include "SpreadSh.h"
#include "Stacks.h"
#include "Scanner.h"
#include <string.h>
#include <ctype.h>
#include <iomanip.h>

enum instructions { pushi, pushv, pop, sum, diff, prod, quot };
enum SemanticKind {num, cell, empty};

Stack<int> evaluationStack;
// ********************** token ************************************

int semanticRecord::evaluate()
{	if(_kind == num )
		return _value;
	else if (_kind == cell)
		return _which->evaluate();
	else 
		return 0;  // should never be executed actually. 
}

ostream& operator <<(ostream& os, semanticRecord& a)
{	if(a._kind == num)
		os << a._value<<' ';
	else if(a._kind == cell)
		os << a._which->name()<<' ';
	else
		os << ' ';
	return os;
}

Boolean legalCellSpelling(const char * s)
{	int i;
	if(!isalpha(s[0])) return false;
	if(strlen(s) < 2) return false;
	for(i = 1; i<strlen(s);++i)
		if(!isdigit(s[i]))
			return false;
	return true;
};

void extractCell(const char * s, int& column, int& row) // assumed to be legal
{	column = s[0] - 'a';
	row = atoi(s+1);
}

// ********************** SpreadSheetCell ************************************

SpreadSheetCell::SpreadSheetCell()
:	_hasValue(true),
	_isEvaluated(false),
	_program(10),
	_programMax(10),
	_programLength(0),
	_value(0),
	_sheet(NULL)
{
}

void SpreadSheetCell::extendProgram(unsigned int n)
{	_program.extendBy(n);
	_programMax += n;
}

String SpreadSheetCell::name()
{	char temp[2];
	temp[0] = char('a' + _col);
	temp[1] = '\0';
	return String(temp) + String(_row).strip();
}

void SpreadSheetCell::mark()
{	_isEvaluated = _hasValue;
}

int SpreadSheetCell::evaluate() // May be infinitely recursive. Not checked. 
{	int temp;
	if(!_isEvaluated)
	{
		for(int i = 0; i<_programLength; ++i)
		{	switch(_program[i].key())
			{	case pushi:
				case pushv:	evaluationStack.push(_program[i].value().evaluate());
					break;
				case pop: // never used here???
					break;
				case sum:
					temp = evaluationStack.pop();
					evaluationStack.push(evaluationStack.pop() + temp);
					break;
				case diff:
					temp = evaluationStack.pop();
					evaluationStack.push(evaluationStack.pop() - temp);
					break;
				case prod:
					temp = evaluationStack.pop();
					evaluationStack.push(evaluationStack.pop() * temp);
					break;
				case quot:
					temp = evaluationStack.pop();
					evaluationStack.push(evaluationStack.pop() / temp);
					break;
			}
		}
		_value = evaluationStack.pop();
	}
	_isEvaluated = true;
	return _value;
}

void SpreadSheetCell::parsefactor()
{	semanticRecord result;
	semanticRecord none;

	switch (nextToken()) 
	{	case lparen:
			match(lparen);
			parseexpression();
			match(rparen);
			break;
		
		case letter:
			result._kind = cell;
			int row, col;
			extractCell(tokenbuffer.which, col, row);
			if(!_sheet->legalCell(col,row))parseError("Illegal cell.");
			result._which = &(_sheet->_cells(col,row));
			toss();
			if(_programLength == _programMax)extendProgram(10);
			_program[_programLength++] = Association<int, semanticRecord>(pushv, result);
			_hasValue = false;
			break;
		
		case number:
			result._kind = num;
			result._value = tokenbuffer.value;
			toss();
			if(_programLength == _programMax) extendProgram(10);
			_program[_programLength++] = Association<int, semanticRecord>(pushi, result);
			_hasValue = true;	
			_value = result._value;
			break;
		  
		case sumfunc:
		case prodfunc:
			int op = sum;
			if(nextToken() == prodfunc)op = prod;
			toss();
			match(lparen);
			int lr,lc,rr,rc; // leftrow...
			if(nextToken() == letter && legalCellSpelling(tokenbuffer.which))
			{	extractCell(tokenbuffer.which, lc, lr);
				toss();
			}	
			else
				parseError("Illegal range");
			match(rangeop);
			if(nextToken() == letter && legalCellSpelling(tokenbuffer.which))
			{	extractCell(tokenbuffer.which, rc, rr);
				toss();
			}
			else
				parseError("Illegal range");
			match(rparen);
			int temp;
			if(lc > rc){temp = lc; lc = rc; rc = temp;}
			if(lr > rr){temp = lr; lr = rr; rr = temp;}
			if(!_sheet->legalCell(lc,lr))parseError("Illegal range");
			if(!_sheet->legalCell(rc,rr))parseError("Illegal range");
			none._kind = empty;
			result._kind = cell;
			result._which = &(_sheet->_cells(lc,lr));
			int cellcount = (rc - lc +1) * (rr - lr + 1);
			extendProgram(cellcount);
			_program[_programLength++] = Association<int, semanticRecord>(pushv, result);
			for(int i = lr; i<= rr; i++)
				for(int j = lc; j<=rc; j++)
				{	if(i!=lr || j != lc)
					{	result._which =  &(_sheet->_cells(j,i));
						_program[_programLength++] = Association<int, semanticRecord>(pushv, result);	  	
						_program[_programLength++] = Association<int, semanticRecord>(op, none);	
					}
				}
			break;
	}
}

void SpreadSheetCell::parsemof()
{	int op;
	if(nextToken() == timesop || nextToken() == divideop)
	{	 op = prod;
		if (nextToken() == divideop)
			op = quot;
		toss();
		parsefactor();
		if(_programLength == _programMax)extendProgram(10);
		semanticRecord none;
		none._kind = empty;	
		_program[_programLength++] = Association<int, semanticRecord>(op, none);	
		_hasValue = false;	
		parsemof();
	}
}


void SpreadSheetCell::parseterm()
{	parsefactor();
  	parsemof();
}


void SpreadSheetCell::parseaot()
{	int op;
	if(nextToken() == plusop || nextToken() == minusop)
	{	op = sum;
		if (nextToken() == minusop)
			op = diff;
		toss();
		parseterm();
		if(_programLength == _programMax) extendProgram(10);
		semanticRecord none;
		none._kind = empty;	
		_program[_programLength++] = Association<int, semanticRecord>(op, none);	
		_hasValue = false;	
		parseaot();
	}
}

void SpreadSheetCell::parseexpression()
{	parseterm();
	parseaot();
}

void SpreadSheetCell::parse()
{	_programLength = 0; // start with a new program
	_hasValue = false; // no value yet
	parseexpression(); 
	match(semicolon);
	// Ths parser is recursive descent, based on the grammar in spreadsh.h. 
}

// ********************** SpreadSheet ****************************************


SpreadSheet::SpreadSheet(unsigned int columns, unsigned int rows)
:	_cells(columns, rows),
	_rows(rows),
	_columns(columns)
{	if(columns > 26) exit(1);
	for(int i = 0; i< columns; ++i)
		for(int j = 0; j<rows; ++j)
		{	_cells(i,j)._sheet = this;
			_cells(i,j)._row = j;
			_cells(i,j)._col = i;
		}
}

/*
void SpreadSheet::draw()
{	for(int i = 0; i < _columns; ++i)
		for(int j = 0; j<_rows; ++j)
			if(_cells(i,j)._value != 0)
				cout << char('a'+i) <<j<<'='<<_cells(i,j)._value<<endl;
}
*/

void SpreadSheet::draw()
{	for(int i = 0; i<_columns; ++i)
	{	cout<<"  "<< char('A'+i) <<"       ";
	}
	cout<<endl;
	for(int j = 0; j<_rows; ++j)
	{	for(int i = 0; i < _columns; ++i)
		{	cout <<setw(10)<<_cells(i,j)._value;
		}
		cout<<endl;
	}
}

void SpreadSheet::dump()
{	for(int i = 0; i < _columns; ++i)
		for(int j = 0; j<_rows; ++j)
			if(_cells(i,j)._hasValue && _cells(i,j)._value != 0)
				cout << char('a'+i) <<j<<'='<<_cells(i,j)._value<<endl;
			else if(!_cells(i,j)._hasValue)
			{	cout << char('a'+i) <<j<<": ";
				for(int k=0; k<_cells(i,j)._programLength; k++)
					cout<<'<'<< _cells(i,j)._program[k].key()<<',' 
						<< _cells(i,j)._program[k].value()<<'>';
				cout<<endl;
			}
}

Boolean SpreadSheet::legalCell(unsigned int col, unsigned int row)
{	return Boolean(col<_columns && row < _rows);
}

void SpreadSheet::evaluate()
{	int i,j;
	for(i = 0; i<_columns; i++)
		for(j = 0; j<_rows; j++)
			_cells(i,j).mark();
	for(i = 0; i<_columns; i++)
		for(j = 0; j<_rows; j++)
			if(!_cells(i,j)._isEvaluated)
				_cells(i,j).evaluate();
}

Boolean SpreadSheet::parse()
{	char *which;

	if (nextToken() == letter) 
	{	which = tokenbuffer.which;
//cout << which<<" < ";
		toss();
	} 
	else
	{	parseError(String("Illegal command "));
		toss();
		return true;
	}
	
	if(strcmp(which, "done")==0)
	{	
//cout<<" done "<<endl; 
		return false;
	}
	
	if(strcmp(which, "eval")==0) 
	{	
//cout<<" evaluating "<<endl;
		evaluate();
	}
	else if(strcmp(which, "print")==0)
	{	
//cout << " printing "<<endl;
		draw();
	}
	else if(strcmp(which, "dump")==0)
	{	
//cout << " showing "<<endl;
		dump();
	}
	else if (legalCellSpelling(which))
	{	match(assignop);
		int i,j;
		extractCell(which, i,j);
		if(!legalCell(i,j)) 
		{	parseError(String("Illegal cell: ") + which);
			return true;
		}
//cout << i<< ' '<<j<<endl;
		_cells(i,j).parse();
	}
	else
	{	parseError(String("Illegal cell/command: ") + which);
	}
	
	return true;
}

// ********************** driver ****************************************

void shMain(SpreadSheet &sheet)
{ 	initscanner();
	while(sheet.parse())
		//nothing
	;
	cout<<"All done."<<endl;
}


