#ifndef POLIZ_HEADER
#define POLIZ_HEADER

#include "Stack.h"
#include "List.h"
#include "String.h"
#include "Map.h"

class ScriptFunctions
{
private:
	virtual void buy(int) = 0;
	virtual void sell(int) = 0;
	virtual void prod(int) = 0;
	virtual void say(int) = 0;
	
	typedef void (ScriptFunctions::*Function)(int);
	typedef Map<String, Function> FunctionsMap;

	FunctionsMap functions;
	
public:
	ScriptFunctions();
	void Call(const String& name, const int count);
	
	class NoSuchFunction{};
};

class VarsTable
{
public:
	class NoVarAtAddress{};
	class InvalidConversion{};
	
	typedef unsigned Address;
	class Variable
	{
	public:
		enum Type {v_unknown, v_int, v_float, v_string, v_array};
		
	private:
		String name;
		Address address;
		bool isConst;
		Type type;
		void *value;
		
		void smartDelete();
	public:
		Variable();
		~Variable();
		
		Variable& operator = (const Variable &variable);
		
		void Print();
		
		inline String GetName() const {return name;}
		inline void SetName(const String& name) {this->name = name;}
		inline Address GetAddress() const {return address;}
		inline void SetAddress(const Address address) {this->address = address;}
		inline bool GetIsConst() const {return isConst;}
		inline void SetIsConst(const bool isConst = true) {this->isConst = isConst;}
		inline Type GetType() const {return type;}
		inline void SetType(const Type type) {this->type = type;}
		inline void* GetValue() const {return value;}
		inline void SetValue(void *value) {smartDelete(); this->value = value;}
	};
	
private:
	typedef Map<Address, Variable*> VarsMap;
	typedef Map<String, Address> NamesMap;

	VarsMap variables;
	NamesMap names;
	unsigned lastID;
	unsigned count;
	
public:
	VarsTable();
	~VarsTable();
	
	Variable *GetVariable(const String &name);
	Variable *GetVariable(const Address address);
	Variable *Append();
	
	Variable *Convert(Variable *var, Variable::Type toType);
	
	inline unsigned GetCount() const {return count;}
};

typedef Map<String, VarsTable::Address> Array;

typedef Stack<VarsTable::Address> PolizStack;

class PolizElement
{
public:
	class PolizException{};
	virtual void Evaluate(PolizStack *stack, VarsTable *table, 
		ScriptFunctions *functions, List<PolizElement*>::Iterator *position) = 0;
	virtual ~PolizElement() {}
};

typedef List<PolizElement*>::Iterator Position;

class PolizSimple: public PolizElement
{
public:
	virtual void Evaluate(PolizStack *stack, VarsTable *table,
		ScriptFunctions *functions, Position *position);
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table) = 0;
};

class PolizVariable: public PolizSimple
{
private:
	String name;
	
public:
	PolizVariable(const String name) {this->name = name;};
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table);
	virtual ~PolizVariable() {}
};

class PolizConst: public PolizSimple
{
public:
	virtual VarsTable::Address GetAddress(VarsTable *table) = 0;
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table);
};

class PolizInt: public PolizConst
{
private:
	int value;
public:
	PolizInt(const int value) {this->value = value;}
	virtual VarsTable::Address GetAddress(VarsTable *table);
};

class PolizFloat: public PolizConst
{
private:
	double value;
public:
	PolizFloat(const double value) {this->value = value;}
	virtual VarsTable::Address GetAddress(VarsTable *table);
};

class PolizString: public PolizConst
{
private:
	String value;
public:
	PolizString(const String &value) {this->value = value;}
	virtual VarsTable::Address GetAddress(VarsTable *table);
};

class PolizBinary: public PolizSimple
{
public:
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table);
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2) = 0;
};

class PolizPlus: public PolizBinary
{
public:
	class PlusException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizMinus: public PolizBinary
{
public:
	class MinusException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizMul: public PolizBinary
{
public:
	class MulException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizDiv: public PolizBinary
{
public:
	class DivException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizMod: public PolizBinary
{
public:
	class ModException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizOr: public PolizBinary
{
public:
	class OrException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizAnd: public PolizBinary
{
public:
	class AndException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizLess: public PolizBinary
{
public:
	class LessException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizMore: public PolizBinary
{
public:
	class MoreException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizLessEq: public PolizBinary
{
public:
	class LessEqException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizMoreEq: public PolizBinary
{
public:
	class MoreEqException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizEq: public PolizBinary
{
public:
	class EqException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizNotEq: public PolizBinary
{
public:
	class NotEqException: public PolizException {};
	virtual VarsTable::Address EvaluateBinary(VarsTable *table,
		VarsTable::Variable *op1, VarsTable::Variable *op2);
};

class PolizAssign: public PolizSimple
{
public:
	class AssignException: public PolizException {};
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table);
};

class PolizUnar: public PolizSimple
{
public:
	class UnarException: public PolizException {};
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table);
};

class PolizNot: public PolizSimple
{
public:
	class NotException: public PolizException {};
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table);
};

class PolizFunction: public PolizElement
{
private:
	String name;
public:
	class FuncException: public PolizException {};
	virtual void Evaluate(PolizStack *stack, VarsTable *table,
		ScriptFunctions *functions, Position *position);
	PolizFunction(const String &name) {this->name = name;}
};

class PolizArray: public PolizSimple
{
private:
	unsigned count;
public:
	class ArrayException: public PolizException {};
	virtual void EvaluateSimple(PolizStack *stack, VarsTable *table);
	PolizArray(const unsigned count) {this->count = count;}
};

#endif
