#ifndef SoftWire_CodeGenerator_hpp
#define SoftWire_CodeGenerator_hpp

#include "Emulator.hpp"

namespace SoftWire
{
	class CodeGenerator : public Emulator
	{
		class Variable
		{
		public:
			virtual ~Variable();

		protected:
			Variable();

			static int stack;
			int reference;
		};

	public:
		class Byte : public Variable
		{
		public:
			Byte();
			Byte(char byte);
			Byte(Byte &byte);

			operator OperandREG8() const;

			Byte &operator=(const Byte &byte);

			Byte &operator+=(const Byte &byte);
			Byte &operator-=(const Byte &byte);
			Byte &operator*=(const Byte &byte);
			Byte &operator/=(const Byte &byte);
			Byte &operator%=(const Byte &byte);
			Byte &operator<<=(const Byte &byte);
			Byte &operator>>=(const Byte &byte);
			Byte &operator&=(const Byte &byte);
			Byte &operator^=(const Byte &byte);
			Byte &operator|=(const Byte &byte);

			Byte operator+(const Byte &byte);
			Byte operator-(const Byte &byte);
			Byte operator*(const Byte &byte);
			Byte operator/(const Byte &byte);
			Byte operator%(const Byte &byte);
			Byte operator<<(const Byte &byte);
			Byte operator>>(const Byte &byte);
			Byte operator&(const Byte &byte);
			Byte operator^(const Byte &byte);
			Byte operator|(const Byte &byte);

			Byte &operator+=(char byte);
			Byte &operator-=(char byte);
			Byte &operator*=(char byte);
			Byte &operator/=(char byte);
			Byte &operator%=(char byte);
			Byte &operator<<=(char byte);
			Byte &operator>>=(char byte);
			Byte &operator&=(char byte);
			Byte &operator^=(char byte);
			Byte &operator|=(char byte);

			Byte operator+(char byte);
			Byte operator-(char byte);
			Byte operator*(char byte);
			Byte operator/(char byte);
			Byte operator%(char byte);
			Byte operator<<(char byte);
			Byte operator>>(char byte);
			Byte operator&(char byte);
			Byte operator^(char byte);
			Byte operator|(char byte);
		};

		typedef Byte Char;

		class Word : public Variable
		{
		public:
			Word();
			Word(short word);
			Word(Word &word);

			operator OperandREG16() const;

			Word &operator=(const Word &word);

			Word &operator+=(const Word &word);
			Word &operator-=(const Word &word);
			Word &operator*=(const Word &word);
			Word &operator/=(const Word &word);
			Word &operator%=(const Word &word);
			Word &operator<<=(const Word &word);
			Word &operator>>=(const Word &word);
			Word &operator&=(const Word &word);
			Word &operator^=(const Word &word);
			Word &operator|=(const Word &word);

			Word operator+(const Word &word);
			Word operator-(const Word &word);
			Word operator*(const Word &word);
			Word operator/(const Word &word);
			Word operator%(const Word &word);
			Word operator<<(const Word &word);
			Word operator>>(const Word &word);
			Word operator&(const Word &word);
			Word operator^(const Word &word);
			Word operator|(const Word &word);

			Word &operator+=(short word);
			Word &operator-=(short word);
			Word &operator*=(short word);
			Word &operator/=(short word);
			Word &operator%=(short word);
			Word &operator<<=(short word);
			Word &operator>>=(short word);
			Word &operator&=(short word);
			Word &operator^=(short word);
			Word &operator|=(short word);

			Word operator+(short word);
			Word operator-(short word);
			Word operator*(short word);
			Word operator/(short word);
			Word operator%(short word);
			Word operator<<(short word);
			Word operator>>(short word);
			Word operator&(short word);
			Word operator^(short word);
			Word operator|(short word);
		};

		typedef Word Short;

		class Dword : public Variable
		{
		public:
			Dword();
			Dword(int dword);
			Dword(Dword &dword);

			operator OperandREG32() const;

			Dword &operator=(const Dword &dword);

			Dword &operator+=(const Dword &dword);
			Dword &operator-=(const Dword &dword);
			Dword &operator*=(const Dword &dword);
			Dword &operator/=(const Dword &dword);
			Dword &operator%=(const Dword &dword);
			Dword &operator<<=(const Dword &dword);
			Dword &operator>>=(const Dword &dword);
			Dword &operator&=(const Dword &dword);
			Dword &operator^=(const Dword &dword);
			Dword &operator|=(const Dword &dword);

			Dword operator+(const Dword &dword);
			Dword operator-(const Dword &dword);
			Dword operator*(const Dword &dword);
			Dword operator/(const Dword &dword);
			Dword operator%(const Dword &dword);
			Dword operator<<(const Dword &dword);
			Dword operator>>(const Dword &dword);
			Dword operator&(const Dword &dword);
			Dword operator^(const Dword &dword);
			Dword operator|(const Dword &dword);

			Dword &operator+=(int dword);
			Dword &operator-=(int dword);
			Dword &operator*=(int dword);
			Dword &operator/=(int dword);
			Dword &operator%=(int dword);
			Dword &operator<<=(int dword);
			Dword &operator>>=(int dword);
			Dword &operator&=(int dword);
			Dword &operator^=(int dword);
			Dword &operator|=(int dword);

			Dword operator+(int dword);
			Dword operator-(int dword);
			Dword operator*(int dword);
			Dword operator/(int dword);
			Dword operator%(int dword);
			Dword operator<<(int dword);
			Dword operator>>(int dword);
			Dword operator&(int dword);
			Dword operator^(int dword);
			Dword operator|(int dword);
		};

		typedef Dword Int;

		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();

	private:
		// Overloaded to set active register manager
		virtual Encoding *x86(int instructionID,
		                      const Operand &firstOperand = Operand::OPERAND_VOID,
		                      const Operand &secondOperand = Operand::OPERAND_VOID,
		                      const Operand &thirdOperand = Operand::OPERAND_VOID);

		// Active code generator
		static CodeGenerator *cg;
	};
}

#endif   // SoftWire_CodeGenerator_hpp
