// ===================================================================
// $Id$
//
// schlick.h
//    Implementation of Schlick (Survey) BRDF.
//
// Class: CSchlickSurvey94
//
// Initial coding by Jaroslav Krivanek, January 2004.
//
// Edited by Jiri Nekola within diploma thesis, September 2011

#ifndef __SURFSCHLICK_SURVEY94_H__
#define __SURFSCHLICK_SURVEY94_H__

// GOLEM headers
#include "configh.h"
#include "surface.h"
#include "ward.h"
#include <vector>

// standard headers

__BEGIN_BRDFLIB_HEADER


// ------------------------------------------------------------------------------
/// Schlick-Survey model
// ------------------------------------------------------------------------------
class CSchlickSurvey94:
  public CSurfaceMicrofacet
{
public:
  /**
     Constructs the Ward isotropic surface.
     @param rho_d (in)  The diffuse reflectance texture.
     @param rho_s (in)  The glossy reflectance texture.
     @param slope (in)  Standard deviation (RMS) of the surface slope.
  */
  CSchlickSurvey94(CTexture *rho_d, CTexture *rho_s, CSlopeMicrofacets *slope,
                   vector<CFresnelTexture*> termsFresnel, bool &anisotropic):
    CSurfaceMicrofacet()
  {
    //cout << "000\n";
    if(rho_d != NULL)
    {
      _diffuse  = rho_d;
      _setSupportedType(EE_REFLECTED_DIFFUSE);
    }

    if(rho_s != NULL)
    {
      int slopeSize   = slope->GetSlopesSize();
      int fresnelSize = (int)termsFresnel.size();

      if(fresnelSize <= 0 || (fresnelSize > 1 && fresnelSize != slopeSize))
      {
		  WARNING_INFO("CShlickSurvey94 :\n\
For Schlick model (Survey'94) there are two possibilities:\n\
1) Fresnel reflection is the same for all facets\n(Fresnel terms size = 1)\n\
2) Fresnel reflection is different for each facet type\n(Fresnel terms size = number of different slopes)");
		return;
      }

      bool approxSchlick = true;
      // Cook-Torrance model contains Fresnel coefficient as an albedo for
      // specular part. Therefore, rho_s should be of Fresnel type
      for(int i = 0; i < fresnelSize; i++)
      {
        bool use_fresnel = termsFresnel[i]->IsFresnel();
        if(!use_fresnel)
        {
          WARNING_INFO("CShlickSurvey94 :\nFresnel terms for CSchlickSurvey94 must be of CFresnelTexture type");
          return;
        }
	_termsFresnel.push_back(termsFresnel[i]);
        _termsFresnel[i]->SetApproxSchlick(approxSchlick);
      }

      if(slope && slopeSize > 0)
      {
	if(slopeSize != slope->GetWeightsSize())
        {
		  WARNING_INFO("CShlickSurvey94 :\nSchlick model (Survey'94): Glossy term requires 'slope' texture with specified 'weight'.");
          return;
        }

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

        // Use Schlick approximation for Fresnel reflection.
	// Note, Schlick's approximation is correct only for dielectrics
	// and gold. Other metalls have point of inflection in their graphs.

	if(slopeSize == 2)
	  _anisotropic = anisotropic;
        else
	  _anisotropic = false;
      }
      else
      {
		WARNING_INFO("CShlickSurvey94 :\nSchlick model (Survey'94): Glossy term requires 'slope' texture with specified 'weight'.");
        return;
      }
    }
    //cout << "111\n";
  }

  CSchlickSurvey94():
    CSurfaceMicrofacet()
  {
    _anisotropic = false;
	_slopesCount = 1; // one slope as default
  }

  virtual ~CSchlickSurvey94()
  {
	 int size = _termsFresnel.size();
	 if (size)
	 {
		for (int i = 0; i < size; i++)
		{
			delete _termsFresnel[i];
		}
	 }
	 _termsFresnel.clear();
  }

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

  virtual bool GetAnisotropy() const
  {return _anisotropic;}

  virtual void SetAnisotropy(bool &anisotropic)
  {
    if((int)_termsFresnel.size() == 2)
      _anisotropic = anisotropic;
    else
      _anisotropic = false;
  }

  virtual vector<CFresnelTexture*> GetFresnelTerms() const
  {
    assert((int)_termsFresnel.size() > 0);
    return _termsFresnel;
  }

  virtual void SetFresnelTerms(vector<CFresnelTexture*> &termsFresnel)
  {
    int slopeSize   = _slope->GetSlopesSize();
    int fresnelSize = (int)termsFresnel.size();

    if(fresnelSize <= 0 || (fresnelSize > 1 && fresnelSize != slopeSize))
    {
      WARNING_INFO("CShlickSurvey94 :\n\
For Schlick model (Survey'94) there are two possibilities:\n\
1) Fresnel reflection is the same for all facets\n(Fresnel terms size = 1)\n\
2) Fresnel reflection is different for each facet type\n(Fresnel terms size = number of different slopes)");
		return;
    }


	
	 int size = _termsFresnel.size();
	 if (size)
	 {
		for (int i = 0; i < size; i++)
		{
			delete _termsFresnel[i];
		}
	 }
	 _termsFresnel.clear();

    bool approxSchlick = true;
    // Cook-Torrance model contains Fresnel coefficient as an albedo for
    // specular part. Therefore, rho_s should be of Fresnel type
    for(int i = 0; i < fresnelSize; i++)
    {
      bool use_fresnel = termsFresnel[i]->IsFresnel();
      if(!use_fresnel)
      {
        WARNING_INFO("CShlickSurvey94 :\nFresnel terms for CSchlickSurvey94 must be of CFresnelTexture type");
        return;
      }
      _termsFresnel.push_back(termsFresnel[i]);
      _termsFresnel[i]->SetApproxSchlick(approxSchlick);
    }
  }


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

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

  virtual CSurfaceMicrofacet* CreateNewObject() const
  { return new CSchlickSurvey94; }

  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
  //GetAlbedoMax(CValues &albedo,
//	    const CHitPointInfo &hitInfo,
//	    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("CSchlickSurvey94::GetAverageDiffuseness() :\n\
No idea how to estimate diffuseness of Schlick's model.\n\
It is a function of slope.");
    return 1.0;
  }

  virtual float GetDiffuseness(const CHitPointInfo &,
			       EDistributionType distType = EE_ALL) const
  {
    WARNING_INFO("CSchlickSurvey94::GetDiffuseness() :\n\
No idea how to estimate diffuseness of Schlick's 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;

protected:

  bool                     _anisotropic;
  vector<CFresnelTexture*> _termsFresnel;
  int					   _slopesCount;

  virtual EDistributionType _microfacetReflectionType() const
  { return EE_REFLECTED_GLOSSY; }

  virtual int SpawnStochasticSpecularRays(const CValues       &ks_local,
                                          const CValues       &kd_local,
                                          const vector<CValues> &termsF,
				          CContributionArray  &carray,
				          const CHitPointInfo &hitInfo,
				          CSamplingInfo       &samplingInfo,
				          int                 maxRays) const;

  virtual int SpawnStochasticDiffuseRays(const CValues        &kd_local,
                                         const CValues        &ks_local,
                                         const vector<CValues> &termsF,
					 CContributionArray   &carray,
				         const CHitPointInfo  &hitInfo,
				         CSamplingInfo        &samplingInfo,
				         int                  maxRays) const;

};










__END_BRDFLIB_HEADER

#endif // __SURFCOOK_TORRANCE_H__

