#ifndef swShader_VS_2_0Assembler_hpp
#define swShader_VS_2_0Assembler_hpp

#include "VertexShader.hpp"
#include "Context.hpp"

#include "Instruction.hpp"
#include "Stream.hpp"
#include "Types.hpp"

namespace swShader
{
	class Stream;
	struct Operand;
	struct XVertex;

	class VS_2_0Assembler : protected Context, public VertexShader
	{
	public:
		VS_2_0Assembler();

		virtual ~VS_2_0Assembler();

		void process(int i);

		void setFloatConstant(int index, const float value[4]);
		void setIntegerConstant(int index, const int integer[4]);
		void setBooleanConstant(int index, bool boolean);

		void setPositionStream(const float4 *position, int usageIndex = 0);
		void setBlendWeightStream(const float4 *blendWeight);
		void setBlendIndicesStream(const byte4 *blendIndices);
		void setNormalStream(const float4 *normal, int usageIndex = 0);
		void setPSizeStream(const float *pSize);
		void setTexCoordStream(const float4 *texCoord, int usageIndex = 0);
		void setTangentStream(const float4 *tangent);
		void setBinormalStream(const float4 *binormal);
		void setTessFactorStream(const float *tessFactor);
		void setPositiontStream(const float4 *positiont);
		void setColorStream(const Color<byte> *color, int usageIndex = 0);
		void setFogStream(const float *fog);
		void setDepthStream(const float *depth);
		void setSampleStream(const float4 *sample);

		void setOutputVertex(XVertex *output);
		FVFFlags getOutputFormat();

	protected:
		void setMnemonic(Instruction::Mnemonic mnemonic);
		void setUsageIndex(int usage_index);
		void setModifier(Instruction::Modifier modifier);
		void setDestination(const Operand &operand);
		void setSource0(const Operand &operand);
		void setSource1(const Operand &operand);
		void setSource2(const Operand &operand);
		void setSource3(const Operand &operand);

		void newInstruction();

		void encode();

		// Shader registers
		static float4 v[16];
		static float4 r[16];
		static float4 c[256];
		static int4 i[16];
		static bool b[16];
		static int a0;
		static int aL;
		static float4 oD[2];
		static float oFog;
		static float4 oPos;
		static float oPts;
		static float4 oT[8];

		// Register storage for internal use
		static float4 internal[8];

		bool vDcl[16];

		Instruction *intermediate;
		Instruction *instruction;

	private:
		void dump();

		void setupStreams();
		void readInput();
		void shader();
		void writeOutput();

		void (*code)();

		FVFFlags oFVF;   // Valid output components

		static Operand tmp0;
		static Operand tmp1;
		static Operand tmp2;
		static Operand tmp3;
		static Operand tmp4;
		static Operand tmp5;
		static Operand tmp6;
		static Operand tmp7;

		void *reference(const Operand &reg);
		void checkDcl(const Operand &op);
		void free(const Operand &tmp);
		void freeTemps();

		Stream input[16];

		const float4 *position[2];
		const float4 *blendWeight;
		const byte4 *blendIndices;
		const float4 *normal[2];
		const float *pSize;
		const float4 *texCoord[8];
		const float4 *tangent;
		const float4 *binormal;
		const float *tessFactor;
		const float4 *positiont;
		const Color<byte> *color[2];
		const float *fog;
		const float *depth;
		const float4 *sample;

		static int n;
		XVertex *output;

		const SoftWire::OperandXMMREG r128(const Operand &reg, int next = 0);
		const SoftWire::OperandXMMREG x128(const Operand &reg, int next = 0);
		const SoftWire::OperandR_M128 m128(const Operand &r_m, int next = 0);
		const SoftWire::OperandXMM32 xmm32(const Operand &r_m, int next = 0);

		const SoftWire::OperandXMMREG r128(const SoftWire::OperandREF &ref);
		const SoftWire::OperandXMMREG x128(const SoftWire::OperandREF &ref);
		const SoftWire::OperandR_M128 m128(const SoftWire::OperandREF &ref);
		const SoftWire::OperandXMM32 xmm32(const SoftWire::OperandREF &ref);

		void free(const SoftWire::OperandREF &ref);

		typedef Operand &Dst;
		typedef Operand &Dest;
		typedef const Operand &Src;
		typedef const Operand &Src0;
		typedef const Operand &Src1;
		typedef const Operand &Src2;
		typedef const Operand &FValue1;
		typedef const Operand &FValue2;
		typedef const Operand &FValue3;
		typedef const Operand &FValue4;
		typedef const Operand &IntegerValue1;
		typedef const Operand &IntegerValue2;
		typedef const Operand &IntegerValue3;
		typedef const Operand &IntegerValue4;
		typedef const Operand &BooleanValue;
		typedef const Operand &BoolRegister;
		typedef const Operand &AL;
		typedef const Operand &IntegerRegister;
		typedef const Operand &Label;
		typedef Operand &Tmp;

		void VS_2_0();
		void DCL_POSITION(Dest);
		void DCL_BLENDWEIGHT(Dest);
		void DCL_BLENDINDICES(Dest);
		void DCL_NORMAL(Dest);
		void DCL_PSIZE(Dest);
		void DCL_TEXCOORD(Dest);
		void DCL_TANGENT(Dest);
		void DCL_BINORMAL(Dest);
		void DCL_TESSFACTOR(Dest);
		void DCL_POSITIONT(Dest);
		void DCL_COLOR(Dest);
		void DCL_FOG(Dest);
		void DCL_DEPTH(Dest);
		void DCL_SAMPLE(Dest);
		void DEF(Dest, FValue1, FValue2, FValue3, FValue4);
		void DEFI(Dest, IntegerValue1, IntegerValue2, IntegerValue3, IntegerValue4);
		void DEFB(Dest, BooleanValue);
		void ABS(Dst, Src);
		void ADD(Dst, Src0, Src1);
		void CRS(Dst, Src0, Src1);
		void DP3(Dst, Src0, Src1);
		void DP4(Dst, Src0, Src1);
		void EXP(Dst, Src);
		void EXPP(Dst, Src);
		void FRC(Dst, Src);
		void LIT(Dst, Src);
		void LOG(Dst, Src);
		void LOGP(Dst, Src);
		void LRP(Dst, Src0, Src1, Src2);
		void M3X2(Dst, Src0, Src1);
		void M3X3(Dst, Src0, Src1);
		void M3X4(Dst, Src0, Src1);
		void M4X3(Dst, Src0, Src1);
		void M4X4(Dst, Src0, Src1);
		void MAD(Dst, Src0, Src1, Src2);
		void MAX(Dst, Src0, Src1);
		void MIN(Dst, Src0, Src1);
		void MOV(Dst, Src);
		void MOVA(Dst, Src);
		void MUL(Dst, Src0, Src1);
		void NOP();
		void NRM(Dst, Src);
		void POW(Dst, Src0, Src1);
		void RCP(Dst, Src);
		void RSQ(Dst, Src);
		void SGE(Dst, Src0, Src1);
		void SGN(Dst, Src0, Src1, Src2);
		void SINCOS(Dst, Src0, Src1, Src2);

		void CALL(Label);
		void CALLNZ(Label, BoolRegister);
		void ELSE();
		void ENDIF();
		void ENDLOOP();
		void ENDREP();
		void IF(BoolRegister);
		void LABEL(Label);
		void LOOP(AL, IntegerRegister);
		void REP(IntegerRegister);
		void RET();

		// Helper macro instructions
		void NEG(Tmp, Src);
		void SWIZZLE(Tmp, Src);
		void MASK(Dst, Tmp);
		void SAT(Dst, Tmp);

		void NEG_SWIZZLE(Tmp, Src);
		void SAT_MASK(Dst, Tmp);
		void SAT_MOV_X(Dst, Tmp);
		void SAT_MOV_XY(Dst, Tmp);
		void SAT_MOV_XYZ(Dst, Tmp);
		void SAT_MOV_XYZW(Dst, Tmp);
	};
}

#endif   // swShader_VS_2_0Assembler_hpp
