/* PhBHeterogeneousSurface.C: PhBHeterogeneousSurface nodes (public source) */

#include "PhBHeterogeneousSurface.H"
#include "utils.H"

namespace xrml {

void PhBHeterogeneousSurface::render(void)
{
  for (int i=0; i<bsdf.size; i++) bsdf[i]->render();

  // allocate sufficient space for holding sampling weights and
  // accumulated transmittances.
  if (w.size < bsdf.size) w.grow(bsdf.size-w.size);
  if (trans.size < bsdf.size) trans.grow(bsdf.size-trans.size);
}

bool PhBHeterogeneousSurface::isLightSource(void)
{
  return false;
}

bool PhBHeterogeneousSurface::isScatterer(void)
{
  return (bsdf.size > 0);
}

Spectrum PhBHeterogeneousSurface::evaluateEDF(const Vec3& point,
				 const Vec3& texco, const Vec3& texco3d,
				 const Vec3& direction,
				 const Spectrum& specWeight,
				 int range,
				 float *pdf)
{
  if (pdf) *pdf = 0.;
  return Spectrum(0.);
}

Vec3 PhBHeterogeneousSurface::sampleEDF(const Vec3& point,
			   const Vec3& texco, const Vec3& texco3d,
			   const Spectrum& specWeight,
			   float xi1, float xi2, 
			   int range,
			   Spectrum *value, float *pdf)
{
  if (pdf) *pdf = 0.;
  if (value) *value = Spectrum(0.);
  return Vec3(0.,0.,0.);
}

Spectrum PhBHeterogeneousSurface::emittance(const Vec3& point, 
					    const Vec3& texco, const Vec3& texco3d,
					    int range)
{
  return Spectrum(0.);
}

float PhBHeterogeneousSurface::calculate_transmittances(const Vec3& inDir, 
				const complex& indexIn, 
				const complex& indexOut,
				const Spectrum& inSpec,
				const int modes,
				float *pdf)
{
  float sumw = 0.;
  trans[0] = Spectrum(1.);
  for (int i=0; i<bsdf.size; i++) {
    Spectrum calb = bsdf[i]->albedo(inDir, indexIn, indexOut, modes);
    if (pdf) {
      w[i] = fabs((trans[i] * calb) & inSpec);
      sumw += w[i];
    }
    if (i<bsdf.size-1)
      trans[i+1] = trans[i] * (Spectrum(1.) - calb);
  }
  return sumw;
}

Spectrum PhBHeterogeneousSurface::evaluateBSDF(const Vec3& point,
				  const Vec3& texco, const Vec3& texco3d,
				  const Vec3& inDir,
				  const Vec3& outDir,
				  const Spectrum& inSpec,
				  const complex& indexIn,
				  const complex& indexOut,
				  int modes,
				  float *pdf)
{
  float sumw = calculate_transmittances(inDir, indexIn, indexOut, inSpec, modes, pdf);

  Spectrum val(0.);
  if (pdf) *pdf = 0.;
  for (int i=0; i<bsdf.size; i++) {
    float cpdf;
    Spectrum cval = bsdf[i]->eval(inDir, outDir, indexIn, indexOut, modes, pdf ? &cpdf : (float*)0);
    val += trans[i] * cval;
    if (pdf) *pdf += w[i] * cpdf;
  }

  if (pdf && sumw > EPSILON) *pdf /= sumw;
  return val;
}

Vec3 PhBHeterogeneousSurface::sampleBSDF(const Vec3& point,
			    const Vec3& texco, const Vec3& texco3d,
			    const Vec3& inDir,
			    const Spectrum& inSpec,
			    const complex& indexIn,
			    const complex& indexOut,
			    float xi1, float xi2,
			    int modes,
			    Spectrum *value, float *pdf)
{
  float sumw = calculate_transmittances(inDir, indexIn, indexOut, inSpec, modes, pdf);
  if (sumw < EPSILON)
    return Vec3(0.,0.,1.);

  int chosen = SampleDiscretePDF(&w[0], bsdf.size, sumw, &xi1);

  Vec3 outDir = bsdf[chosen]->sample(inDir, indexIn, indexOut, xi1, xi2, modes);

  if (pdf || value) {
    if (value) *value = Spectrum(0.);
    if (pdf) *pdf = 0.;
    for (int i=0; i<bsdf.size; i++) {
      float cpdf;
      Spectrum cval = bsdf[i]->eval(inDir, outDir, indexIn, indexOut, modes, pdf ? &cpdf : (float*)0);
      if (value) *value += trans[i] * cval;
      if (pdf) *pdf += w[i] * cpdf;
    }
    if (pdf) *pdf /= sumw;
  }

  return outDir;
}

Spectrum PhBHeterogeneousSurface::albedo(const Vec3& point,
			    const Vec3& texco, const Vec3& texco3d,
			    const Vec3& inDir,
			    const complex& indexIn,
			    const complex& indexOut,
			    int modes)
{
  Spectrum albedo(0.), trans(1.);
  for (int i=0; i<bsdf.size; i++) {
    Spectrum calb = bsdf[i]->albedo(inDir, indexIn, indexOut, modes);
    albedo += trans * calb;
    trans *= Spectrum(1.) - calb;
  }
  return albedo;
}

}  // namespace xrml
