/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef EXPRESSION_20070402_TCC_
#define EXPRESSION_20070402_TCC_

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
template <class T>
Expression<T>::Expression(const QString &s, variable_getter_t vg, void *vg_param, memory_reader_t mr, void *mr_param) : 
		m_Expression(s), m_ExpressionPtr(m_Expression.begin()), 
		m_VariableGetter(vg), m_VariableGetterParam(vg_param), 
		m_MemoryReader(mr), m_MemoryReaderParam(mr_param) {
}

//------------------------------------------------------------------------------
// Name: 
// Desc: privat entry point with sanity check
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp(T &result) {
	if(m_Token.m_Type == Token::UNKNOWN) {
		throw Error(Error::SYNTAX);
	}
	
	eval_exp0(result);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: logic
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp0(T &result) {
	eval_exp1(result);

	for(Token op = m_Token; op.m_Operator == Token::LOGICAL_AND || op.m_Operator == Token::LOGICAL_OR; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp1(partial_value);

		// add or subtract
		switch(op.m_Operator) {
		case Token::LOGICAL_AND:
			result = result && partial_value;
			break;
		case Token::LOGICAL_OR:
			result = result || partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: binary logic
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp1(T &result) {
	eval_exp2(result);

	for(Token op = m_Token; op.m_Operator == Token::AND || op.m_Operator == Token::OR || op.m_Operator == Token::XOR; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp2(partial_value);

		// add or subtract
		switch(op.m_Operator) {
		case Token::AND:
			result &= partial_value;
			break;
		case Token::OR:
			result |= partial_value;
			break;
		case Token::XOR:
			result ^= partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: comparisons
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp2(T &result) {
	eval_exp3(result);

	for(Token op = m_Token; op.m_Operator == Token::LT || op.m_Operator == Token::LE || op.m_Operator == Token::GT || op.m_Operator == Token::GE || op.m_Operator == Token::EQ || op.m_Operator == Token::NE; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp3(partial_value);

		// perform the relational operation
		switch(op.m_Operator) {
		case Token::LT:
			result = result < partial_value;
			break;
		case Token::LE:
			result = result <= partial_value;
			break;
		case Token::GT:
			result = result > partial_value;
			break;
		case Token::GE:
			result = result >= partial_value;
			break;
		case Token::EQ:
			result = result == partial_value;
			break;
		case Token::NE:
			result = result != partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: shifts
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp3(T &result) {
	eval_exp4(result);

	for(Token op = m_Token; op.m_Operator == Token::RSHFT || op.m_Operator == Token::LSHFT; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp4(partial_value);

		// perform the shift operation
		switch(op.m_Operator) {
		case Token::LSHFT:
			result <<= partial_value;
			break;
		case Token::RSHFT:
			result >>= partial_value;
			break;
		default:
			break;
		}
	}	
}

//------------------------------------------------------------------------------
// Name: 
// Desc: addition/subtraction
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp4(T &result) {
	eval_exp5(result);

	for(Token op = m_Token; op.m_Operator == Token::PLUS || op.m_Operator == Token::MINUS; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp5(partial_value);

		// add or subtract
		switch(op.m_Operator) {
		case Token::PLUS:
			result += partial_value;
			break;
		case Token::MINUS:
			result -= partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: multiplication/division
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp5(T &result) {
	eval_exp6(result);

	for(Token op = m_Token; op.m_Operator == Token::MUL || op.m_Operator == Token::DIV || op.m_Operator == Token::MOD; op = m_Token) {
		T partial_value;

		getToken();
		eval_exp6(partial_value);

		// mul, div, or modulus
		switch(op.m_Operator) {
		case Token::MUL:
			result *= partial_value;
			break;
		case Token::DIV:
			if(partial_value == 0) {
				throw Error(Error::DIVIDE_BY_ZERO);
			}
			result /= partial_value;
			break;
		case Token::MOD:
			if(partial_value == 0) {
				throw Error(Error::DIVIDE_BY_ZERO);
			}
			result %= partial_value;
			break;
		default:
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: unary expressions
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp6(T &result) {

	Token op = m_Token;
	if(op.m_Operator == Token::PLUS || op.m_Operator == Token::MINUS || op.m_Operator == Token::CMP || op.m_Operator == Token::NOT) {
		getToken();
	}

	eval_exp7(result);

	switch(op.m_Operator) {
	case Token::PLUS:
		// this may seems like a waste, but unary + can be overloaded for a type
		// to have a non-nop effect!
		result = +result;
		break;
	case Token::MINUS:
		result = -result;
		break;
	case Token::CMP:
		result = ~result;
		break;
	case Token::NOT:
		result = !result;
		break;
	default:
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: sub-expressions
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_exp7(T &result) {


	switch(m_Token.m_Operator) {
	case Token::LPAREN:
		getToken();

		// get sub-expression
		eval_exp0(result);

		if(m_Token.m_Operator != Token::RPAREN) {
			throw Error(Error::UNBALANCED_PARENS);
		}

		getToken();
		break;
	case Token::RPAREN:
		throw Error(Error::INCORRECT_PARENS);
		break;
	case Token::LBRACE:
		getToken();

		// get sub-expression
		eval_exp0(result);
		
		if(m_MemoryReader != 0) {
			result = (*m_MemoryReader)(result, m_MemoryReaderParam);
		} else {
			throw Error(Error::CANNOT_READ_MEMORY);
		}

		if(m_Token.m_Operator != Token::RBRACE) {
			throw Error(Error::UNBALANCED_BRACES);
		}

		getToken();
		break;
	case Token::RBRACE:
		throw Error(Error::INCORRECT_BRACES);
		break;
	default:
		eval_atom(result);
		break;
	
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: atoms (variables/constants)
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::eval_atom(T &result) {

	switch(m_Token.m_Type) {	
	case Token::VARIABLE:
		if(m_VariableGetter != 0) {
			result = m_VariableGetter(m_Token.m_Data, m_VariableGetterParam);
		} else {
			throw Error(Error::UNKNOWN_VARIABLE);
		}
		getToken();	
		break;
	case Token::NUMBER:
		bool ok;
		result = m_Token.m_Data.toULong(&ok, 0);
		if(!ok) {
			throw Error(Error::INVALID_NUMBER);
		}
		getToken();	
		break;
	default:
		throw Error(Error::SYNTAX);
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
template <class T>
void Expression<T>::getToken() {
	
	// clear previous token
	m_Token = Token();

	// eat up white space
	while(m_ExpressionPtr != m_Expression.end() && m_ExpressionPtr->isSpace()) {
		++m_ExpressionPtr;
	}

	if(m_ExpressionPtr != m_Expression.end()) {

		// get the token
		switch(m_ExpressionPtr->toAscii()) {
		case '(':
			++m_ExpressionPtr;
			m_Token.m_Data = "(";
			m_Token.m_Operator = Token::LPAREN;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case ')':
			++m_ExpressionPtr;
			m_Token.m_Data = ")";
			m_Token.m_Operator = Token::RPAREN;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '[':
			++m_ExpressionPtr;
			m_Token.m_Data = "[";
			m_Token.m_Operator = Token::LBRACE;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case ']':
			++m_ExpressionPtr;
			m_Token.m_Data = "]";
			m_Token.m_Operator = Token::RBRACE;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '!':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.m_Data = "!=";
				m_Token.m_Operator = Token::NE;
				m_Token.m_Type = Token::OPERATOR;
			} else {
				m_Token.m_Data = "!";
				m_Token.m_Operator = Token::NOT;
				m_Token.m_Type = Token::OPERATOR;
			}
			break;
		case '+':
			++m_ExpressionPtr;
			m_Token.m_Data = "+";
			m_Token.m_Operator = Token::PLUS;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '-':
			++m_ExpressionPtr;
			m_Token.m_Data = "-";
			m_Token.m_Operator = Token::MINUS;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '*':
			++m_ExpressionPtr;
			m_Token.m_Data = "*";
			m_Token.m_Operator = Token::MUL;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '/':
			++m_ExpressionPtr;
			m_Token.m_Data = "/";
			m_Token.m_Operator = Token::DIV;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '%':
			++m_ExpressionPtr;
			m_Token.m_Data = "%";
			m_Token.m_Operator = Token::MOD;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '&':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '&') {
				++m_ExpressionPtr;
				m_Token.m_Data = "&&";
				m_Token.m_Operator = Token::LOGICAL_AND;
				m_Token.m_Type = Token::OPERATOR;
			} else {
				m_Token.m_Data = "&";
				m_Token.m_Operator = Token::AND;
				m_Token.m_Type = Token::OPERATOR;
			}
			break;
		case '|':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '|') {
				++m_ExpressionPtr;
				m_Token.m_Data = "&&";
				m_Token.m_Operator = Token::LOGICAL_OR;
				m_Token.m_Type = Token::OPERATOR;
			} else {
				m_Token.m_Data = "|";
				m_Token.m_Operator = Token::OR;
				m_Token.m_Type = Token::OPERATOR;
			}
			break;
		case '^':
			++m_ExpressionPtr;
			m_Token.m_Data = "^";
			m_Token.m_Operator = Token::RPAREN;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '~':
			++m_ExpressionPtr;
			m_Token.m_Data = "~";
			m_Token.m_Operator = Token::CMP;
			m_Token.m_Type = Token::OPERATOR;
			break;
		case '=':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.m_Data = "==";
				m_Token.m_Operator = Token::EQ;
				m_Token.m_Type = Token::OPERATOR;
			} else {
				throw Error(Error::SYNTAX);
			}
			break;
		case '<':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '<') {
				++m_ExpressionPtr;
				m_Token.m_Data = "<<";
				m_Token.m_Operator = Token::LSHFT;
				m_Token.m_Type = Token::OPERATOR;
			} else if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.m_Data = "<=";
				m_Token.m_Operator = Token::LE;
				m_Token.m_Type = Token::OPERATOR;
			} else {
				m_Token.m_Data = "<";
				m_Token.m_Operator = Token::LT;
				m_Token.m_Type = Token::OPERATOR;
			}
			break;
		case '>':
			++m_ExpressionPtr;
			if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '>') {
				++m_ExpressionPtr;
				m_Token.m_Data = ">>";
				m_Token.m_Operator = Token::RSHFT;
				m_Token.m_Type = Token::OPERATOR;
			} else if(m_ExpressionPtr != m_Expression.end() && *m_ExpressionPtr == '=') {
				++m_ExpressionPtr;
				m_Token.m_Data = ">=";
				m_Token.m_Operator = Token::GE;
				m_Token.m_Type = Token::OPERATOR;
			} else {
				m_Token.m_Data = ">";
				m_Token.m_Operator = Token::GT;
				m_Token.m_Type = Token::OPERATOR;
			}
			break;
		default:
			// is it a numerical constant?
			if(m_ExpressionPtr->isDigit()) {
				QString tempString;

				while(m_ExpressionPtr != m_Expression.end() && !isDelim(*m_ExpressionPtr)) {
					tempString += *m_ExpressionPtr++;
				}

				m_Token.m_Data = tempString;
				m_Token.m_Operator = Token::NONE;
				m_Token.m_Type = Token::NUMBER;
			} else {
				// it must be a variable constant, get its name
				QString tempString;

				while(m_ExpressionPtr != m_Expression.end() && !isDelim(*m_ExpressionPtr)) {
					tempString += *m_ExpressionPtr++;
				}

				m_Token.m_Data = tempString;
				m_Token.m_Operator = Token::NONE;
				m_Token.m_Type = Token::VARIABLE;

			}
			break;
		}
	}		
}

#endif
