#include "config.h"
#include "layered_mixture.h"
#include "texture.h"
#include "functions.h"

__BEGIN_BRDFLIB_SOURCE

typedef vector<double> rnd_array;
rnd_array CSurfaceLayeredMixture::diffuse_rnd;
rnd_array CSurfaceLayeredMixture::specular_rnd;

//========================================================================

CSurface* CSurfaceLayeredMixture::Dup() const
{
	CSurfaceLayeredMixture* dc = new CSurfaceLayeredMixture;
	dc->_layers = DupLayers();
	return dc;
}

//========================================================================

bool
CSurfaceLayeredMixture::GetAlbedoMax(CValues &albedo,
		const CHitPointInfo &hitInfo,
		EDistributionType   distType) const
{
	return GetAlbedo(albedo,hitInfo,distType);
}

//========================================================================

bool
CSurfaceLayeredMixture::GetAlbedo(CValues &albedo,
		const CHitPointInfo &hitInfo,
		EDistributionType   distType) const
{
	if (_layers.size() == 0 || _layers[0]->surface == NULL)
	{
		albedo.Set(0);
		return true;
	}
	else
	{
		return _layers[0]->surface->GetAlbedo(albedo,hitInfo,distType);
	}
}

//========================================================================

bool 
CSurfaceLayeredMixture::GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) const
{
	return false;
}

//========================================================================

bool
CSurfaceLayeredMixture::EvaluateBSDF(CValues &result,
	       const CHitPointInfo &hitInfo,
	       const CVector3D     &dirIn,
	       EDistributionType   distType) const
{
	if (_layers.size() == 0 || _layers[0]->surface == NULL)
	{
		// no layers were added yet..return some small value
		CVector3D N = hitInfo.GetNormal();
		float lightCos = DotProd(dirIn, N);
		result.Set(0.01f);
		result *= lightCos;
		return true;
	}
	return EvaluateBSDFRecursively(result, hitInfo, dirIn, distType, 0);
}

//========================================================================

bool
CSurfaceLayeredMixture::EvaluateBSDFRecursively(CValues &result,
	       const CHitPointInfo &hitInfo,
	       const CVector3D     &dirIn,
	       EDistributionType   distType,
		   const int layerIndex) const
{
	// main idea is based on the Weidlich-Wilkie BRDF model :
	// final BRDF = sum of BRDFs of all layers, where value of BRDF of 
	// layer l, is attenuated by the attenuations factor a = a0 * a1 * ... * a(l-1)
	// so this is a recursive approach
	CSurfaceLayer* layer = _layers[layerIndex];

	if (layer->surface == NULL)
	{
		result.Set(0);
		return true;
	}

	bool ownRes = layer->surface->EvaluateBSDF(result,hitInfo,dirIn,distType);
	if (layerIndex == _layers.size()-1)	// last layer
	{
		return ownRes;
	}
	else // any other layers under this current one
	{
		CValues subValue(CValues::EE_Weigth3);
		bool subRes = EvaluateBSDFRecursively(subValue,hitInfo,dirIn,distType,layerIndex+1);
		CValues a;
		CValues absVal;
		layer->absorbtion->GetValue(absVal,CHitPointInfo::constMapInfo);
		CValues thVal;
		layer->thickness->GetValue(thVal,CHitPointInfo::constMapInfo);
		float d = thVal.Get(0);

		CVector3D N      =  hitInfo.GetNormal();
		float lightCos   =  Max(DotProd(dirIn, N),(float)WTINY);
		float incEyeCos  =  Max(-hitInfo.GetDotProd(),(float)WTINY);

		float mult = d * (1.f / lightCos + 1.f / incEyeCos);

		a.Set(0,exp(-absVal.Get(0) * mult));
		a.Set(1,exp(-absVal.Get(1) * mult));
		a.Set(2,exp(-absVal.Get(2) * mult));

		assert(a.Max() <= WTINY1G && a.Max() >= -WTINY); // absorbtion should not be a) greater than 1.0, b) negative
		result += subValue * a;

		// t? how we get G from surface interface..there is no way how to do that :/
		// same for T21 = transmission coefficients
		return ownRes && subRes;
	}
}

//========================================================================

int
CSurfaceLayeredMixture::SpawnDeterministicRays(CContributionArray  &carray,
		         const CHitPointInfo &hitInfo,
       		         EDistributionType   distType) const
{
	return 0;
}

//========================================================================

float 
CSurfaceLayeredMixture::GetAverageDiffuseness(void) const
{
	return 0;
}

//========================================================================

float 
CSurfaceLayeredMixture::GetDiffuseness(const CHitPointInfo &,
			       EDistributionType distType) const
{
	return 0;
}

//========================================================================

int
CSurfaceLayeredMixture::SpawnStochasticRays(CContributionArray  &carray,
		      const CHitPointInfo &hitInfo,
		      CSamplingInfo       &samplingInfo,
		      EDistributionType    distType) const
{
	if (_layers.size() == 0 || _layers[0]->surface == NULL)
	{
		return 0;
	}
	else
	{
		return _layers[0]->surface->SpawnStochasticRays(carray,hitInfo,
			samplingInfo,distType);
	}
}

const char* 
CSurfaceLayeredMixture::GetHelp() const
{
	return BrdfStrings::KLayeredMixtureDesc;
}


__END_BRDFLIB_SOURCE