#ifndef swShader_VertexProcessor_hpp
#define swShader_VertexProcessor_hpp

#include "Matrix.hpp"
#include "FIFOCache.hpp"
#include "VertexShader.hpp"
#include "String.hpp"

namespace swShader
{
	class Viewport;
	class VertexBuffer;

	class VertexProcessor
	{
		class State
		{
		public:
			State();
			State(const State &state);

			State &operator=(const State &state);

			~State();

			void setShaderFile(const char *shaderFile);
			void setPipelineState(int state);

			bool operator==(const State &state) const;
			bool operator!=(const State &state) const;

		private:
			void updateStamp();

			char *shaderFile;
			int pipelineState;
			int stamp;
		};

	public:
		VertexProcessor();

		~VertexProcessor();

		void setShader(const char *shaderFile);

		void setVertexBuffer(const VertexBuffer *VB);

		void setFloatConstant(int index, const float value[4]);
		void setIntegerConstant(int index, const int value[4]);
		void setBooleanConstant(int index, const bool boolean);

		void setFloatConstant(int index, const Vector &V);
		void setFloatConstant(int index, const Point &P);
		void setFloatConstant(int index, const Matrix &M);

		// Transformations
		void setModelMatrix(const Matrix &M);
		void setViewMatrix(const Matrix &V);
		void setBaseMatrix(const Matrix &B);

		// Projection
		void setFOV(float FOV);
		void setNearClip(float nearClip);
		void setFarClip(float farClip);

		// Lighting
		void setLightEnable(int light, bool lightEnable);
		void setSpecularEnable(int light, bool specularEnable);

		void setAmbientLight(const Color<float> &ambientLight);
		void setLightPosition(int light, const Point &lightPosition);
		void setLightColor(int light, const Color<float> &lightColor);
		void setLightAttenuation(int light, float constant, float linear, float quadratic);
		void setLightRange(int light, float lightRange);

		void setMaterialEmission(const Color<float> &emission);
		void setMaterialAmbient(const Color<float> &materialAmbient);
		void setMaterialDiffuse(const Color<float> &diffuseColor);
		void setMaterialSpecular(const Color<float> &specularColor);
		void setMaterialShininess(const float specularPower[4]);

	protected:
		void setViewport(const Viewport *viewport);

		const Matrix &getModelTransform(const Viewport *viewport);
		const Matrix &getViewTransform(const Viewport *viewport);

		FVFFlags getOutputFormat();
		XVertex processVertex(int i);
		void clearCache();

		bool isFixedFunction();
		bool specularEnabled();

	private:
		const State &status() const;
		void updateTransform(const Viewport *viewport = 0);
		void update();

		const VertexBuffer *vertexBuffer;

		char *shaderFile;
		FIFOCache<State, VertexShader> *shaderCache;
		VertexShader *shader;

		int cacheIndex[32];
		XVertex vertexCache[32];

		float W;   // Viewport width
		float H;   // Viewport height

		float tanFOV;
		float nearClip;
		float farClip;

		Matrix M;	// Model/Geometry/World matrix
		Matrix V;	// View/Camera/Eye matrix
		Matrix B;	// Base matrix
		Matrix P;	// Projection matrix

		Matrix PB;		// P * B
		Matrix PBV;		// P * B * V
		Matrix PBVM;	// P * B * V * M

		bool lightEnable[8];
		bool specularEnable[8];
		Point lightPosition[8];   // World coordinates

		// Update hierarchy
		bool updateProcessor;
			bool updateMatrix;
				bool updateModelMatrix;
				bool updateViewMatrix;
				bool updateBaseMatrix;
				bool updateProjectionMatrix;
			bool updateShader;
			bool updateLighting;   // Light position only, not camera
	};
}

#endif   // swShader_VertexProcessor_hpp