#include <math.h>

#include "hitlist.h"
#include "dest.h"
#include "phong.h"
#include "potential.h"
#include "ray.h"
#include "scene.h"
#include "Common.h"
#include "SamplingStrategy.h"



PatchBRDFSamplingStrategy::PatchBRDFSamplingStrategy(PatchProbabilityAssigner *nppa)
{
  // Save
  ppa = nppa;
}


PatchBRDFSamplingStrategy::~PatchBRDFSamplingStrategy()
{
  delete ppa;
}


void PatchBRDFSamplingStrategy::Update()
{
  ppa->assignProbabilities((PATCH *)NULL);
}


double PatchBRDFSamplingStrategy::BounceRay(RAY &rin,HITREC &hin,RAY &rout,HITREC &hout)
{
  DENSITY_ESTIMATION_DATA     *thisData;
  PATCH                       *thisPatch;
  double                      xi_1,xi_2;
  double                      cosTheta;
  double                      res;

  // Choose patch
  thisPatch = ppa->choosePatch();

  // Set default result
  res = 0.0;

  // A patch is choosen
  if ( thisPatch != hin.patch ) 
    {
      // First select the ray.
      
      // Select position
      rout.pos = hin.point;

      // Select direction
      thisData =  (DENSITY_ESTIMATION_DATA_PTR) thisPatch->radiance_data;
      xi_1 = drand48();
      xi_2 = drand48();
      
      PatchUniformPoint(thisPatch,xi_1,xi_2,&rout.dir);
      VECTORSUBTRACT(rout.dir,rout.pos,rout.dir);
      VECTORNORMALIZE(rout.dir);

      // Calculate cosTheta
      cosTheta = VECTORDOTPRODUCT(hin.normal,rout.dir);
      
      // Send ray
      RayCastingFunction(hin.patch,&rout,hout); // !!! FRONT ONLY !!!

      // Evaluate probability
      res = EvalRay(rin,hin,rout,hout,cosTheta);
    }

  return res;
}


double PatchBRDFSamplingStrategy::EvalRay(RAY &,HITREC &hin,RAY &rout,HITREC &,const double cosTheta)
{ 
  HITLIST *hitlist;
  double  res,act;

  // We are only working with brdf
  if (cosTheta<0.0)
    return 0.0;

  // Initialise res.
  res = 0.0;

  // Accumulate probability
  PatchDontIntersect(1, hin.patch);
  hitlist = AllGridIntersections((HITLIST *)NULL, WorldGrid, &rout, 0.0, HUGE, HIT_FRONT|HIT_BACK|HIT_PATCH);
  ForAllHits(hit,hitlist)
    {
      act = fabs(VECTORDOTPRODUCT(rout.dir,hit->patch->normal));
      if (act>EPSILON)
	res += (ppa->getProbability(hit->patch)*hit->t*hit->t)/(act*hit->patch->area);
    } EndForAll;

  DestroyHitlist(hitlist);

  return res;
}

