// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef sw_PixelProcessor_hpp
#define sw_PixelProcessor_hpp

#include "Context.hpp"
#include "RoutineCache.hpp"

namespace sw
{
	class PixelShader;
	class Rasterizer;
	struct Texture;
	struct DrawData;

	class PixelProcessor
	{
	public:
		struct States
		{
			unsigned int computeHash();

			int shaderID;

			bool depthOverride                        : 1;
			bool shaderContainsKill                   : 1;

			DepthCompareMode depthCompareMode         : BITS(DEPTH_LAST);
			AlphaCompareMode alphaCompareMode         : BITS(ALPHA_LAST);
			bool depthWriteEnable                     : 1;
			bool quadLayoutDepthBuffer                : 1;

			bool stencilActive                        : 1;
			StencilCompareMode stencilCompareMode     : BITS(STENCIL_LAST);
			StencilOperation stencilFailOperation     : BITS(OPERATION_LAST);
			StencilOperation stencilPassOperation     : BITS(OPERATION_LAST);
			StencilOperation stencilZFailOperation    : BITS(OPERATION_LAST);
			bool noStencilMask                        : 1;
			bool noStencilWriteMask                   : 1;
			bool stencilWriteMasked                   : 1;
			bool twoSidedStencil                      : 1;
			StencilCompareMode stencilCompareModeCCW  : BITS(STENCIL_LAST);
			StencilOperation stencilFailOperationCCW  : BITS(OPERATION_LAST);
			StencilOperation stencilPassOperationCCW  : BITS(OPERATION_LAST);
			StencilOperation stencilZFailOperationCCW : BITS(OPERATION_LAST);
			bool noStencilMaskCCW                     : 1;
			bool noStencilWriteMaskCCW                : 1;
			bool stencilWriteMaskedCCW                : 1;

			bool depthTestActive                      : 1;
			bool fogActive                            : 1;
			FogMode pixelFogMode                      : BITS(FOG_LAST);
			bool specularAdd                          : 1;
			bool occlusionEnabled                     : 1;
			bool wBasedFog                            : 1;
			bool perspective                          : 1;

			bool alphaBlendActive                     : 1;
			BlendFactor sourceBlendFactor             : BITS(BLEND_LAST);
			BlendFactor destBlendFactor               : BITS(BLEND_LAST);
			BlendOperation blendOperation             : BITS(BLENDOP_LAST);
			BlendFactor sourceBlendFactorAlpha        : BITS(BLEND_LAST);
			BlendFactor destBlendFactorAlpha          : BITS(BLEND_LAST);
			BlendOperation blendOperationAlpha        : BITS(BLENDOP_LAST);

			unsigned int colorWriteMask                       : RENDERTARGETS * 4;   // Four component bit masks
			Format targetFormat[RENDERTARGETS];
			bool writeSRGB                                    : 1;
			unsigned int multiSample                          : 3;
			unsigned int multiSampleMask                      : 4;
			TransparencyAntialiasing transparencyAntialiasing : BITS(TRANSPARENCY_LAST);
			bool centroid                                     : 1;

			LogicalOperation logicalOperation : BITS(LOGICALOP_LAST);

			Sampler::State sampler[TEXTURE_IMAGE_UNITS];
			TextureStage::State textureStage[8];

			struct Interpolant
			{
				unsigned char component : 4;
				unsigned char flat : 4;
				unsigned char project : 2;
				bool centroid : 1;
			};

			union
			{
				struct
				{
					Interpolant color[2];
					Interpolant texture[8];
					Interpolant fog;
				};

				Interpolant interpolant[MAX_FRAGMENT_INPUTS];
			};
		};

		struct State : States
		{
			State();

			bool operator==(const State &state) const;

			int colorWriteActive(int index) const
			{
				return (colorWriteMask >> (index * 4)) & 0xF;
			}

			bool alphaTestActive() const
			{
				return (alphaCompareMode != ALPHA_ALWAYS) || (transparencyAntialiasing != TRANSPARENCY_NONE);
			}

			bool pixelFogActive() const
			{
				return pixelFogMode != FOG_NONE;
			}

			unsigned int hash;
		};

		struct Stencil
		{
			int64_t testMaskQ;
			int64_t referenceMaskedQ;
			int64_t referenceMaskedSignedQ;
			int64_t writeMaskQ;
			int64_t invWriteMaskQ;
			int64_t referenceQ;

			void set(int reference, int testMask, int writeMask)
			{
				referenceQ = replicate(reference);
				testMaskQ = replicate(testMask);
				writeMaskQ = replicate(writeMask);
				invWriteMaskQ = ~writeMaskQ;
				referenceMaskedQ = referenceQ & testMaskQ;
				referenceMaskedSignedQ = replicate((reference + 0x80) & 0xFF & testMask);
			}

			static int64_t replicate(int b)
			{
				int64_t w = b & 0xFF;

				return (w << 0) | (w << 8) | (w << 16) | (w << 24) | (w << 32) | (w << 40) | (w << 48) | (w << 56);
			}
		};

		struct Fog
		{
			float4 scale;
			float4 offset;
			word4 color4[3];
			float4 colorF[3];
			float4 densityE;
			float4 density2E;
		};

		struct Factor
		{
			word4 textureFactor4[4];

			word4 alphaReference4;

			word4 blendConstant4W[4];
			float4 blendConstant4F[4];
			word4 invBlendConstant4W[4];
			float4 invBlendConstant4F[4];
		};

	public:
		typedef void (*RoutinePointer)(const Primitive *primitive, int count, int thread, DrawData *draw);

		PixelProcessor(Context *context);

		virtual ~PixelProcessor();

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

		void setUniformBuffer(int index, sw::Resource* buffer, int offset);
		void lockUniformBuffers(byte** u, sw::Resource* uniformBuffers[]);

		void setRenderTarget(int index, Surface *renderTarget);
		void setDepthBuffer(Surface *depthBuffer);
		void setStencilBuffer(Surface *stencilBuffer);

		void setTexCoordIndex(unsigned int stage, int texCoordIndex);
		void setStageOperation(unsigned int stage, TextureStage::StageOperation stageOperation);
		void setFirstArgument(unsigned int stage, TextureStage::SourceArgument firstArgument);
		void setSecondArgument(unsigned int stage, TextureStage::SourceArgument secondArgument);
		void setThirdArgument(unsigned int stage, TextureStage::SourceArgument thirdArgument);
		void setStageOperationAlpha(unsigned int stage, TextureStage::StageOperation stageOperationAlpha);
		void setFirstArgumentAlpha(unsigned int stage, TextureStage::SourceArgument firstArgumentAlpha);
		void setSecondArgumentAlpha(unsigned int stage, TextureStage::SourceArgument secondArgumentAlpha);
		void setThirdArgumentAlpha(unsigned int stage, TextureStage::SourceArgument thirdArgumentAlpha);
		void setFirstModifier(unsigned int stage, TextureStage::ArgumentModifier firstModifier);
		void setSecondModifier(unsigned int stage, TextureStage::ArgumentModifier secondModifier);
		void setThirdModifier(unsigned int stage, TextureStage::ArgumentModifier thirdModifier);
		void setFirstModifierAlpha(unsigned int stage, TextureStage::ArgumentModifier firstModifierAlpha);
		void setSecondModifierAlpha(unsigned int stage, TextureStage::ArgumentModifier secondModifierAlpha);
		void setThirdModifierAlpha(unsigned int stage, TextureStage::ArgumentModifier thirdModifierAlpha);
		void setDestinationArgument(unsigned int stage, TextureStage::DestinationArgument destinationArgument);
		void setConstantColor(unsigned int stage, const Color<float> &constantColor);
		void setBumpmapMatrix(unsigned int stage, int element, float value);
		void setLuminanceScale(unsigned int stage, float value);
		void setLuminanceOffset(unsigned int stage, float value);

		void setTextureFilter(unsigned int sampler, FilterType textureFilter);
		void setMipmapFilter(unsigned int sampler, MipmapType mipmapFilter);
		void setGatherEnable(unsigned int sampler, bool enable);
		void setAddressingModeU(unsigned int sampler, AddressingMode addressingMode);
		void setAddressingModeV(unsigned int sampler, AddressingMode addressingMode);
		void setAddressingModeW(unsigned int sampler, AddressingMode addressingMode);
		void setReadSRGB(unsigned int sampler, bool sRGB);
		void setMipmapLOD(unsigned int sampler, float bias);
		void setBorderColor(unsigned int sampler, const Color<float> &borderColor);
		void setMaxAnisotropy(unsigned int sampler, float maxAnisotropy);
		void setHighPrecisionFiltering(unsigned int sampler, bool highPrecisionFiltering);
		void setSwizzleR(unsigned int sampler, SwizzleType swizzleR);
		void setSwizzleG(unsigned int sampler, SwizzleType swizzleG);
		void setSwizzleB(unsigned int sampler, SwizzleType swizzleB);
		void setSwizzleA(unsigned int sampler, SwizzleType swizzleA);
		void setBaseLevel(unsigned int sampler, int baseLevel);
		void setMaxLevel(unsigned int sampler, int maxLevel);
		void setMinLod(unsigned int sampler, float minLod);
		void setMaxLod(unsigned int sampler, float maxLod);

		void setWriteSRGB(bool sRGB);
		void setDepthBufferEnable(bool depthBufferEnable);
		void setDepthCompare(DepthCompareMode depthCompareMode);
		void setAlphaCompare(AlphaCompareMode alphaCompareMode);
		void setDepthWriteEnable(bool depthWriteEnable);
		void setAlphaTestEnable(bool alphaTestEnable);
		void setCullMode(CullMode cullMode);
		void setColorWriteMask(int index, int rgbaMask);

		void setColorLogicOpEnabled(bool colorLogicOpEnabled);
		void setLogicalOperation(LogicalOperation logicalOperation);

		void setStencilEnable(bool stencilEnable);
		void setStencilCompare(StencilCompareMode stencilCompareMode);
		void setStencilReference(int stencilReference);
		void setStencilMask(int stencilMask);
		void setStencilFailOperation(StencilOperation stencilFailOperation);
		void setStencilPassOperation(StencilOperation stencilPassOperation);
		void setStencilZFailOperation(StencilOperation stencilZFailOperation);
		void setStencilWriteMask(int stencilWriteMask);
		void setTwoSidedStencil(bool enable);
		void setStencilCompareCCW(StencilCompareMode stencilCompareMode);
		void setStencilReferenceCCW(int stencilReference);
		void setStencilMaskCCW(int stencilMask);
		void setStencilFailOperationCCW(StencilOperation stencilFailOperation);
		void setStencilPassOperationCCW(StencilOperation stencilPassOperation);
		void setStencilZFailOperationCCW(StencilOperation stencilZFailOperation);
		void setStencilWriteMaskCCW(int stencilWriteMask);

		void setTextureFactor(const Color<float> &textureFactor);
		void setBlendConstant(const Color<float> &blendConstant);

		void setFillMode(FillMode fillMode);
		void setShadingMode(ShadingMode shadingMode);

		void setAlphaBlendEnable(bool alphaBlendEnable);
		void setSourceBlendFactor(BlendFactor sourceBlendFactor);
		void setDestBlendFactor(BlendFactor destBlendFactor);
		void setBlendOperation(BlendOperation blendOperation);

		void setSeparateAlphaBlendEnable(bool separateAlphaBlendEnable);
		void setSourceBlendFactorAlpha(BlendFactor sourceBlendFactorAlpha);
		void setDestBlendFactorAlpha(BlendFactor destBlendFactorAlpha);
		void setBlendOperationAlpha(BlendOperation blendOperationAlpha);

		void setAlphaReference(float alphaReference);

		void setGlobalMipmapBias(float bias);

		void setFogStart(float start);
		void setFogEnd(float end);
		void setFogColor(Color<float> fogColor);
		void setFogDensity(float fogDensity);
		void setPixelFogMode(FogMode fogMode);

		void setPerspectiveCorrection(bool perspectiveCorrection);

		void setOcclusionEnabled(bool enable);

	protected:
		const State update() const;
		Routine *routine(const State &state);
		void setRoutineCacheSize(int routineCacheSize);

		// Shader constants
		word4 cW[8][4];
		float4 c[FRAGMENT_UNIFORM_VECTORS];
		int4 i[16];
		bool b[16];

		// Other semi-constants
		Stencil stencil;
		Stencil stencilCCW;
		Fog fog;
		Factor factor;

	private:
		struct UniformBufferInfo
		{
			UniformBufferInfo();

			Resource* buffer;
			int offset;
		};
		UniformBufferInfo uniformBufferInfo[MAX_UNIFORM_BUFFER_BINDINGS];

		void setFogRanges(float start, float end);

		Context *const context;

		RoutineCache<State> *routineCache;
	};
}

#endif   // sw_PixelProcessor_hpp
