// This is C5E7_attenuateLighting from "The Cg Tutorial" (Addison-Wesley, ISBN
// 0321194969) by Randima Fernando and Mark J. Kilgard.  See page 135.

// From page 128
struct Material {
  float3 Ke;
  float3 Ka;
  float3 Kd;
  float3 Ks;
  float shininess;
};

// From page 138
struct Light {
  float3 position;
  float3 color;
  float kC;
  float kL;
  float kQ;
  float3 direction;
  float cosInnerCone;
  float cosOuterCone;
};

// From page 135
float C5E6_attenuation(float3 P, 
                       Light light) 
{
  float d = distance(P, light.position);
  return 1 / (light.kC + light.kL * d + light.kQ * d * d); 
}

// From page 139
float C5E9_dualConeSpotlight(float3 P,
                             Light  light)
{
  float3 V = normalize(P - light.position);
  float cosOuterCone = light.cosOuterCone;
  float cosInnerCone = light.cosInnerCone;
  float cosDirection = dot(V, light.direction);
  //float cosDirection = dot(V, normalize(light.direction));
  return smoothstep(cosOuterCone, 
                    cosInnerCone, 
                    cosDirection);
}

// From page 139
void C5E10_spotAttenLighting(Light  light, 
                             float3 P,
                             float3 N,
                             float3 eyePosition,
                             float  shininess,

                         out float3 diffuseResult,
                         out float3 specularResult) 
{
  // Compute attenuation
  float attenuationFactor = C5E6_attenuation(P, light);

  // Compute spotlight effect
  float spotEffect = C5E9_dualConeSpotlight(P, light);
  //spotEffect = 1;

  // Compute the diffuse lighting
  float3 L = normalize(light.position - P);
  float diffuseLight = max(dot(N, L), 0);
  diffuseResult = attenuationFactor * spotEffect * 
                  light.color * diffuseLight;

  // Compute the specular lighting
  float3 V = normalize(eyePosition - P);
  float3 H = normalize(L + V);
  float specularLight = pow(max(dot(N, H), 0), 
                            shininess);
  if (diffuseLight <= 0) specularLight = 0;
  specularResult = attenuationFactor * spotEffect * 
                   light.color * specularLight;
}

// Based on C5E4v_twoLights (page 131) but for just one per-fragment light
void oneLight(float4 position : TEXCOORD0,
              float3 normal   : TEXCOORD1,

          out float4 color     : COLOR,

      uniform float3   eyePosition,
      uniform float3   globalAmbient,
      uniform Light    lights[1],
      uniform Material material)
{
  // Calculate emissive and ambient terms
  float3 emissive = material.Ke;
  float3 ambient = material.Ka * globalAmbient;

  // Loop over diffuse and specular contributions for each light
  float3 diffuseLight;
  float3 specularLight;
  float3 diffuseSum  = 0;
  float3 specularSum = 0;
  for (int i = 0; i < 1; i++) {
    C5E10_spotAttenLighting(lights[i], position.xyz, normal, 
                            eyePosition, material.shininess,
                            diffuseLight, specularLight);
    diffuseSum += diffuseLight;
    specularSum += specularLight;
  }

  // Now modulate diffuse and specular by material color
  float3 diffuse = material.Kd * diffuseSum;
  float3 specular = material.Ks * specularSum;

  color.xyz = emissive + ambient + diffuse + specular;
  //color.xyz = 0.2 * diffuseSum;
  color.w = 1;
}
