//----------------------------------------------------------------------------
// William Baxter III's Ray Tracer
//
//     Project for Comp 238, Raster Graphics
//     University of North Carolina at Chapel Hill
//     
// $Id:$
//----------------------------------------------------------------------------

#include <math.h>
#include "wb3Light.hpp"
#include "wb3Material.hpp"
#include "wb3Artifact.hpp"
#include "wb3Scene.hpp"

//----------------------------------------------------------------------------
wb3Light::wb3Light(const Vec3f& position)
{
  m_pos = position;
  m_AColor = Vec3f::ZERO;
  m_DColor = Vec3f::ZERO;
  m_SColor = Vec3f::ZERO;
  m_fA = m_fB = 0.0f;
  m_fC = 1.0f; // no attenuation by default
}
//----------------------------------------------------------------------------
wb3Light::wb3Light(const Vec3f& position, const Vec3f& color)
{
  m_pos = position;
  m_AColor = color;
  m_DColor = color;
  m_SColor = color;
  m_fA = m_fB = 0.0f;
  m_fC = 1.0f; // no attenuation by default
}
//----------------------------------------------------------------------------
wb3Light::wb3Light(const Vec3f& position, 
                   const Vec3f& ambientColor,
                   const Vec3f& diffuseColor,
                   const Vec3f& specularColor)
{
  m_pos = position;
  m_AColor = ambientColor;
  m_DColor = diffuseColor;
  m_SColor = specularColor;
  m_fA = m_fB = 0.0f;
  m_fC = 1.0f; // no attenuation by default
}
//----------------------------------------------------------------------------


// Computes the local portion of the illumination for a point
void wb3Light::Contribute(const wb3Scene *scene, const wb3Artifact *hit,
                          const Ray3f& I, 
                          const Vec3f &isect, 
                          const Vec3f &N, Vec3f& color) const
{

  // Get Material
  wb3Material *mat = hit->GetMaterial();
  if (!mat) return;

  // Contribute Ambient 
  color += GetAmbientColor().CompMult(mat->GetAmbient());

  Vec3f toLight = GetPosition() - isect;
  float scale = N * toLight;
  if (scale > 0) { // SCALE IS NOT NORMALIZED YET!!
    // the light is on the front side.
    float toLightDist = toLight.Length();
    // Make sure nothing else is in the way
    toLight /= toLightDist;
    Ray3f shadowRay(isect, toLight);
    float dist;
    if (!scene->Intersect(shadowRay, &dist, hit) || dist > toLightDist)
    {
      // Diffuse component
      scale /= toLightDist; // normalize scale
      scale *= ComputeAttenuation(dist);
      color += scale * GetDiffuseColor().CompMult(mat->GetDiffuse());

      // Specular component
      Vec3f R = I.V() + 2.0f * (-I.V() * N) * N; // reflected direction
      scale = float(pow(toLight * R, mat->GetSpecularity()));
      color += scale * GetSpecularColor().CompMult(mat->GetSpecular());
    }
  }
}

