#ifndef swShader_Operand_hpp
#define swShader_Operand_hpp

#undef VOID

namespace swShader
{
	struct Operand
	{
		enum Type
		{
			VOID,

			ADDRESS_REGISTER,					// vs2.0
			CONSTANT_BOOLEAN_REGISTER,			// vs2.0
			CONSTANT_INTEGER_REGISTER,			// vs2.0
			COLOR_REGISTER,						// ps2.0
			CONSTANT_FLOAT_REGISTER,			// ps2.0/vs2.0
			INPUT_REGISTER,						// vs2.0
			INPUT_TEXTURE_COORDINATE_REGISTER,	// ps2.0
			LOOP_COUNTER_REGISTER,				// vs2.0
			SAMPLER_REGISTER,					// ps2.0
			TEMPORARY_REGISTER,					// ps2.0

			DIFFUSE_SPECULAR_REGISTER,			// vs2.0
			POSITION_REGISTER,					// vs2.0
			POINT_SIZE_REGISTER,				// vs2.0
			TEXTURE_COORDINATE_REGISTER,		// vs2.0
			FOG_REGISTER,						// vs2.0
			OUTPUT_COLOR_REGISTER,				// ps2.0
			OUTPUT_DEPTH_REGISTER,				// ps2.0

			CONSTANT_FLOAT_LITERAL,				// ps2.0

			// Internal usage
			INTERNAL_REGISTER
		};

		enum SourceModifier
		{
			NONE,
			NEGATE								// ps2.0/vs2.0
		};

		struct Register
		{
			Register(Type type = Operand::VOID, int index = 0)
			{
				this->type = type;
				this->index = index;
			}

			Register(const Register &reg)
			{
				type = reg.type;
				index = reg.index;
			}

			Register(const Operand &op)
			{
				type = op.type;
				index = op.index;
			}

			operator==(const Register &reg) const
			{
				 return type == reg.type && index == reg.index;
			}

			Type type;

			union
			{
				int index;
				int integer;
				bool boolean;
				float value;
			};

			bool dcl;
		};

		enum Component
		{
			M = -1,   // Masked out

			X = 0,   // Copy x component
			Y = 1,   // Copy y component
			Z = 2,   // Copy z component
			W = 3,   // Copy w component
		};

		Operand(const Operand &op)
		{
			mod = op.mod;
			reg = op.reg;
			sel = op.sel;
		}

		Operand(const Register &reg)
		{
			mod = NONE;
			this->reg = reg;
			sel.x = X;
			sel.y = Y;
			sel.z = Z;
			sel.w = W;
		}

		Operand(SourceModifier mod = NONE, Register reg = VOID, Component a = X, Component b = Y, Component c = Z, Component d = W)
		{
			this->mod = mod;
			this->reg = reg;
			this->sel.x = a;
			this->sel.y = b;
			this->sel.z = c;
			this->sel.w = d;
		}

		Operand(Type type, int index = 0)
		{
			mod = NONE;
			this->type = type;
			this->index = index;
			sel.x = X;
			sel.y = Y;
			sel.z = Z;
			sel.w = W;
		}

		Operand &operator=(const Operand &op)
		{
			mod = op.mod;
			reg = op.reg;
			sel = op.sel;

			return *this;
		}

		operator==(const Register &reg) const
		{
			return this->reg.type == reg.type && this->reg.index == reg.index;
		}

		unsigned char swizzle() const
		{
			return (sel.x << 0) |
			       (sel.y << 2) |
			       (sel.z << 4) |
			       (sel.w << 6);
		}

		SourceModifier mod;

		union
		{
			struct
			{
				Register reg;
			};

			struct
			{
				Type type;

				union
				{
					int index;
					int integer;
					bool boolean;
					float value;
				};
			};
		};

		struct Select
		{
			Component x : 8;
			Component y : 8;
			Component z : 8;
			Component w : 8;

			operator bool() const
			{
				if(x == M && y == M && z == M && w == M)
				{
					return false;
				}

				if(x == X && y == Y && z == Z && w == W)
				{
					return false;
				}

				return true;
			}

			bool operator==(const Select &mask) const
			{
				return x == mask.x &&
				       y == mask.y &&
				       z == mask.z &&
				       w == mask.w;
			}

			bool operator!=(const Select &mask) const
			{
				return !(*this == mask);
			}
		} sel;

		const char *string() const;
	};

	const Operand voidOperand;   // Default constructor creates void

	const Operand::Select xMask = {Operand::X, Operand::M, Operand::M, Operand::M};
	const Operand::Select yMask = {Operand::M, Operand::Y, Operand::M, Operand::M};
	const Operand::Select zMask = {Operand::M, Operand::M, Operand::Z, Operand::M};
	const Operand::Select wMask = {Operand::M, Operand::M, Operand::M, Operand::W};
	const Operand::Select xyMask = {Operand::X, Operand::Y, Operand::M, Operand::M};
	const Operand::Select xzMask = {Operand::X, Operand::M, Operand::Z, Operand::M};
	const Operand::Select xwMask = {Operand::X, Operand::M, Operand::M, Operand::W};
	const Operand::Select yzMask = {Operand::M, Operand::Y, Operand::Z, Operand::M};
	const Operand::Select ywMask = {Operand::M, Operand::Y, Operand::M, Operand::W};
	const Operand::Select zwMask = {Operand::M, Operand::M, Operand::Z, Operand::W};
	const Operand::Select xyzMask = {Operand::X, Operand::Y, Operand::Z, Operand::M};
	const Operand::Select xywMask = {Operand::X, Operand::Y, Operand::M, Operand::W};
	const Operand::Select xzwMask = {Operand::X, Operand::M, Operand::Z, Operand::W};
	const Operand::Select yzwMask = {Operand::M, Operand::Y, Operand::Z, Operand::W};
	const Operand::Select xyzwMask = {Operand::X, Operand::Y, Operand::Z, Operand::W};
}

#endif