#ifndef SoftWire_CodeGenerator_hpp
#define SoftWire_CodeGenerator_hpp

#include "Emulator.hpp"

namespace SoftWire
{
	class CodeGenerator : public Emulator
	{
		class Variable
		{
		public:
			virtual ~Variable();

			void free();

		protected:
			Variable(int size);

			int ref() const;

			const int size;

		private:
			int reference;
			int previous;
		};

	public:
		class Byte : public Variable
		{
		public:
			Byte();

			operator OperandREG8() const;
		};

		class Char : public Byte
		{
		public:
			Char();
			Char(char c);
			Char(Char &c);

			Char &operator=(const Char &c);

			Char &operator+=(const Char &c);
			Char &operator-=(const Char &c);
			Char &operator*=(const Char &c);
			Char &operator/=(const Char &c);
			Char &operator%=(const Char &c);
			Char &operator<<=(const Char &c);
			Char &operator>>=(const Char &c);
			Char &operator&=(const Char &c);
			Char &operator^=(const Char &c);
			Char &operator|=(const Char &c);

			Char operator+(const Char &c);
			Char operator-(const Char &c);
			Char operator*(const Char &c);
			Char operator/(const Char &c);
			Char operator%(const Char &c);
			Char operator<<(const Char &c);
			Char operator>>(const Char &c);
			Char operator&(const Char &c);
			Char operator^(const Char &c);
			Char operator|(const Char &c);

			Char &operator+=(char c);
			Char &operator-=(char c);
			Char &operator*=(char c);
			Char &operator/=(char c);
			Char &operator%=(char c);
			Char &operator<<=(char c);
			Char &operator>>=(char c);
			Char &operator&=(char c);
			Char &operator^=(char c);
			Char &operator|=(char c);

			Char operator+(char c);
			Char operator-(char c);
			Char operator*(char c);
			Char operator/(char c);
			Char operator%(char c);
			Char operator<<(char c);
			Char operator>>(char c);
			Char operator&(char c);
			Char operator^(char c);
			Char operator|(char c);
		};

		class Word : public Variable
		{
		public:
			Word();

			operator OperandREG16() const;
		};

		class Short : public Word
		{
		public:
			Short();
			Short(short s);
			Short(Short &s);

			Short &operator=(const Short &s);

			Short &operator+=(const Short &s);
			Short &operator-=(const Short &s);
			Short &operator*=(const Short &s);
			Short &operator/=(const Short &s);
			Short &operator%=(const Short &s);
			Short &operator<<=(const Short &s);
			Short &operator>>=(const Short &s);
			Short &operator&=(const Short &s);
			Short &operator^=(const Short &s);
			Short &operator|=(const Short &s);

			Short operator+(const Short &s);
			Short operator-(const Short &s);
			Short operator*(const Short &s);
			Short operator/(const Short &s);
			Short operator%(const Short &s);
			Short operator<<(const Short &s);
			Short operator>>(const Short &s);
			Short operator&(const Short &s);
			Short operator^(const Short &s);
			Short operator|(const Short &s);

			Short &operator+=(short s);
			Short &operator-=(short s);
			Short &operator*=(short s);
			Short &operator/=(short s);
			Short &operator%=(short s);
			Short &operator<<=(short s);
			Short &operator>>=(short s);
			Short &operator&=(short s);
			Short &operator^=(short s);
			Short &operator|=(short s);

			Short operator+(short s);
			Short operator-(short s);
			Short operator*(short s);
			Short operator/(short s);
			Short operator%(short s);
			Short operator<<(short s);
			Short operator>>(short s);
			Short operator&(short s);
			Short operator^(short s);
			Short operator|(short s);
		};

		class Dword : public Variable
		{
		public:
			Dword();

			operator OperandREG32() const;
		};

		class Int : public Dword
		{
		public:
			Int();
			Int(int i);
			Int(Int &i);

			Int &operator=(const Int &i);

			Int &operator+=(const Int &i);
			Int &operator-=(const Int &i);
			Int &operator*=(const Int &i);
			Int &operator/=(const Int &i);
			Int &operator%=(const Int &i);
			Int &operator<<=(const Int &i);
			Int &operator>>=(const Int &i);
			Int &operator&=(const Int &i);
			Int &operator^=(const Int &i);
			Int &operator|=(const Int &i);

			Int operator+(const Int &i);
			Int operator-(const Int &i);
			Int operator*(const Int &i);
			Int operator/(const Int &i);
			Int operator%(const Int &i);
			Int operator<<(const Int &i);
			Int operator>>(const Int &i);
			Int operator&(const Int &i);
			Int operator^(const Int &i);
			Int operator|(const Int &i);

			Int &operator+=(int i);
			Int &operator-=(int i);
			Int &operator*=(int i);
			Int &operator/=(int i);
			Int &operator%=(int i);
			Int &operator<<=(int i);
			Int &operator>>=(int i);
			Int &operator&=(int i);
			Int &operator^=(int i);
			Int &operator|=(int i);

			Int operator+(int i);
			Int operator-(int i);
			Int operator*(int i);
			Int operator/(int i);
			Int operator%(int i);
			Int operator<<(int i);
			Int operator>>(int i);
			Int operator&(int i);
			Int operator^(int i);
			Int operator|(int i);
		};

		class Word4;
		class Dword2;

		class Qword : public Variable
		{
			friend Word4;
			friend Dword2;

		public:
			Qword();
			Qword(Qword &qword);

			operator OperandMMREG() const;

			Qword &operator=(const Qword &qword);

			Qword &operator+=(const Qword &qword);
			Qword &operator-=(const Qword &qword);
			Qword &operator<<=(const Qword &qword);
			Qword &operator&=(const Qword &qword);
			Qword &operator^=(const Qword &qword);
			Qword &operator|=(const Qword &qword);

			Qword operator+(const Qword &qword);
			Qword operator-(const Qword &qword);
			Qword operator<<(const Qword &qword);
			Qword operator&(const Qword &qword);
			Qword operator^(const Qword &qword);
			Qword operator|(const Qword &qword);

			Qword &operator<<=(char imm);
			Qword operator<<(char imm);
		};

		class Word4 : public Qword
		{
		public:
			Word4();
			Word4(Word4 &word4);

			operator OperandMMREG() const;

			Word4 &operator=(const Word4 &word4);

			Word4 &operator+=(const Word4 &word4);
			Word4 &operator-=(const Word4 &word4);
			Word4 &operator<<=(const Qword &qword);
			Word4 &operator>>=(const Qword &qword);
			Word4 &operator&=(const Word4 &word4);
			Word4 &operator^=(const Word4 &word4);
			Word4 &operator|=(const Word4 &word4);

			Word4 operator+(const Word4 &word4);
			Word4 operator-(const Word4 &word4);
			Word4 operator<<(const Qword &qword);
			Word4 operator>>(const Qword &qword);
			Word4 operator&(const Word4 &word4);
			Word4 operator^(const Word4 &word4);
			Word4 operator|(const Word4 &word4);

			Word4 &operator<<=(char imm);
			Word4 &operator>>=(char imm);

			Word4 operator<<(char imm);
			Word4 operator>>(char imm);
		};

		typedef Word4 Short4;

		class Dword2 : public Qword
		{
		public:
			Dword2();
			Dword2(Dword2 &dword2);

			operator OperandMMREG() const;

			Dword2 &operator=(const Dword2 &dword2);

			Dword2 &operator+=(const Dword2 &dword2);
			Dword2 &operator-=(const Dword2 &dword2);
			Dword2 &operator<<=(const Qword &qword);
			Dword2 &operator>>=(const Qword &qword);
			Dword2 &operator&=(const Dword2 &dword2);
			Dword2 &operator^=(const Dword2 &dword2);
			Dword2 &operator|=(const Dword2 &dword2);

			Dword2 operator+(const Dword2 &dword2);
			Dword2 operator-(const Dword2 &dword2);
			Dword2 operator<<(const Qword &qword);
			Dword2 operator>>(const Qword &qword);
			Dword2 operator&(const Dword2 &dword2);
			Dword2 operator^(const Dword2 &dword2);
			Dword2 operator|(const Dword2 &dword2);

			Dword2 &operator<<=(char imm);
			Dword2 &operator>>=(char imm);

			Dword2 operator<<(char imm);
			Dword2 operator>>(char imm);
		};

		typedef Dword2 Int2;

		class Float : public Variable
		{
		public:
			Float();
			Float(Float &f);

			operator OperandXMMREG() const;

			Float &operator=(const Float &f);

			Float &operator+=(const Float &f);
			Float &operator-=(const Float &f);
			Float &operator*=(const Float &f);
			Float &operator/=(const Float &f);
		//	Float &operator&=(const Float &f);   // NOTE: No andss instruction, andps gives trouble
		//	Float &operator^=(const Float &f);
		//	Float &operator|=(const Float &f);

			Float operator+(const Float &f);
			Float operator-(const Float &f);
			Float operator*(const Float &f);
			Float operator/(const Float &f);
		//	Float operator&(const Float &f);
		//	Float operator^(const Float &f);
		//	Float operator|(const Float &f);
		};

		class Float4 : public Variable
		{
		public:
			Float4();
			Float4(Float4 &float4);

			operator OperandXMMREG() const;

			Float4 &operator=(const Float4 &float4);

			Float4 &operator+=(const Float4 &float4);
			Float4 &operator-=(const Float4 &float4);
			Float4 &operator*=(const Float4 &float4);
			Float4 &operator/=(const Float4 &float4);
			Float4 &operator&=(const Float4 &float4);
			Float4 &operator^=(const Float4 &float4);
			Float4 &operator|=(const Float4 &float4);

			Float4 operator+(const Float4 &float4);
			Float4 operator-(const Float4 &float4);
			Float4 operator*(const Float4 &float4);
			Float4 operator/(const Float4 &float4);
			Float4 operator&(const Float4 &float4);
			Float4 operator^(const Float4 &float4);
			Float4 operator|(const Float4 &float4);
		};

		CodeGenerator();

		~CodeGenerator();

		void prologue(int functionArguments);
		void epilogue();
		OperandMEM32 argument(int i);

		using Emulator::free;
		void free(Variable &var);

	private:
		Dword arg;

		static int stack;
		static int stackTop;
		static Encoding *stackUpdate;

		// Active code generator
		static CodeGenerator *cg;
	};
}

#endif   // SoftWire_CodeGenerator_hpp
