// ===================================================================
// $Id$
//
// kurt_kalos.h
//    Implementation of Kurt-Kalos BRDF.
//
// Class: CSurfaceKurtKalosAnisotropic, CSurfaceKurtKalosIsotropic
//
// Initial coding by Jiri Nekola, November 2011.
// ===================================================================

#ifndef __SURFKURT_KALOS_H__
#define __SURFKURT_KALOS_H__

// Framework headers
#include "configh.h"
#include "surface.h"
#include "surface_microfacet.h"
#include <vector>

// standard headers

__BEGIN_BRDFLIB_HEADER


// ----------------------------------------------------------------------------
//  Reference:
//  1. Kurt, M., Szirmay-Kalos, L., Krivanek, J. 
//     "An Anisotropic BRDF Model for Fitting and Monte Carlo Rendering"
//     SIGGRAPH 2010 vol.44 no. 1
// ----------------------------------------------------------------------------
class CSurfaceKurtKalosAnisotropic:
  public CSurfaceMicrofacet
{
public:
	/**
		Constructs the Kurt-Kalos surface.
		@param rho_d (in)  The diffuse reflectance texture.
		@param rho_s (in)  The glossy reflectance texture.
		@param alpha (in)  Alpha parameter texture.
		@param slope (in)  Standard deviation (RMS) of the surface slope.
	*/
	CSurfaceKurtKalosAnisotropic(CTexture *rho_d, CTexture *rho_s, CTexture *alpha, CSlopeMicrofacets *slope):
		CSurfaceMicrofacet()
	{
		_alpha = alpha;
		if(rho_d != NULL)
		{
			_diffuse  = rho_d;
			_setSupportedType(EE_REFLECTED_DIFFUSE);
		}

		if(rho_s != NULL)
		{
			// Kurt-Kalos model contains Fresnel coefficient as an albedo for
			// specular part. Therefore, rho_s should be of Fresnel type
			bool use_fresnel = rho_s->IsFresnel();
			if(!use_fresnel)
			{
				WARNING_INFO("Albedo for CSurfaceKurtKalosAnisotropic must be of CFresnelTexture type.");
				return;
			}

			if(slope && slope->GetSlopesSize() > 0)
			{
				if(slope->GetSlopesSize() != slope->GetWeightsSize())
				{
					WARNING_INFO("Kurt-Kalos model: Glossy term requires 'slope' texture");
					return;
				}

				_specular = rho_s;
				_slope   = slope;
				_setSupportedType(EE_REFLECTED_GLOSSY);

			}
			else
			{
				WARNING_INFO("Kurt-Kalos model: Glossy term requires 'slope' texture");
				return;
			}
		}
	}

	CSurfaceKurtKalosAnisotropic():
		CSurfaceMicrofacet(),
		_alpha(0)
	{
	}

	virtual ~CSurfaceKurtKalosAnisotropic() {};

	virtual char* GetName() const
	{return (char*)"CSurfaceKurtKalosAnisotropic";}

	virtual void Describe(ostream &app, int ind) const;

	//  Deep copy this object.
	virtual CSurface *Dup() const;

	virtual CSurfaceKurtKalosAnisotropic* CreateNewObject() const
	{ return new CSurfaceKurtKalosAnisotropic; }

	// There are no deterministic rays for model
	virtual int
	SpawnDeterministicRays(CContributionArray  &carray,
				 const CHitPointInfo &hitInfo,
   					 EDistributionType   distType = EE_ALL) const
	{
		return 0;
	}

	virtual int
	SpawnStochasticRays(CContributionArray  &carray,
			  const CHitPointInfo &hitInfo,
			  CSamplingInfo       &samplingInfo,
			  EDistributionType    distType = EE_ALL) const;

	virtual bool
	EvaluateBSDF(CValues             &res,
		   const CHitPointInfo &hitInfo,
		   const CVector3D     &dirIn,
		   EDistributionType    distType = EE_ALL) const;


	virtual bool
	GetAlbedo(CValues &albedo,
		const CHitPointInfo &hitInfo,
		EDistributionType   distType = EE_ALL) const;


	virtual float GetAverageDiffuseness(void) const  
	{
		WARNING_INFO("No idea how to estimate diffusenes of Kurt-Kalos model.\n\
It is a function of slope.");
		return 1.0;
	}

	virtual float GetDiffuseness(const CHitPointInfo &,
				   EDistributionType distType = EE_ALL) const
	{
		WARNING_INFO("No idea how to estimate diffusenes of Kurt-Kalos model.\n\
It is a function of slope.");
		return 1.0;
	}

	virtual inline EType GetType() const { return  EE_BSDFMicrofacetRefl; }

	virtual const char* GetParameterTitle(int parameterIndex) const;

	virtual const char* GetParameterDescription(int parameterIndex) const;

	virtual EParameterUIType GetParameterSyntax(int parameterIndex) const;

	virtual void SetParameter(int parameterIndex, 
							const CValues& value);

	virtual CValues GetParameter(int parameterIndex) const;

	virtual CValues GetParameterConstraints(int parameterIndex) const;

	virtual int GetNumParameters() const;

	virtual const char* GetHelp() const;

	inline void SetAlpha(CTexture *texture)
	{
		AssignPointer(_alpha,texture);
	}

protected:

	CTexture* _alpha; // shadowing/masking normalization factor

	virtual EDistributionType _microfacetReflectionType() const
	{ return EE_REFLECTED_GLOSSY; }

	virtual int SpawnStochasticSpecularRays(const CValues       &ks_local,
											CVector2D           &slopeXY,
											CVector2D           &slopeXY_sq,
											const CValues       &kd_local,
											CContributionArray  &carray,
											const CHitPointInfo &hitInfo,
											CSamplingInfo       &samplingInfo,
											int                 maxRays) const;

	virtual int SpawnStochasticDiffuseRays(const CValues         &kd_local,
											const CValues        &ks_local,
											CVector2D            &slopeXY,
											CVector2D            &slopeXY_sq,
											CContributionArray   &carray,
											const CHitPointInfo  &hitInfo,
											CSamplingInfo        &samplingInfo,
											int                  maxRays) const;

};




// ----------------------------------------------------------------------------
// Isotropic variant of Kurt-Kalos BRDF model
// ----------------------------------------------------------------------------
class CSurfaceKurtKalosIsotropic:
  public CSurfaceKurtKalosAnisotropic
{
public:
	/**
		Constructs the Kurt-Kalos surface.
		@param rho_d (in)  The diffuse reflectance texture.
		@param rho_s (in)  The glossy reflectance texture.
		@param alpha (in)  Alpha parameter texture.
		@param slope (in)  Standard deviation (RMS) of the surface slope.
	*/
	CSurfaceKurtKalosIsotropic(CTexture *rho_d, CTexture *rho_s, CTexture *alpha, CSlopeMicrofacets *slope):
		CSurfaceKurtKalosAnisotropic(rho_d, rho_s, alpha, slope)
	{
	}

	CSurfaceKurtKalosIsotropic():
		CSurfaceKurtKalosAnisotropic()
	{
	}

	virtual ~CSurfaceKurtKalosIsotropic() {};

	virtual char* GetName() const
	{return (char*)"CSurfaceKurtKalosIsotropic";}

	virtual CSurfaceKurtKalosIsotropic* CreateNewObject() const
	{ return new CSurfaceKurtKalosIsotropic; }

	virtual int
	SpawnStochasticRays(CContributionArray  &carray,
			  const CHitPointInfo &hitInfo,
			  CSamplingInfo       &samplingInfo,
			  EDistributionType    distType = EE_ALL) const;

	virtual bool
	EvaluateBSDF(CValues             &res,
		   const CHitPointInfo &hitInfo,
		   const CVector3D     &dirIn,
		   EDistributionType    distType = EE_ALL) const;

	virtual const char* GetParameterTitle(int parameterIndex) const;

	virtual const char* GetParameterDescription(int parameterIndex) const;

	virtual EParameterUIType GetParameterSyntax(int parameterIndex) const;

	virtual void SetParameter(int parameterIndex, 
							const CValues& value);

	virtual CValues GetParameter(int parameterIndex) const;

	virtual CValues GetParameterConstraints(int parameterIndex) const;

	virtual int GetNumParameters() const;

	virtual const char* GetHelp() const;

protected:
	virtual int SpawnStochasticSpecularRays(const CValues   &ks_local,
										float				&slope,
										float               &slope_sq,
										const CValues       &kd_local,
										CContributionArray  &carray,
										const CHitPointInfo &hitInfo,
										CSamplingInfo       &samplingInfo,
										int                 maxRays) const;

	virtual int SpawnStochasticDiffuseRays(const CValues     &kd_local,
										const CValues        &ks_local,
										float				 &slope,
										float                &slope_sq,
										CContributionArray   &carray,
										const CHitPointInfo  &hitInfo,
										CSamplingInfo        &samplingInfo,
										int                  maxRays) const;

};


__END_BRDFLIB_HEADER

#endif // __SURFKURT_KALOS_H__

