// ===================================================================
// $Id: refrsurf.h,v 1.3 2003/10/30 16:36:35 havran Exp $
//
// refrsurf.h
//     Header file for basic refractors.
//
// Class: CIdealDiffuseRefractor, CSpecularRefractor
// Class: CIdealMirrorRefractor
//
// 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 (Nov 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 __REFRSURF_H__
#define __REFRSURF_H__

// BRDFLIB headers
#include "configh.h"
#include "reflsurf.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
//  6. Shirley, "Realistic Ray Tracing", 2003
// for more information see also reflsurf.h, reflsurf.cpp, phong.h, phong.cpp

/**---------------------------------------------------------------------
   Ideal diffusely refracting surface.
   
   This is refractor - so the Albedo is not really  the albedo is not the
   albedo in physical meaning (fraction of REFLECTED energy).
   It just returns the fraction of energy refracted into the surface.

   We don't care about index of refraction. The behaviour is the same
   as for diffuse reflector but with negated normal.
----------------------------------------------------------------------*/
class CIdealDiffuseRefractor:
  public CIdealDiffuseReflector
{
public:
  CIdealDiffuseRefractor(CTexture *kd):
    CIdealDiffuseReflector(kd)
  {
    _supportedType = EE_REFRACTED_DIFFUSE;
  }

  CIdealDiffuseRefractor():
    CIdealDiffuseReflector()
  {}

  virtual CSurface *Dup() const
  { return new CIdealDiffuseRefractor(_kd); }

  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 &,
	       EDistributionType   distType=EE_ALL) const
  {
    assert(_kd);
    if(distType & EE_REFRACTED_DIFFUSE) {
      res = kd(hitInfo) * M_1_PI;
      return true;
    }
    else
      return false;
  }

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

  virtual bool
  GetAlbedo(CValues& albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType=EE_ALL) const
  {
    assert(_kd);
    if(distType & EE_REFRACTED_DIFFUSE) {
      albedo = kd(hitInfo);
      return true;
    }
    else
      return false;
  }

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

  virtual bool GetTransparency(CValues &transparency,
			       const CHitPointInfo &hitinfo) const;

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

  virtual const char* GetHelp() const;
};



// ------------------------------------------------------------------------
// Ideal refractor (produces only one, ideally refracted, ray).
// ------------------------------------------------------------------------
class CIdealRefractor:
    public CSurface
{
  friend class CSurfacePhongPhysical;
public:
  CIdealRefractor(CTexture *kr, CTexture *ior):
    CSurface( kr ? EE_REFRACTED_PERFECT : EE_NONE),
    _kr(kr),
    _ior(ior)
  {}

  CIdealRefractor():
	CSurface(EE_NONE),
	_kr(0),
	_ior(0)
  {}

  virtual ~CIdealRefractor()
  {	
	  if (_kr) delete _kr;
	  if (_ior) delete _ior;
  }

  virtual CSurface *Dup() const
  {
    return new CIdealRefractor(_kr,_ior);
  }

  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 bool
  GetAlbedoMax(CValues& albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    if(distType & EE_REFRACTED_PERFECT) {
      albedo = kr(hitInfo);
      return true;
    }
    else
      return false;
  }

  // TODO
  // Hm... If by the Fresnel equation R(theta) is angle-dependent reflectance,
  // if T(theta) is angle-dependent transmittance and R(theta)+T(theta) = 1 or
  // some coefficient < 0 => the whole albedo is constant?   11.03.04   P.K.
  virtual bool
  GetAlbedo(CValues& albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    if(distType & EE_REFRACTED_PERFECT) {
      albedo = kr(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 inline EType GetType() const
  {
    return  EE_BSDFIdealRefr;
  }

  inline const CTexture* GetKr() const  { return _kr;  }
  inline const CTexture* GetIor() const { return _ior; }
  inline void SetKr(CTexture* kr) 
  { 
	AssignPointer(_kr,kr);
	if (_kr)
	{
		_setSupportedType(EE_REFRACTED_PERFECT);
	}
  }
  inline void SetIor(CTexture* ior) 
  {
	AssignPointer(_ior,ior);
  }

  virtual bool GetTransparency(CValues &transparency,
			       const CHitPointInfo &hitinfo) const;

  virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) 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: 
  CTexture *_kr;
  CTexture *_ior;

  inline CValues kr(const CHitPointInfo &hitInfo) const
  {
    CValues ret;
    if(_kr && _ior)
    {
      hitInfo.GetTextureValue(ret, *_kr, CMappingBase::EE_KR);
    }
    else
    {
	  WARNING_INFO("CIdealRefractor::kr() : No parameters were set for IdealRefractor");
      return CValues(0,0,0);
    }
    return ret;
  }
};


// ------------------------------------------------------------------------
// Ideal dielectric refractor. See [2], [3] for Fresnel formulae
// ------------------------------------------------------------------------
class CIdealDielectric:
    public CSurface
{
  friend class CSurfacePhongPhysical;
public:
  CIdealDielectric(CTexture *kr):
    CSurface(EE_NONE),
    _kr(kr)
  {
     if(kr)
       _supportedType = EE_REFRACTED_PERFECT | EE_REFLECTED_PERFECT;

     bool use_fresnel = kr->IsFresnel();
     if(!use_fresnel)
     {
	   WARNING_INFO("CIdealDielectric::CIdealDielectric() : albedo for CIdealDielectric must be of CFresnelTexture type");
       return;
     }
  }

  CIdealDielectric():
    CSurface(EE_NONE),
    _kr(0)
  {
    
  }

  virtual ~CIdealDielectric()
  {	
	  if (_kr) delete _kr;
  }

  virtual CSurface *Dup() const
  {
    return new CIdealDielectric(_kr); 
  }

  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 inline bool
  EvaluateBSDF(CValues &,
	       const CHitPointInfo &,
	       const CVector3D &,
	       EDistributionType  = EE_ALL) const
  {
    // The value of BSDF is always zero
    return false;
  }

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

  // TODO
  // Hm... If by the Fresnel equation R(theta) is angle-dependent reflectance,
  // if T(theta) is angle-dependent transmittance and R(theta)+T(theta) = 1 or
  // some coefficient < 0 => the whole albedo is constant?   11.03.04   P.K.
  virtual bool
  GetAlbedo(CValues& albedo,
	    const CHitPointInfo &hitInfo,
	    EDistributionType   distType = EE_ALL) const
  {
    if(distType & EE_REFRACTED_PERFECT) {
       // The transmittance is computed according to the Fresnel formulae.
      //    reflectance = 1 - transmittance =>
      //    reflectance + transmittance = albedo(hitPoint) does not depend
      // on the angle of incidence. (For metals: if reflectance is changing
      // by varying the incidence angle => absorption is changing
      // => albedo is also changing).
      bool max = true;
      albedo = kr(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 inline EType GetType() const
  {
    return  EE_REAL_DIELECTRIC;
  }

  inline const CTexture* GetKr() const  { return _kr;  }

  inline void SetKr(CTexture* kr)
  {
	AssignPointer(_kr,kr);
	if (_kr)
	{
	  bool use_fresnel = kr->IsFresnel();
      if(!use_fresnel)
      {
        WARNING_INFO("CIdealDielectric::SetKr() : albedo for CIdealDielectric must be of CFresnelTexture type");
        return;
      }
	  _supportedType = (EE_REFRACTED_PERFECT | EE_REFLECTED_PERFECT);
	}
	else
	{
	  _supportedType &= ~(EE_REFRACTED_PERFECT | EE_REFLECTED_PERFECT);
	}
  }

  virtual bool GetTransparency(CValues &transparency,
			       const CHitPointInfo &hitinfo) const;

  virtual bool GetDefaultColor(CValues &defaultColor,
			       const CHitPointInfo &hitInfo) 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:
  CTexture *_kr;
  
  typedef vector<double> rnd_array;
  static rnd_array specular_rnd;
  static rnd_array refraction_rnd;


   int SpawnIdealRefractedRay(const CValues      &kr_local,
                             const float         &ior,
			     CContributionArray  &carray,
			     const CHitPointInfo &hitInfo) const;

   int SpawnIdealReflectedRay(const CValues       &ks_local,
			     CContributionArray  &carray,
			     const CHitPointInfo &hitInfo) const;

  inline CValues kr(const CHitPointInfo &hitInfo, bool max) const
  {
    CValues ret;

    if(_kr)
    {
      if(!max)
      {
 	    bool use_fresnel = _kr->IsFresnel();
        if(use_fresnel)
           hitInfo.GetTextureValue(ret, *_kr, CMappingBase::EE_KFR);
	    else
           hitInfo.GetTextureValue(ret, *_kr, CMappingBase::EE_KR);
      }
      else
      {
         hitInfo.GetTextureValue(ret, *_kr, CMappingBase::EE_KR);
      }
    }
    else
    {
      WARNING_INFO("CIdealDielectric::kr() : No parameters were set for Ideal dielectric");
      return CValues(0,0,0);
    }
    return ret;
  }
};

__END_BRDFLIB_HEADER

#endif // __REFRSURF_H__

