// BGDNetNDSFunction.h: interface for the NDSFunction class.
//
//////////////////////////////////////////////////////////////////////

#ifndef __BGDNetNDSFunction_h__
#define __BGDNetNDSFunction_h__

#include "BGDNetConfig.h"

#include <stack>
#include <ctype.h>

#ifdef _MSC_VER
# pragma warning(disable:4251)
#endif

namespace BGDNet
{

using std::stack;

template<class T>
class BGDNET_API ExprParser  
{

	typedef stack<T>  StackE;
	typedef stack<char>  StackT;

	struct ParserException
	{
		ParserException(int err) : error(err) {};
		enum
		{
			ERR_NO_NEW_MEMORY,
			ERR_INVALID_OPERATION,
			ERR_ZERO_DIVIDE,
			ERR_PARSE
		};
		int error;
	};

public:

	bool assign(const char* pExpr);
	bool evaluate(T* values, T* result) throw (ParserException);
	ExprParser();
	virtual ~ExprParser();

private:

	void F_6();
	void F_5()				throw (ParserException);
	void F_4(char c)	throw (ParserException);
	void F_3();
	void F_2(char c)	throw (ParserException);
	void F_1(char c);

	StackE m_E;
	StackT m_T;

	static int jump_table[6][7];
	
	char* m_pExpr;

	bool m_assigned;

	bool m_parsed;
};

template<class T>
ExprParser<T>::ExprParser() :
		m_assigned(false),
		m_parsed(false),
		m_pExpr(NULL)
{

}

template<class T>
ExprParser<T>::~ExprParser()
{
	if(m_pExpr != NULL)
	{
		delete[] m_pExpr;
	}
}
template<class T>
bool ExprParser<T>::evaluate(T* values, T* result)
{
	int i = 0, j = 0;;
	char* e = m_pExpr;
	char numArg[20];
	bool numArgEnd = true;
try
{
	if(!m_assigned)
	{
		return false;
	}

	while(!m_parsed)
	{
		if(e[i] == ' ')
		{
			i++;
			continue;
		}

		if(isdigit(e[i]) ||
				e[i] == '.' ||
				e[i] == ',')
		{
			numArgEnd = false;
			numArg[j] = e[i];
			i++; j++;
			continue;
		}
		
		if(numArgEnd == false)
		{
			numArgEnd = true;
			numArg[j] = 0;
			j = 0;

			m_E.push(T(atof(numArg)));
		}

		if(isalpha(e[i]))
		{
			m_E.push(values[tolower(e[i]) - 'a']);
		}
		else
		{
			int m,n;

			if(m_T.empty())
			{
				m = 0;
			}
			else switch(m_T.top())
			{
				case '(': m = 1; break;
				case '+': m = 2; break;
				case '-': m = 3; break;
				case '*': m = 4; break;
				case '/': m = 5; break;
			
				default:
					throw ParserException(ParserException::ERR_PARSE);
			}

			switch(e[i])
			{
				case 0:   n = 0; break;
				case '(': n = 1; break;
				case '+': n = 2; break;
				case '-': n = 3; break;
				case '*': n = 4; break;
				case '/': n = 5; break;
				case ')': n = 6; break;
			
				default:
					throw ParserException(ParserException::ERR_PARSE);
			}

			switch(jump_table[m][n])
			{
				case 1: F_1(e[i]); break;
				case 2: F_2(e[i]); break;
				case 3: F_3(); break;
				case 4: F_4(e[i]); break;
				case 5: F_5(); break;
				case 6: F_6(); break;

				default:
					throw ParserException(ParserException::ERR_PARSE);
			}
		}

		if(e[i] != 0) i++;
	}

	m_parsed = false;
	*result = m_E.top(); m_E.pop();

#ifdef _DEBUG
	if(m_E.size() || m_T.size())
	{
		assert(false);
	}
#endif
	
	return true;

}
catch(ParserException)
{
	while(!m_E.empty())
	{
		m_E.pop();
	}

	while(!m_T.empty())
	{
		m_T.pop();
	}
	
	m_parsed = false;
	
	return false;
}
catch(...)
{
	while(!m_E.empty())
	{
		m_E.pop();
	}

	while(!m_T.empty())
	{
		m_T.pop();
	}
	
	m_parsed = false;
	
	return false;
}
}

template<class T>
bool ExprParser<T>::assign(const char* pExpr)
{
	int sLen = strlen(pExpr);
	if(m_pExpr != NULL)
	{
		delete[] m_pExpr;
		m_pExpr = NULL;
		m_assigned = false;
	}
	
	if(sLen)
	{
		m_pExpr = new char[200];
	}
	else
	{
		return false;
	}
	
	if(m_pExpr == NULL)
	{
		return false;
	}


	int i = 0, j = 0;
	while(pExpr[i])
	{
		if(pExpr[i] == '-' &&
				( j == 0							||
					m_pExpr[j-1] == '(' ||
					m_pExpr[j-1] == '+' ||
					m_pExpr[j-1] == '-' ||
					m_pExpr[j-1] == '*' ||
					m_pExpr[j-1] == '/'))
		{
				//унарный минус !
				//заменяем его вычитанием следующего арумента из нуля

				m_pExpr[j++] = '(';
				m_pExpr[j++] = '0';
				m_pExpr[j++] = pExpr[i++]; //сам минус

				while(pExpr[i] != ')' &&
							pExpr[i] != '+' &&
							pExpr[i] != '-' &&
							pExpr[i] != '*' &&
							pExpr[i] != '/' &&
							pExpr[i] != 0)
				{
					//ищем следующую операцию чтобы определить
					//что аргумент прочитан и поставить закрывающую скобку
					if(pExpr[i] == ' ')
					{
						i++;
						continue;
					}

					m_pExpr[j++] = pExpr[i++];
				}

				m_pExpr[j++] = ')';
				//в результате -x превращается в (0-x)
		}
		else if(pExpr[i] == ' ')
		{
			i++;
		}
		else
		{
			m_pExpr[j++] = pExpr[i++];
		}
	}

	m_pExpr[j] = 0;

	m_assigned = true;

	return true;
}

template<class T>
void ExprParser<T>::F_1(char c)
{
	m_T.push(c);
}

template<class T>
void ExprParser<T>::F_2(char c) throw (ParserException)
{
	T b = m_E.top(); m_E.pop();
	T a = m_E.top(); m_E.pop();
	T y;

	char cc = m_T.top(); m_T.pop();
	switch(cc)
	{
		case '+': y = a + b; break;
		case '-': y = a - b; break;
		case '*': y = a * b; break;
		case '/': y = a / b; break;
	
		default:
			throw ParserException(ParserException::ERR_INVALID_OPERATION);
	}

	m_E.push(y);
	m_T.push(c);
}

template<class T>
void ExprParser<T>::F_3()
{
	m_T.pop();
}

template<class T>
void ExprParser<T>::F_4(char c) throw (ParserException)
{
	T b = m_E.top(); m_E.pop();
	T a = m_E.top(); m_E.pop();
	T y;

	char cc = m_T.top(); m_T.pop();
	switch(cc)
	{
		case '+': y = a + b; break;
		case '-': y = a - b; break;
		case '*': y = a * b; break;
		case '/':
					if(b == T(0))
					{
						throw ParserException(ParserException::ERR_ZERO_DIVIDE);;
					}
					else
					{
						y = a / b;
					}
					break;
	
		default:
			throw ParserException(ParserException::ERR_INVALID_OPERATION);
	}

	m_E.push(y);
//
	int m,n;

	if(m_T.empty())
	{
		m = 0;
	}
	else switch(m_T.top())
	{
		case '(': m = 1; break;
		case '+': m = 2; break;
		case '-': m = 3; break;
		case '*': m = 4; break;
		case '/': m = 5; break;
	
		default:
			throw ParserException(ParserException::ERR_PARSE);
	}

	switch(c)
	{
		case 0:   n = 0; break;
		case '(': n = 1; break;
		case '+': n = 2; break;
		case '-': n = 3; break;
		case '*': n = 4; break;
		case '/': n = 5; break;
		case ')': n = 6; break;
	
		default:
			throw ParserException(ParserException::ERR_PARSE);
	}

	switch(jump_table[m][n])
	{
		case 1: F_1(c); break;
		case 2: F_2(c); break;
		case 3: F_3(); break;
		case 4: F_4(c); break;
		case 5: F_5(); break;
		case 6: F_6(); break;

		default:
			throw ParserException(ParserException::ERR_PARSE);
	}
}

template<class T>
void ExprParser<T>::F_5() throw (ParserException)
{
	throw ParserException(ParserException::ERR_PARSE);
}

template<class T>
void ExprParser<T>::F_6()
{
	m_parsed = true;
}

template<class T>
int ExprParser<T>::jump_table[6][7] =
	{ {6, 1, 1, 1, 1, 1, 5 },
		{5, 1, 1, 1, 1, 1, 3 },
		{4, 1, 2, 2, 1, 1, 4 },
		{4, 1, 2, 2, 1, 1, 4 },
		{4, 1, 4, 4, 2, 2, 4 },
		{4, 1, 4, 4, 2, 2, 4 }};


#ifdef _MSC_VER
# pragma warning(disable:4231)
#endif

BGDNET_TEMPLATE(ExprParser<double>);

#ifdef _MSC_VER
# pragma warning(default:4231)
#endif


class BGDNET_API NDSFunction  
{
public:
	bool init(const char* prjName,
						const int fNum,
						const char* orgName);

	bool evaluate (double* pVals, double* pRet);
	
	NDSFunction();
	virtual ~NDSFunction();

private:

	ExprParser<double> m_parser;

	bool m_initialized;
};

} // namespace BGDNet

#ifdef _MSC_VER
# pragma warning(default:4231)
#endif

#endif // __BGDNetNDSFunction_h__