#ifndef MATH_PARSER_H
#define MATH_PARSER_H

#include "mathParser\mathStringParser.h"

/// Base node class, every node must implement this interface.
template< typename T>
class AbstractNode
{

public:
	virtual bool evaluate( T &dstValue )=0;
};

/// Base binary node class, every binary node must be inherited from this class.
template< typename T>
class BinaryNode : public AbstractNode<T>
{
protected:
	AbstractNode<T> *leftNode;
	AbstractNode<T> *rightNode;
public:
	virtual bool evaluate( T &dstValue )=0;
};

/// Base unary node class, every unary node must be inherited from this class.
template< typename T>
class UnaryNode : public AbstractNode<T>
{
protected:
	AbstractNode<T> *leftNode;
public:
	virtual bool evaluate( T &dstValue )=0;
};

/// Base leaf node class, every leag node must be inherited from this class.
template< typename T>
class Leaf : public AbstractNode<T>
{
public:
	T value;

	Leaf( const T& value): value( value ) {};

	virtual bool evaluate( T &dstValue ) 
	{ 
		dstValue = value;
		return true; 
	}
};


//########################################
/// Sums values of its childs nodes
template< typename T>
class BinaryPlus: public BinaryNode<T>
{
public:
	BinaryPlus( AbstractNode<T> *leftNode, AbstractNode<T> *rightNode )
	{
		this->leftNode =leftNode;
		this->rightNode = rightNode;
	}

	virtual bool evaluate( T &dstValue )
	{
		T leftOperand, rightOperand;
		bool leftOk = this->leftNode->evaluate( leftOperand );
		bool rightOk = this->rightNode->evaluate( rightOperand );

		assert( leftOk && rightOk && "BinaryPlus node failed during left & right node evaluation!" );

		dstValue = leftOperand + rightOperand;
		return true;
	}
};

/// Subtracts values of its childs nodes.
template< typename T>
class BinaryMinus: public BinaryNode<T>
{
public:
	BinaryMinus( AbstractNode<T> *leftNode, AbstractNode<T> *rightNode )
	{
		this->leftNode =leftNode;
		this->rightNode = rightNode;
	}

	virtual bool evaluate( T &dstValue )
	{
		T leftOperand, rightOperand;
		bool leftOk = this->leftNode->evaluate( leftOperand );
		bool rightOk = this->rightNode->evaluate( rightOperand );

		assert( leftOk && rightOk && "MinusPlus node failed during left & right node evaluation!" );

		dstValue = leftOperand - rightOperand;
		return true;
	}
};

/// Multiplies values of its childs nodes.
template< typename T>
class BinaryMultiply: public BinaryNode<T>
{
public:
	BinaryMultiply( AbstractNode<T> *leftNode, AbstractNode<T> *rightNode )
	{
		this->leftNode =leftNode;
		this->rightNode = rightNode;
	}

	virtual bool evaluate( T &dstValue )
	{
		T leftOperand, rightOperand;
		bool leftOk = this->leftNode->evaluate( leftOperand );
		bool rightOk = this->rightNode->evaluate( rightOperand );

		assert( leftOk && rightOk && "BinaryMultiply node failed during left & right node evaluation!" );

		dstValue = leftOperand * rightOperand;
		return true;
	}
};

/// Divides values of its childs nodes.
template< typename T>
class BinaryDivide: public BinaryNode<T>
{
public:
	BinaryDivide( AbstractNode<T> *leftNode, AbstractNode<T> *rightNode )
	{
		this->leftNode =leftNode;
		this->rightNode = rightNode;
	}

	virtual bool evaluate( T &dstValue )
	{
		T leftOperand, rightOperand;
		bool leftOk = this->leftNode->evaluate( leftOperand );
		bool rightOk = this->rightNode->evaluate( rightOperand );

		assert( leftOk && rightOk && "BinaryDivide node failed during left & right node evaluation!" );

		dstValue = leftOperand / rightOperand;
		return true;
	}
};

/// Build tree from given prefix tokens array
template< typename T> bool buildTree(  vector<mathParser::Token>& prefixTokenArray, AbstractNode<T> **dstTree )
{
	vector< AbstractNode<T>* > nodes;

	for ( int i=0; i<prefixTokenArray.size(); ++i)
	{
		if ( prefixTokenArray[i].type == mathParser::IDENTIFIER )
		{
			Leaf<T>* leaf= new Leaf<T>( prefixTokenArray[i].value );
			nodes.push_back( leaf );
		}

		if ( prefixTokenArray[i].type == mathParser::NUMBER )
		{
			Leaf<T>* leaf= new Leaf<T>( stringToFloat( prefixTokenArray[i].value ) );
			nodes.push_back( leaf );
		}

		if (  prefixTokenArray[i].type == mathParser::OPERATOR )
		{
			BinaryNode<T>* node;

			if ( prefixTokenArray[i].value == "+" )
			{
				node = new BinaryPlus<T>( nodes[ nodes.size()-2 ], nodes[ nodes.size()-1 ] );
				nodes.pop_back();
				nodes.pop_back();
				nodes.push_back( node );
			}

			if ( prefixTokenArray[i].value == "-" )
			{
				node = new BinaryMinus<T>( nodes[ nodes.size()-2 ], nodes[ nodes.size()-1 ] );
				nodes.pop_back();
				nodes.pop_back();
				nodes.push_back( node );
			}

			if ( prefixTokenArray[i].value == "*" )
			{
				node = new BinaryMultiply<T>( nodes[ nodes.size()-2 ], nodes[ nodes.size()-1 ] );
				nodes.pop_back();
				nodes.pop_back();
				nodes.push_back( node );
			}

			if ( prefixTokenArray[i].value == "/" )
			{
				node = new BinaryDivide<T>( nodes[ nodes.size()-2 ], nodes[ nodes.size()-1 ] );
				nodes.pop_back();
				nodes.pop_back();
				nodes.push_back( node );
			}
		}
	}

	assert( nodes.size() == 1 && "There should be exactly one node at this moment" );
	if ( nodes.size() != 1 )
	{
		return false;
	}

	*dstTree = nodes[0];
	return true;
}
#endif