// ===================================================================
// $Id: reflsurf.h,v 1.5 2003/11/04 15:27:18 havran Exp $
//
// reflsurf.h
//     Header file for basic reflectors.
//
// Class: CIdealDiffuseReflector, CSpecularReflector
// Class: CIdealMirrorReflector
//
// Licence and usage conditions in November 2003:
// This file can be used and modified only with written permission of
// Vlastimil Havran, Pohodli 27, 57001 Litomysl, the Czech Republic.
// This library is granted to be used by Polina Kondratieva within
// her MPI HIWI job working on BRDFLIB library. The usage condition
// may change in the next version of the library. At the moment,
// the library development is supported by Max-Planck-Institut for
// Informatik, Stuhlsatzenhausweg 85, 66123 Saarbruecken Germany
// See WWW: http://www.mpi-sb.mpg.de
// REPLACEMENT_STRING
//
// Initial coding by Jaroslav Krivanek (oct 2000) under supervision
// of Vlastimil Havran.
//
// Given to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.
//
// Edited by Jiri Nekola within diploma thesis, September 2011
// ===================================================================

#ifndef __REFLSURF_H__
#define __REFLSURF_H__

// BRDFLIB headers
#include "configh.h"
#include "surface.h"
#include "functions.h"


__BEGIN_BRDFLIB_HEADER

// Refernce:
//  1. Szirmay-Kalos, L. "Monte-Carlo Methods in Global Illumination", 2000
//  2. Glassner, "Principles of Digital Image Synthesis", Vol. 2
//  3. Dutre, P. "Global Illumination Compendium", 2001
//  4. Neumann, A. Ph.D. "Construction of Bidirectional Reflection Distribution
//     Functions", 2001
//  5. Blinn, J.F. Models of Light Reflection fo Computer Synthetized
//     Pictures", SIGGRAPH`77, In Computer Graphics 11(2), pp. 192-198


// ------------------------------------------------------------------------
// This class represents ideal diffusely reflecting surface
// ------------------------------------------------------------------------
class CIdealDiffuseReflector:
  public CSurface
{
  friend class CSurfacePhongPhysical;
public:
  /**
     Constructs the Lambertian surface.
     @param kd(in)  The diffuse color of the object (used as albedo).
  */
  CIdealDiffuseReflector(CTexture *kd):
    CSurface(kd ? EE_REFLECTED_DIFFUSE : EE_NONE),
    _kd(kd)
  {}

  /**
     Constructs the Lambertian surface.
  */
  CIdealDiffuseReflector():
    CSurface(EE_NONE),
    _kd(0)
  {}

  virtual ~CIdealDiffuseReflector()
  {
	if (_kd) delete _kd;
  }

  /**
     Deep copy this object.
  */
  virtual CSurface *Dup() const
  {
    return new CIdealDiffuseReflector(this->_kd);
  }

  // This is used for raytracing. The rays for diffuse surface
  // should be spawn uniformly in all directions over the hemisphere,
  // therefore, there is no a single deterministic direction of reflected
  // light for this surface type
  virtual inline int
  SpawnDeterministicRays(CContributionArray  &,
			 const CHitPointInfo &,
			 EDistributionType) const
  {
    return 0;
  }
  
  virtual int
  SpawnStochasticRays(CContributionArray  &carray,
		      const CHitPointInfo &hitInfo,
		      CSamplingInfo       &samplingInfo,
		      EDistributionType distType = EE_ALL) const;
  
  virtual inline bool
  EvaluateBSDF(CValues &res,
	       const CHitPointInfo &hitInfo,
	       const CVector3D &dirOut,
	       EDistributionType   distType = EE_ALL) const
  {
    assert(_kd);
    if(distType & EE_REFLECTED_DIFFUSE) {
      res = kd(hitInfo) * M_1_PI * fabs(DotProd(hitInfo.GetNormal(), dirOut)); // lambertian weighted by cos(theta)
      return true;
    }
    else
      return false;
  }
  
  virtual bool
  GetAlbedoMax(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    assert(_kd);
    if (distType & EE_REFLECTED_DIFFUSE) {
      albedo = kd(hitInfo);
      return true;
    }
    return false;
  }

  virtual bool
  GetAlbedo(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    assert(_kd);
    if (distType & EE_REFLECTED_DIFFUSE) {
      albedo = kd(hitInfo);
      return true;
    }
    return false;
  }
  /**
     Returns the value of diffuse coefficient at given point.
     (kd is albedo)
  */
  inline CValues kd(const CHitPointInfo &hitInfo) const
  {
    CValues ret(CValues::EE_Weigth3);
    hitInfo.GetTextureValue(ret, *_kd, CMappingBase::EE_KD);
    return ret;
  }
  
  CTexture* GetKd(void) const {return _kd;}

  inline void SetKd(CTexture* kd)
  {
	AssignPointer(_kd,kd);
  }
  
  virtual inline float GetAverageDiffuseness(void) const { return 1.0; }

  virtual float GetDiffuseness(const CHitPointInfo &,
			       EDistributionType distType = EE_ALL) const
  {
    if (distType & EE_REFLECTED_DIFFUSE)
      return 1.0;
    else
      return 0.0;
  }

  virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) const;

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

  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:
  CTexture *_kd;		// value in <0,1> -> albedo

  // Function that spawns the rays using stratified sampling around a
  // given hemisphere. Can be used for reflection and for refraction
  // (just by inverting the passed normal).
  // distType MUST be either EE_REFLECTED_DIFFUSE or EE_REFRACTED_DIFFUSE
  int SpawnStochasticRaysStratify(CContributionArray  &carray,
				  const CHitPointInfo &hitInfo,
				  EDistributionType distType,
				  CSamplingInfo       &samplingInfo,
				  const CVector3D &N) const;
};


// ------------------------------------------------------------------------
// This class represents ideal mirror reflector.
// ------------------------------------------------------------------------
class CIdealMirrorReflector:
  public CSurface
{
public:
  /**
     Constructs the mirror.

     @@@ The table of refraction indexes at different wavelengths should
     be passed to be able to compute the Fresnell function.
  */
  CIdealMirrorReflector(CTexture *km):
    CSurface(km ? EE_REFLECTED_PERFECT : EE_NONE),
    _km(km)
  {}

  /**
     Constructs the mirror.
  */
  CIdealMirrorReflector():
    CSurface(EE_NONE),
    _km(0)
  {}

  virtual ~CIdealMirrorReflector()
  {
	if (_km) delete _km;
  }

  virtual CSurface *Dup() const { return new CIdealMirrorReflector(_km); }

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

  virtual inline int
  SpawnStochasticRays(CContributionArray  &carray,
		      const CHitPointInfo &hitInfo,
		      CSamplingInfo       &samplingInfo,
		      EDistributionType distType = EE_ALL) const
  {
    return samplingInfo.GetMaxSamples() > 0 ?
      SpawnDeterministicRays(carray,hitInfo,distType) : 0;
  }


  virtual inline bool
  EvaluateBSDF(CValues &,
	       const CHitPointInfo &,
	       const CVector3D &,
	       EDistributionType = EE_ALL) const
  {
    // The value of BSDF is always zero
    return false;
  }


  virtual inline bool
  GetAlbedoMax(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    assert(_km);
    if(distType & EE_REFLECTED_PERFECT) {
      albedo = km(hitInfo);
      return true;
    }
    else
      return false;
  }

  virtual inline bool
  GetAlbedo(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    assert(_km);
    if(distType & EE_REFLECTED_PERFECT) {
      albedo = km(hitInfo);
      return true;
    }
    else
      return false;
  }

  virtual inline float GetAverageDiffuseness(void) const
  {
    return 0.0;
  }

  virtual float GetDiffuseness(const CHitPointInfo &,
			       EDistributionType = EE_ALL) const
  {
    return 0.0;
  }

  virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) const;

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

  inline void SetKm(CTexture* km)
  {
    AssignPointer(_km,km);
	_supportedType = (_km ? EE_REFLECTED_PERFECT : EE_NONE);
  }

  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:
  CTexture *_km;

  /**
     Evaluates the reflective coefficient for a given point on a surface.
     This value is used as albedo.
  */
  inline CValues km(const CHitPointInfo &hitInfo) const
  {
    CValues ret;
    hitInfo.GetTextureValue(ret, *_km, CMappingBase::EE_KM);
    return ret;
  }
};

// ------------------------------------------------------------------------
// This class represents ideal metal (reflectance is computed according to
// Fresnel formulae. km is considered as Fresnel texture. All the parameters
// should be set in an appropriate way ). Added by KP, December 2003
// ------------------------------------------------------------------------
class CIdealMetal:
  public CSurface
{
public:
  /**
     Constructs the mirror.

     @@@ The table of refraction indexes at different wavelengths should
     be passed to be able to compute the Fresnel function.
  */
  CIdealMetal(CTexture *km):
    CSurface(km ? EE_REFLECTED_PERFECT : EE_NONE),
    _km(km)
  {
    bool use_fresnel = km->IsFresnel();
    if(!use_fresnel)
    {
      WARNING_INFO("CIdealMetal::CIdealMetal() : albedo for CIdealMetal must be of CFresnelTexture type");
      return;
    }
  }

  CIdealMetal():
    CSurface(EE_NONE),
    _km(0)
  {}

  virtual ~CIdealMetal()
  {
	if (_km) delete _km;
  }

  virtual CSurface *Dup() const { return new CIdealMetal(_km); }

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

  virtual inline int
  SpawnStochasticRays(CContributionArray  &carray,
		      const CHitPointInfo &hitInfo,
		      CSamplingInfo       &samplingInfo,
		      EDistributionType distType = EE_ALL) const
  {
    return samplingInfo.GetMaxSamples() > 0 ?
      SpawnDeterministicRays(carray,hitInfo,distType) : 0;
  }


  virtual inline bool
  EvaluateBSDF(CValues &,
	       const CHitPointInfo &,
	       const CVector3D &,
	       EDistributionType = EE_ALL) const
  {
    // The value of BSDF is always zero
    return false;
  }


  virtual inline bool
  GetAlbedoMax(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    assert(_km);
    if(distType & EE_REFLECTED_PERFECT) {
      bool max = true;
      albedo = km(hitInfo, max);
      return true;
    }
    else
      return false;
  }

  virtual inline bool
  GetAlbedo(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    assert(_km);
    if(distType & EE_REFLECTED_PERFECT) {
      bool max = false;
      albedo = km(hitInfo, max);
      return true;
    }
    else
      return false;
  }

  virtual inline float GetAverageDiffuseness(void) const
  {
    return 0.0;
  }

  virtual float GetDiffuseness(const CHitPointInfo &,
			       EDistributionType = EE_ALL) const
  {
    return 0.0;
  }

  virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) const;

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

  inline void SetKm(CTexture* km)
  {
    if (km)
	{
	  bool use_fresnel = km->IsFresnel();
      if(!use_fresnel)
      {
        WARNING_INFO("CIdealMetal::SetKm() : albedo for CIdealMetal must be of CFresnelTexture type");
        return;
      }
	}
	AssignPointer(_km,km);
  }

  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:
  CTexture *_km;

  /**
     Evaluates the reflective coefficient for a given incident direction.
     For more information about Fresnel texture see [2], [3]
     This value is used as albedo.
  */
  inline CValues km(const CHitPointInfo &hitInfo, bool max) const
  {
    CValues ret;

    if(_km) 
    {
      if(!max)
      {
        bool use_fresnel = _km->IsFresnel();

	// get value of albedo according to Fresnel formulae if this is
	// Fresnel texture (EE_KFM mapping type), otherwise compute
	// value of albedo according to EE_KM mapping (see texture.h
	// and hminfo.h, hminfo.cpp files)
        if(use_fresnel)
           hitInfo.GetTextureValue(ret, *_km, CMappingBase::EE_KFM);
        else
           hitInfo.GetTextureValue(ret, *_km, CMappingBase::EE_KM);
      }
      else
      {
        hitInfo.GetTextureValue(ret, *_km, CMappingBase::EE_KM);
      }
    }
    else
    {
      WARNING_INFO("CIdealMetal::km() : No parameters were set for Ideal metal");
      return CValues(0,0,0);
    }
    return ret;
  }
};


// ------------------------------------------------------------------------
// This class represents Phong specular component.
// ------------------------------------------------------------------------
class CSpecularReflector:
  public CSurface
{
  friend class CSurfacePhongPhysical;
public:
  /**
     Constructs the surface.

     @param ks(in)    The specular coefficient of the object (used as
                      approximation of albedo).
     @param n(in)     Phong exponent.
  */
  CSpecularReflector(CTexture *ks,CTexture *n):
    CSurface( ks ? EE_REFLECTED_GLOSSY : EE_NONE ),
    _ks(ks),_n(n)
  {
    _maxPhong = false;
    _normalsBlinn = false;
  }

  /**
     Constructs specular reflector
  */
  CSpecularReflector():
    CSurface( EE_NONE ),
    _ks(0),_n(0)
  {
    _maxPhong = false;
    _normalsBlinn = false;
  }

  virtual ~CSpecularReflector()
  {
	if (_ks) delete _ks;
	if (_n) delete _n;
  }

  //  Deep copy this object.
  virtual CSurface *Dup() const
  {
    return new CSpecularReflector(_ks, _n);
  }

  // This is used for raytracing. The rays for Phong-specular surface should
  // be spawn accoring to power-cosine distribution inside the cosine lobe,
  // therefore, there is no a single deterministic direction of reflected
  // light for this surface type
  virtual int
  SpawnDeterministicRays(CContributionArray  &contributionArray,
			 const CHitPointInfo &hitInfo,
			 EDistributionType distributionType = EE_ALL) const
  {
    return 0;
  }
  
  virtual int
  SpawnStochasticRays(CContributionArray  &carray,
		      const CHitPointInfo &hitInfo,
		      CSamplingInfo       &samplingInfo,
		      EDistributionType   distributionType = EE_ALL) const;

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

  virtual bool
  GetAlbedoMax(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    if(distType & EE_REFLECTED_GLOSSY) {
      albedo = ks(hitInfo);
      return true;
    }
    else
      return false;
  }

  virtual bool
  GetAlbedo(CValues &albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    if(distType & EE_REFLECTED_GLOSSY) {
      CVector3D N = hitInfo.GetNormal();
      float cosine_inc = hitInfo.GetDotProd();

      if (cosine_inc < 0.0)
      {
        CVector3D Rm = hitInfo.GetReflectedDir();

	float nnn = n(hitInfo).Get(0);
        float integral = IntegralCosNCos(Rm, N, nnn);
        albedo = ks(hitInfo)*(nnn+2.0)*(integral/(2.0*M_PI)); 
        return true;
      }
      else
        return false;
    }
    else
      return false;
  }

  virtual EType GetType() const
  {
    return EE_BSDFSpecularRefl;
  }

  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 CValues ks(const CHitPointInfo &hitInfo) const
  {
    CValues ret;
    hitInfo.GetTextureValue(ret, *_ks, CMappingBase::EE_KS);
    return ret;
  }

  inline CValues n(const CHitPointInfo &hitInfo) const
  {
    CValues ret;
    hitInfo.GetTextureValue(ret, *_n, CMappingBase::EE_KSI);
    return ret;
  }

  // Set specular color of the object
  inline void SetSpecular(CTexture *ks)
  {
    AssignPointer(_ks,ks);
	if (_ks)
	{
	  _supportedType = EE_REFLECTED_GLOSSY;
	}
  }
  
  // set shininess of the object
  inline void SetShininess(CTexture *shine) 
  {
    AssignPointer(_n,shine);
  }

  inline CTexture* GetKs(void)        const {return _ks;}
  inline CTexture* GetShininess(void) const {return _n;}

  virtual float GetAverageDiffuseness(void) const;
  
  virtual float GetDiffuseness(const CHitPointInfo &hitinfo,
			       EDistributionType distType = EE_ALL) const;

  virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) const;

  virtual void SetMaxPhong(bool &maxPhong) 
  { 
	  _maxPhong = maxPhong; 
  }

  virtual void SetBlinnNormals(bool &blinnNormals) 
  { 
	  _normalsBlinn = blinnNormals; 
  }

protected:
  CTexture *_ks;	       // return value in <0,1> -> albedo
  CTexture *_n;		       // phong specular exponent (or shininess)

  bool _maxPhong;              // Use MaxPhong of Phong resiprocial
  bool _normalsBlinn;          // Use Blinn or Classical shading normals
};

__END_BRDFLIB_HEADER 

#endif //  __REFLSURF_H__


