#ifndef __SYNTREE_H__
#define __SYNTREE_H__

#include <string>
#include <map>
#include <vector>
#include <iostream>
using namespace std;

// Hijerarhija klasa kojima se predstavlja sintaksno stablo 
// koje opisuje kod funkcija jezika. 

// Apstarktna bazna klasa 
class SynTreeNode
{
public:
    virtual ~SynTreeNode()
    {}
    
    // Funkcija vrsi interpretaciju koda za navedene vrednosti lokalnih 
    // promenljivih funkcije
    virtual int Interpret(map<string, int>& variables) = 0;
};

// Klasa za predstavljanje brojevnih konstanti
class ConstantNode : public SynTreeNode
{
public:
	ConstantNode(int value)
		: _value(value)
	{}
	
	int Interpret(map<string, int>& variables)
	{
		return _value;
	}
	
private:
    // Vrednost brojevne konstante
    int _value;
};

// Klasa za predstavljanje promenljivih
class VariableNode : public SynTreeNode
{
public:
	VariableNode(const string& name)
		: _name(name)
	{}
	
	int Interpret(map<string, int>& variables)
	{
		// Vrednost promenljive citamo iz mape
		map<string, int>::iterator i = variables.find(_name);
		// Ukoliko promenljiva nije definisana, podrazumevamo da joj 
		// je vrednost 0
		return i == variables.end() ? 0 : i->second;
	}
	
private:
    // Ime promenljive
    string _name;
};

// Apstraktna bazna klasa za predstavljanje operatora
class OperatorNode : public SynTreeNode
{
protected:
    OperatorNode(SynTreeNode* op1)
    {
    	_operands.push_back(op1);
    }
    
    OperatorNode(SynTreeNode* op1, SynTreeNode* op2)
    {
    	_operands.push_back(op1);
    	_operands.push_back(op2);
    }    

    OperatorNode(SynTreeNode* op1, SynTreeNode* op2, SynTreeNode* op3)
    {
    	_operands.push_back(op1);
    	_operands.push_back(op2);
    	_operands.push_back(op3);
    }    

    OperatorNode(const vector<SynTreeNode*>& operands)
    	: _operands(operands)
    {}
	
public:
    ~OperatorNode()
    {
    	vector<SynTreeNode*>::iterator i;
        for (i = _operands.begin(); i != _operands.end(); i++)
        	delete *i;
    }

    // Operator se interpretira tako sto se izvrsi interpretacija 
    // svih njegovih potomaka, a zatim se vrednost objedini primenom 
    // virtuelne funkcije Operate
    int Interpret(map<string, int>& variables)
    {
        vector<int> operand_values;

        vector<SynTreeNode*>::iterator i;
        for (i = _operands.begin(); i != _operands.end(); i++)
            operand_values.push_back((*i)->Interpret(variables));

        return Operate(operand_values);
    }

    // Pomocna virtuelna funkcija koja vrsi primenu operatora na dati niz brojeva
    virtual int Operate(const vector<int>&)
    {  	return 0;	}

protected:
    // Operandi
    vector<SynTreeNode*> _operands;
};

// Operator sabiranja
class PlusNode: public OperatorNode
{
public:
	PlusNode(SynTreeNode* op1, SynTreeNode* op2)
		: OperatorNode(op1, op2)
	{}
	
protected:
    int Operate(const vector<int>& v)
    {   return v[0] + v[1]; }
};

// Operator mnozenja
class TimesNode: public OperatorNode
{
public:
	TimesNode(SynTreeNode* op1, SynTreeNode* op2)
		: OperatorNode(op1, op2)
	{}
	
protected:
    int Operate(const vector<int>& v)
    {   return v[0] * v[1]; }
};

// Operator deljenja
class DivideNode: public OperatorNode
{
public:
	DivideNode(SynTreeNode* op1, SynTreeNode* op2)
		: OperatorNode(op1, op2)
	{}
	
protected:
    int Operate(const vector<int>& v)
    {   return v[0] / v[1]; }
};


// Operator oduzimanja
class MinusNode: public OperatorNode
{
public:
	MinusNode(SynTreeNode* op1, SynTreeNode* op2)
		: OperatorNode(op1, op2)
	{}
protected:
    int Operate(const vector<int>& v)
    {   return v[0] - v[1]; }
};

// Operator poredjenja ==
class EqNode : public OperatorNode
{
public:
	EqNode(SynTreeNode* op1, SynTreeNode* op2)
		: OperatorNode(op1, op2)
	{}

protected:
    int Operate(const vector<int>& v)
    {   return v[0] == v[1]; }
};

// Ternarni uslovni operator
class QuestionNode : public OperatorNode
{
public:
	QuestionNode(SynTreeNode* op1, SynTreeNode* op2, SynTreeNode* op3) 
		: OperatorNode(op1, op2, op3)
	{}
		
protected:
	// Prilikom interpretacije se izvrsava lenjo izracunavanje
	// Zbog toga je neophodno predefinisati funkciju Interpret
	int Interpret(map<string, int>& variables)
	{
		return _operands[0]->Interpret(variables) ? _operands[1]->Interpret(variables) : _operands[2]->Interpret(variables);
	}

};

// Klasom se predstavlja operator dodele
class AssignmentNode : public SynTreeNode
{
public:
	AssignmentNode(const string& name, SynTreeNode* expression)
		: _var_name(name), _expression(expression)
	{}
	
	~AssignmentNode()
	{   delete _expression;	}

	int Interpret(map<string, int>& variables)
	{
		// Izracunava se vrednost izraza sa desne strane
		// A zatim se u mapu koja cuva vrednosti lokalnih promenljivih
		// ta vrednost pridruzuje promenljivoj sa leve strane
		return (variables[_var_name] = _expression->Interpret(variables));
	}
	
private:
	// Ime promenljive kojoj se dodeljuje vrednost
	string _var_name; 
	// Desna strana dodele
	SynTreeNode* _expression;
};



// Klasa koja prestavlja poziv funkcije
class FunctionCallNode : public OperatorNode
{
public:
	FunctionCallNode(const string& name, const vector<SynTreeNode*>& operands)
		: OperatorNode(operands), _name(name)
	{}

protected:
        int Operate(const vector<int>& v);

private:
    string _name;
};

class PrintNode : public SynTreeNode
{
public:
	PrintNode(SynTreeNode* expression)
		: _expression(expression)
	{}
	
	~PrintNode()
	{	delete _expression;	}
	
	int Interpret(map<string, int>& variables)
	{
		cout<<_expression->Interpret(variables)<<endl;
		return 0;
	}
private:
	SynTreeNode* _expression;
};

class SequenceNode : public OperatorNode
{
public:
	SequenceNode(SynTreeNode* op1, SynTreeNode* op2)
		: OperatorNode(op1, op2)
	{}
	
protected:
	int Operate(const vector<int>& v)
	{
		return 0;
	}
};

#endif

