#include "Sampler.hpp"

#include "Error.hpp"
#include "Texture.hpp"
#include "MetaMacro.hpp"

namespace swShader
{
	Sampler::Sampler()
	{
		init(0);
	}

	Sampler::~Sampler()
	{
		if(texture)
		{
			texture->release();
			texture = 0;
		}
	}

	void Sampler::init(int stage)
	{
		texture = 0;
		constantColorShort = 0;
		constantColorFloat = 0;

	//	borderColor = Color<float>::BLACK;
		textureFilter = FILTER_LINEAR;
		addressingMode = ADDRESSING_WRAP;
	//	mipmapFilter = FILTER_POINT;
	//	mipmapBias = 0;
	//	gamma = 2.2f;

		stageOperation = (stage == 0 ? STAGE_MODULATE : STAGE_DISABLE);
		firstArgument = SOURCE_TEXTURE;
		secondArgument = SOURCE_CURRENT;
		thirdArgument = SOURCE_VOID;
		firstModifier = MODIFIER_COLOR;
		secondModifier = MODIFIER_COLOR;
		thirdModifier = MODIFIER_COLOR;
		destinationArgument = DESTINATION_CURRENT;

		texCoordIndex = stage;
	}

	void Sampler::setTextureMap(Texture *texture)
	{
		if(!texture) throw Error("Invalid texture");

		if(this->texture) this->texture->release();
		this->texture = texture;
		texture->aquire();
	}

	void Sampler::releaseTexture()
	{
		if(texture)
		{
			texture->release();
			texture = 0;
		}
	}

	void Sampler::setConstantColor(const Color<float> &constantColor)
	{
		constantColorFloat = constantColor;
		constantColorShort = constantColor;
	}

	bool Sampler::setTexCoordIndex(int texCoordIndex)
	{
		if(texCoordIndex < 0 || texCoordIndex >= 8) throw Error("Texture stage index out of [0, 7] range: %d", texCoordIndex);

		bool update = (this->texCoordIndex != texCoordIndex);
		this->texCoordIndex = texCoordIndex;
		return update;
	}

	bool Sampler::setStageOperation(StageOperation stageOperation)
	{
		bool update = (this->stageOperation != stageOperation);
		this->stageOperation = stageOperation;
		return update;
	}

	bool Sampler::setFirstArgument(SourceArgument firstArgument)
	{
		bool update = (this->firstArgument != firstArgument);
		this->firstArgument = firstArgument;
		return update;
	}

	bool Sampler::setSecondArgument(SourceArgument secondArgument)
	{
		bool update = (this->secondArgument != secondArgument);
		this->secondArgument = secondArgument;
		return update;
	}

	bool Sampler::setThirdArgument(SourceArgument thirdArgument)
	{
		bool update = (this->thirdArgument != thirdArgument);
		this->thirdArgument = thirdArgument;
		return update;
	}

	bool Sampler::setFirstModifier(ArgumentModifier firstModifier)
	{
		bool update = (this->firstModifier != firstModifier);
		this->firstModifier = firstModifier;
		return update;
	}

	bool Sampler::setSecondModifier(ArgumentModifier secondModifier)
	{
		bool update = (this->secondModifier != secondModifier);
		this->secondModifier = secondModifier;
		return update;
	}

	bool Sampler::setThirdModifier(ArgumentModifier thirdModifier)
	{
		bool update = (this->thirdModifier != thirdModifier);
		this->thirdModifier = thirdModifier;
		return update;
	}

	bool Sampler::setDestinationArgument(DestinationArgument destinationArgument)
	{
		bool update = (this->destinationArgument != destinationArgument);
		this->destinationArgument = destinationArgument;
		return update;
	}

	bool Sampler::setTextureFilter(FilterType textureFilter)
	{
		bool update = (this->textureFilter != textureFilter);
		this->textureFilter = textureFilter;
		return update;
	}

	bool Sampler::setAddressingMode(AddressingMode addressingMode)
	{
		bool update = (this->addressingMode != addressingMode);
		this->addressingMode = addressingMode;
		return update;
	}

	int Sampler::status() const
	{
		const int a = 0 + BITS(FILTER_LAST);		// filterMode	
		const int b = a + BITS(ADDRESSING_LAST);	// addressingMode
		const int c = b + BITS(STAGE_LAST);			// stageOperation
		const int d = c + BITS(SOURCE_LAST);		// firstArgument
		const int e = d + BITS(SOURCE_LAST);		// secondArgument
		const int f = e + BITS(SOURCE_LAST);		// thirdArgument
		const int g = f + BITS(MODIFIER_LAST);		// firstModifier
		const int h = g + BITS(MODIFIER_LAST);		// secondModifier
		const int i = h + BITS(MODIFIER_LAST);		// thirdModifier
		const int j = i + BITS(DESTINATION_LAST);	// destArgument
		const int k = j + BITS(8);					// texCoordIndex

		// Test whether status fits in 32-bit
		META_ASSERT(k <= 32);

		return textureFilter		<< 0 |
		       addressingMode		<< a |
		       stageOperation		<< b |
		       firstArgument		<< c |
		       secondArgument		<< d |
		       thirdArgument		<< e |
	           firstModifier		<< f |
			   secondModifier		<< g |
			   thirdModifier		<< h |
		       destinationArgument	<< i |
		       texCoordIndex		<< j;
	}
}