
#ifndef SAMPLING_STRATEGY_H
#define SAMPLING_STRATEGY_H

#include "brdf.h"
#include "ray.h"
#include "spherical.h"


// Sampling Strategy flags
const int DE_UNIFORM_SAMPLE             =  1;
const int DE_COSTHETA_SAMPLE            =  2;
const int DE_BRDF_SAMPLE                =  4;
const int DE_AREA_SAMPLE                =  8;
const int DE_DIRECT_POTENTIAL_SAMPLE    = 16;
const int DE_EQUI_WIN_SAMPLE            = 32;


// SamplingStrategy
// This abstract class describes a sampling strategy.
class SamplingStrategy
{
public:
  // SamplingStrategy factory.
  static SamplingStrategy* newSamplingStrategy(int sf);
  static SamplingStrategy* newSingleSamplingStrategy(int sm);

  // Destructor
  virtual ~SamplingStrategy() {};


  // Get Sampling Strategy flags
  // virtual int GetSamplingStrategyFlags();


  // Reset the sampling strategy (recompute probabilities according to situation)
  virtual void Update();

  // Choose a direction for the reflected particle.
  // IN:  The incoming ray.
  //      The hit record corresponding to the ray.
  // OUT: The choosen outgoing ray.
  //      The hit record corresponding to the ray.
  //      The probability of selection of this ray according to the sampling strategy modelised.
  //      (for each wavelength)
  virtual double BounceRay(RAY &rin,HITREC &hin,RAY &rout,HITREC &hout) = 0;

  // Evaluate the probability of selection of a ray.
  // IN:  The incoming ray.
  //      The hit record corresponding to the ray.
  //      The outgoing ray.
  //      The hit record corresponding to the ray.
  //      The cosine between the normal and the outgoing ray.
  // OUT: The probability of selection of the outgoing ray according to the sampling strategy modelised.
  virtual double EvalRay(RAY &rin,HITREC &hin,RAY &rout,HITREC &hout,const double cosTheta) = 0;
};



// UniformBRDFSamplingStrategy modelises an uniform hemisphere sampling strategy.
class UniformBRDFSamplingStrategy : public SamplingStrategy
{
public:
  ~UniformBRDFSamplingStrategy() {};
  double BounceRay( RAY &rin, HITREC &hin,RAY &rout,HITREC &hout);
  double EvalRay( RAY &rin, HITREC &hin, RAY &rout, HITREC &hout,const double cosTheta);
};



// CosThetaBRDFSamplingStrategy modelises a cos theta hemisphere sampling strategy.
class CosThetaBRDFSamplingStrategy : public SamplingStrategy
{
public:
  ~CosThetaBRDFSamplingStrategy() {};
  double BounceRay( RAY &rin, HITREC &hin,RAY &rout,HITREC &hout);
  double EvalRay( RAY &rin, HITREC &hin, RAY &rout, HITREC &hout,const double cosTheta);
};



// CosNThetaBRDFSamplingStrategy modelises a cos^n theta hemisphere sampling strategy.
class CosNThetaBRDFSamplingStrategy : public SamplingStrategy
{
public:
  ~CosNThetaBRDFSamplingStrategy() {};
  double BounceRay( RAY &rin, HITREC &hin,RAY &rout,HITREC &hout);
  double EvalRay( RAY &rin, HITREC &hin, RAY &rout, HITREC &hout,const double cosTheta);
};


// PhysicalBRDFSamplingStrategy modelises a physical sampling strategy.
class PhysicalBRDFSamplingStrategy : public SamplingStrategy
{
public:
  double BounceRay( RAY &rin, HITREC &hin,RAY &rout,HITREC &hout);
  double EvalRay( RAY &rin, HITREC &hin, RAY &rout, HITREC &hout,const double cosTheta);
};


// CombineSamplingStrategy modelises a combinaison of hemisphere sampling strategy
// using Eric Veach article.
class CombineBRDFSamplingStrategy : public SamplingStrategy
{
private:
  int               nm;   // Number of sampling strategies to combine.
  SamplingStrategy  **pb; // Array of sampling strategies pointers.
  float             *p;   // Array of probability for each sampling strategy.

public:
  // Constructor.
  // IN:  The number of techniques to combine.
  CombineBRDFSamplingStrategy(int n);

  // Destructor.
  ~CombineBRDFSamplingStrategy();

  // Set the Nth technique.
  void setSamplingStrategy(int i,float p,SamplingStrategy *npb);

  // Virtual functions from SamplingStrategy
  void   Update();
  double BounceRay( RAY &rin, HITREC &hin,RAY &rout,HITREC &hout);
  double EvalRay( RAY &rin, HITREC &hin, RAY &rout, HITREC &hout,const double cosTheta);
};
  


// Patch Probability SamplingStrategy
class PatchBRDFSamplingStrategy : public SamplingStrategy
{
private:
  PatchProbabilityAssigner *ppa;

public:
  PatchBRDFSamplingStrategy(PatchProbabilityAssigner *nppa);
  ~PatchBRDFSamplingStrategy();

  void Update();
  double BounceRay( RAY &rin, HITREC &hin,RAY &rout,HITREC &hout);
  double EvalRay( RAY &rin, HITREC &hin, RAY &rout, HITREC &hout,const double cosTheta);
};


#endif













