#include "ShaderParameter.h"


ShaderParameter::ShaderParameter(const std::string& inName, ID3DXEffect* inEffect, D3DXHANDLE inHandle, ShaderParameterType::Enumeration inType, unsigned int inArrayLength)
	: Name(inName)
	, Effect(inEffect)
	, Handle(inHandle)
	, Type(inType)
	, Parameter(NULL)
	, ArrayLength(inArrayLength)
	, Valid(true)
	, PointerParameter(false)
{
	switch (inType)
	{
	case ShaderParameterType::Texture1D:
		this->set_Texture1D(NULL);
		break;
	case ShaderParameterType::Texture2D:
		this->set_Texture2D(NULL);
		break;
	case ShaderParameterType::Texture3D:
		this->set_Texture3D(NULL);
		break;
	case ShaderParameterType::TextureCube:
		this->set_TextureCube(NULL);
		break;
	case ShaderParameterType::String:
		this->set_String("");
		break;
	case ShaderParameterType::Matrix:
		{
			D3DXMATRIX TheMatrix;
			::D3DXMatrixIdentity(&TheMatrix);
			this->set_Matrix(TheMatrix);
			break;
		}		
	case ShaderParameterType::Bool:
		this->set_Bool(false);
		break;
	case ShaderParameterType::Int:
		this->set_Int(0);
		break;
	case ShaderParameterType::Float4:
		this->set_Float4(D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f));
		break;
	case ShaderParameterType::Float3:
		this->set_Float3(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
		break;
	case ShaderParameterType::Float2:
		this->set_Float2(D3DXVECTOR2(0.0f, 0.0f));
		break;
	case ShaderParameterType::Float:
		this->set_Float(0.0f);
		break;
	case ShaderParameterType::IntArray:
		{
			std::vector<int> IntArray;
			for (unsigned int i=0; i<ArrayLength; ++i)
				IntArray.push_back(0);
			this->set_IntArray(IntArray);
		}
		break;
	case ShaderParameterType::Float4Array:
		{
			std::vector<D3DXVECTOR4> Float4Array;
			for (unsigned int i=0; i<ArrayLength; ++i)
				Float4Array.push_back(D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f));
			this->set_Float4Array(Float4Array);
		}
		break;
	case ShaderParameterType::Float3Array:
		{
			std::vector<D3DXVECTOR3> Float3Array;
			for (unsigned int i=0; i<ArrayLength; ++i)
				Float3Array.push_back(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
			this->set_Float3Array(Float3Array);
		}
		break;
	case ShaderParameterType::Float2Array:
		{
			std::vector<D3DXVECTOR2> Float2Array;
			for (unsigned int i=0; i<ArrayLength; ++i)
				Float2Array.push_back(D3DXVECTOR2(0.0f, 0.0f));
			this->set_Float2Array(Float2Array);
		}
		break;
	case ShaderParameterType::FloatArray:
		{
			std::vector<float> FloatArray;
			for (unsigned int i=0; i<ArrayLength; ++i)
				FloatArray.push_back(0.0f);
			this->set_FloatArray(FloatArray);
		}
		break;
	default:
		Type = inType;
		Valid = false;
	}
}


ShaderParameter::~ShaderParameter()
{
	this->FreeParameter();
}


ShaderParameterType::Enumeration ShaderParameter::get_Type() const
{
	return Type;
}


void ShaderParameter::set_Float4(const D3DXVECTOR4& inValue)
{
	if (Type!=ShaderParameterType::Float4 && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = new D3DXVECTOR4(inValue);
	Type = ShaderParameterType::Float4;
	PointerParameter = false;

	this->UpdateEffect();
}


D3DXVECTOR4 ShaderParameter::get_Float4() const
{
	return *static_cast<D3DXVECTOR4*>(Parameter);
}


void ShaderParameter::set_Float3(const D3DXVECTOR3& inValue)
{
	if (Type!=ShaderParameterType::Float3 && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = new D3DXVECTOR3(inValue);
	Type = ShaderParameterType::Float3;
	PointerParameter = false;

	this->UpdateEffect();
}


D3DXVECTOR3 ShaderParameter::get_Float3() const
{
	return *static_cast<D3DXVECTOR3*>(Parameter);
}


void ShaderParameter::set_Float2(const D3DXVECTOR2& inValue)
{
	if (Type!=ShaderParameterType::Float2 && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = new D3DXVECTOR2(inValue);
	Type = ShaderParameterType::Float2;
	PointerParameter = false;

	this->UpdateEffect();
}


D3DXVECTOR2 ShaderParameter::get_Float2() const
{
	return *static_cast<D3DXVECTOR2*>(Parameter);
}


void ShaderParameter::set_Float(float inValue)
{
	if (Type!=ShaderParameterType::Float && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = new float(inValue);
	Type = ShaderParameterType::Float;
	PointerParameter = false;

	this->UpdateEffect();
}


float ShaderParameter::get_Float() const
{
	return *static_cast<float*>(Parameter);
}


void ShaderParameter::set_Int(int inValue)
{
	if (Type!=ShaderParameterType::Int && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = new int(inValue);
	Type = ShaderParameterType::Int;
	PointerParameter = false;

	this->UpdateEffect();
}


int ShaderParameter::get_Int() const
{
	return *static_cast<int*>(Parameter);
}


void ShaderParameter::set_Bool(bool inValue)
{
	if (Type!=ShaderParameterType::Bool && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = new bool(inValue);
	Type = ShaderParameterType::Bool;
	PointerParameter = false;

	this->UpdateEffect();
}


bool ShaderParameter::get_Bool() const
{
	return *static_cast<bool*>(Parameter);
}


void ShaderParameter::set_String(const std::string& inValue)
{
	if (Type!=ShaderParameterType::String && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	char* NewString = new char[inValue.length()+1];
	for (unsigned int i=0; i<inValue.length(); ++i)
		NewString[i] = inValue[i];
	NewString[inValue.length()] = '\0';

	Parameter = NewString;
	Type = ShaderParameterType::String;
	PointerParameter = false;

	this->UpdateEffect();
}


std::string ShaderParameter::get_String() const
{
	return std::string( static_cast<char*>(Parameter) );
}


void ShaderParameter::set_Matrix(const D3DXMATRIX& inValue)
{
	if (Type!=ShaderParameterType::Matrix && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = new D3DXMATRIX(inValue);
	Type = ShaderParameterType::Matrix;
	PointerParameter = false;

	this->UpdateEffect();
}


D3DXMATRIX ShaderParameter::get_Matrix() const
{
	return *static_cast<D3DXMATRIX*>(Parameter);
}


void ShaderParameter::set_Texture1D(IDirect3DBaseTexture9* inValue)
{
	if (Type!=ShaderParameterType::Texture1D && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = inValue;
	Type = ShaderParameterType::Texture1D;
	PointerParameter = true;

	this->UpdateEffect();
}


IDirect3DBaseTexture9* ShaderParameter::get_Texture1D() const
{
	return static_cast<IDirect3DBaseTexture9*>(Parameter);
}


void ShaderParameter::set_Texture2D(IDirect3DBaseTexture9* inValue)
{
	if (Type!=ShaderParameterType::Texture2D && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = inValue;
	Type = ShaderParameterType::Texture2D;
	PointerParameter = true;

	this->UpdateEffect();
}


IDirect3DBaseTexture9* ShaderParameter::get_Texture2D() const
{
	return static_cast<IDirect3DBaseTexture9*>(Parameter);
}


void ShaderParameter::set_Texture3D(IDirect3DBaseTexture9* inValue)
{
	if (Type!=ShaderParameterType::Texture3D && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = inValue;
	Type = ShaderParameterType::Texture3D;
	PointerParameter = true;

	this->UpdateEffect();
}


IDirect3DBaseTexture9* ShaderParameter::get_Texture3D() const
{
	return static_cast<IDirect3DBaseTexture9*>(Parameter);
}


void ShaderParameter::set_TextureCube(IDirect3DBaseTexture9* inValue)
{
	if (Type!=ShaderParameterType::TextureCube && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	Parameter = inValue;
	Type = ShaderParameterType::TextureCube;
	PointerParameter = true;

	this->UpdateEffect();
}


IDirect3DBaseTexture9* ShaderParameter::get_TextureCube() const
{
	return static_cast<IDirect3DBaseTexture9*>(Parameter);
}


void ShaderParameter::set_Float4Array(const std::vector<D3DXVECTOR4>& inValue)
{
	if (Type!=ShaderParameterType::Float4Array && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	ArrayLength = static_cast<unsigned int>(inValue.size());
	D3DXVECTOR4* Float4Array = new D3DXVECTOR4[ArrayLength];
	for (unsigned int i=0; i<ArrayLength; ++i)
		Float4Array[i] = inValue[i];

	Parameter = Float4Array;
	Type = ShaderParameterType::Float4Array;
	PointerParameter = false;

	this->UpdateEffect();
}


std::vector<D3DXVECTOR4> ShaderParameter::get_Float4Array() const
{
	std::vector<D3DXVECTOR4> Float4VectorArray;
	D3DXVECTOR4* Float4Array = static_cast<D3DXVECTOR4*>(Parameter);
	for (unsigned int i=0; i<ArrayLength; ++i)
		Float4VectorArray.push_back(Float4Array[i]);
	return Float4VectorArray;
}


void ShaderParameter::set_Float3Array(const std::vector<D3DXVECTOR3>& inValue)
{
	if (Type!=ShaderParameterType::Float3Array && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	ArrayLength = static_cast<unsigned int>(inValue.size());
	D3DXVECTOR3* Float3Array = new D3DXVECTOR3[ArrayLength];
	for (unsigned int i=0; i<ArrayLength; ++i)
		Float3Array[i] = inValue[i];

	Parameter = Float3Array;
	Type = ShaderParameterType::Float3Array;
	PointerParameter = false;

	this->UpdateEffect();
}


std::vector<D3DXVECTOR3> ShaderParameter::get_Float3Array() const
{
	std::vector<D3DXVECTOR3> Float3VectorArray;
	D3DXVECTOR3* Float3Array = static_cast<D3DXVECTOR3*>(Parameter);
	for (unsigned int i=0; i<ArrayLength; ++i)
		Float3VectorArray.push_back(Float3Array[i]);
	return Float3VectorArray;
}


void ShaderParameter::set_Float2Array(const std::vector<D3DXVECTOR2>& inValue)
{
	if (Type!=ShaderParameterType::Float2Array && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	ArrayLength = static_cast<unsigned int>(inValue.size());
	D3DXVECTOR2* Float2Array = new D3DXVECTOR2[ArrayLength];
	for (unsigned int i=0; i<ArrayLength; ++i)
		Float2Array[i] = inValue[i];

	Parameter = Float2Array;
	Type = ShaderParameterType::Float2Array;
	PointerParameter = false;

	this->UpdateEffect();
}


std::vector<D3DXVECTOR2> ShaderParameter::get_Float2Array() const
{
	std::vector<D3DXVECTOR2> Float2VectorArray;
	D3DXVECTOR2* Float2Array = static_cast<D3DXVECTOR2*>(Parameter);
	for (unsigned int i=0; i<ArrayLength; ++i)
		Float2VectorArray.push_back(Float2Array[i]);
	return Float2VectorArray;
}


void ShaderParameter::set_FloatArray(const std::vector<float>& inValue)
{
	if (Type!=ShaderParameterType::FloatArray && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	ArrayLength = static_cast<unsigned int>(inValue.size());
	float* FloatArray = new float[ArrayLength];
	for (unsigned int i=0; i<ArrayLength; ++i)
		FloatArray[i] = inValue[i];

	Parameter = FloatArray;
	Type = ShaderParameterType::FloatArray;
	PointerParameter = false;

	this->UpdateEffect();
}


std::vector<float> ShaderParameter::get_FloatArray() const
{
	std::vector<float> FloatVectorArray;
	float* FloatArray = static_cast<float*>(Parameter);
	for (unsigned int i=0; i<ArrayLength; ++i)
		FloatVectorArray.push_back(FloatArray[i]);
	return FloatVectorArray;
}


void ShaderParameter::set_IntArray(const std::vector<int>& inValue)
{
	if (Type!=ShaderParameterType::IntArray && Type!=ShaderParameterType::Unknown)
	{
		Valid = false;
		return;
	}
	Valid = true;

	this->FreeParameter();

	ArrayLength = static_cast<unsigned int>(inValue.size());
	int* IntArray = new int[ArrayLength];
	for (unsigned int i=0; i<ArrayLength; ++i)
		IntArray[i] = inValue[i];

	Parameter = IntArray;
	Type = ShaderParameterType::IntArray;
	PointerParameter = false;

	this->UpdateEffect();
}


std::vector<int> ShaderParameter::get_IntArray() const
{
	std::vector<int> IntVectorArray;
	int* IntArray = static_cast<int*>(Parameter);
	for (unsigned int i=0; i<ArrayLength; ++i)
		IntVectorArray.push_back(IntArray[i]);
	return IntVectorArray;
}


unsigned int ShaderParameter::get_ArrayLength() const
{
	return ArrayLength;
}


bool ShaderParameter::isValid() const
{
	return Valid;
}


const std::string& ShaderParameter::get_Name() const
{
	return Name;
}


void ShaderParameter::Refresh(ID3DXEffect* inEffect)
{
	Effect = inEffect;
	Handle = Effect->GetParameterByName(NULL, Name.c_str());

	if (Handle==NULL)
		Valid = false;
}


D3DXHANDLE ShaderParameter::get_Handle() const
{
	return Handle;
}


void ShaderParameter::UpdateEffect()
{
	switch (Type)
	{
	case ShaderParameterType::Float4:
		Effect->SetVector(Handle, &this->get_Float4());
		break;
	case ShaderParameterType::Float3:
		{
			D3DXVECTOR3 CurrentValue = this->get_Float3();
			Effect->SetVector(Handle, &D3DXVECTOR4(CurrentValue.x, CurrentValue.y, CurrentValue.z, 0.0f));
			break;
		}
	case ShaderParameterType::Float2:
		{
			D3DXVECTOR2 CurrentValue = this->get_Float2();
			Effect->SetVector(Handle, &D3DXVECTOR4(CurrentValue.x, CurrentValue.y, 0.0f, 0.0f));
			break;
		}
	case ShaderParameterType::Float:
		Effect->SetFloat(Handle, this->get_Float());
		break;
	case ShaderParameterType::Int:
		Effect->SetInt(Handle, this->get_Int());
		break;
	case ShaderParameterType::Bool:
		Effect->SetBool(Handle, this->get_Bool());
		break;
	case ShaderParameterType::String:
		Effect->SetString(Handle, this->get_String().c_str());
		break;
	case ShaderParameterType::Matrix:
		Effect->SetMatrix(Handle, &this->get_Matrix());
		break;
	case ShaderParameterType::Texture1D:
		Effect->SetTexture(Handle, this->get_Texture1D());
		break;
	case ShaderParameterType::Texture2D:
		Effect->SetTexture(Handle, this->get_Texture2D());
		break;
	case ShaderParameterType::Texture3D:
		Effect->SetTexture(Handle, this->get_Texture3D());
		break;
	case ShaderParameterType::TextureCube:
		Effect->SetTexture(Handle, this->get_TextureCube());
		break;
	case ShaderParameterType::Float4Array:
		{
			D3DXVECTOR4* CurrentValue = static_cast<D3DXVECTOR4*>(Parameter);
			for (unsigned int i=0; i<ArrayLength; ++i)
			{
				D3DXHANDLE SubHandle = Effect->GetParameterElement(Handle, i);
				if (SubHandle!=NULL)
					Effect->SetVector(SubHandle, &CurrentValue[i]);
			}
		}
		break;
	case ShaderParameterType::Float3Array:
		{
			D3DXVECTOR3* CurrentValue = static_cast<D3DXVECTOR3*>(Parameter);
			for (unsigned int i=0; i<ArrayLength; ++i)
			{
				D3DXVECTOR4 CurrentValue4(CurrentValue[i].x, CurrentValue[i].y, CurrentValue[i].z, 0.0f);
				D3DXHANDLE SubHandle = Effect->GetParameterElement(Handle, i);
				if (SubHandle!=NULL)
					Effect->SetVector(SubHandle, &CurrentValue4);
			}
		}
		break;
	case ShaderParameterType::Float2Array:
		{
			D3DXVECTOR2* CurrentValue = static_cast<D3DXVECTOR2*>(Parameter);
			for (unsigned int i=0; i<ArrayLength; ++i)
			{
				D3DXVECTOR4 CurrentValue4(CurrentValue[i].x, CurrentValue[i].y, 0.0f, 0.0f);
				D3DXHANDLE SubHandle = Effect->GetParameterElement(Handle, i);
				if (SubHandle!=NULL)
					Effect->SetVector(SubHandle, &CurrentValue4);
			}
		}
		break;
	case ShaderParameterType::FloatArray:
		{
			float* CurrentValue = static_cast<float*>(Parameter);
			for (unsigned int i=0; i<ArrayLength; ++i)
			{
				D3DXHANDLE SubHandle = Effect->GetParameterElement(Handle, i);
				if (SubHandle!=NULL)
					Effect->SetFloat(SubHandle, CurrentValue[i]);
			}
		}
		break;
	case ShaderParameterType::IntArray:
		{
			int* li_Value_ = static_cast<int*>(Parameter);
			for (unsigned int i=0; i<ArrayLength; ++i)
			{
				D3DXHANDLE SubHandle = Effect->GetParameterElement(Handle, i);
				if (SubHandle!=NULL)
					Effect->SetInt(SubHandle, li_Value_[i]);
			}
		}
		break;
	}
}


void ShaderParameter::FreeParameter()
{
	if (!PointerParameter)
	{
		if (Parameter && ArrayLength == 0)
			delete Parameter;
		else if (Parameter)
			delete [] Parameter;
	}	

	ArrayLength = 0;
	Parameter = NULL;
}