/* SPDX-License-Identifier: Apache-2.0
 * Copyright 2011-2022 Blender Foundation */

#pragma once

#include "kernel/geom/geom.h"

#include "kernel/light/common.h"

CCL_NAMESPACE_BEGIN

ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight,
                                            const float randu,
                                            const float randv,
                                            ccl_private LightSample *ls)
{
  /* distant light */
  float3 lightD = klight->co;
  float3 D = lightD;
  float radius = klight->distant.radius;
  float invarea = klight->distant.invarea;

  if (radius > 0.0f) {
    D = normalize(D + disk_light_sample(D, randu, randv) * radius);
  }

  ls->P = D;
  ls->Ng = D;
  ls->D = -D;
  ls->t = FLT_MAX;

  float costheta = dot(lightD, D);
  ls->pdf = invarea / (costheta * costheta * costheta);
  ls->eval_fac = ls->pdf;

  return true;
}

ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
                                                       const float3 ray_D,
                                                       const int lamp,
                                                       ccl_private LightSample *ccl_restrict ls)
{
  ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp);
  const int shader = klight->shader_id;
  const float radius = klight->distant.radius;
  const LightType type = (LightType)klight->type;

  if (type != LIGHT_DISTANT) {
    return false;
  }
  if (!(shader & SHADER_USE_MIS)) {
    return false;
  }
  if (radius == 0.0f) {
    return false;
  }

  /* a distant light is infinitely far away, but equivalent to a disk
   * shaped light exactly 1 unit away from the current shading point.
   *
   *     radius              t^2/cos(theta)
   *  <---------->           t = sqrt(1^2 + tan(theta)^2)
   *       tan(th)           area = radius*radius*pi
   *       <----->
   *        \    |           (1 + tan(theta)^2)/cos(theta)
   *         \   |           (1 + tan(acos(cos(theta)))^2)/cos(theta)
   *       t  \th| 1         simplifies to
   *           \-|           1/(cos(theta)^3)
   *            \|           magic!
   *             P
   */

  float3 lightD = klight->co;
  float costheta = dot(-lightD, ray_D);
  float cosangle = klight->distant.cosangle;

  /* Workaround to prevent a hang in the classroom scene with AMD HIP drivers 22.10,
   * Remove when a compiler fix is available. */
#ifdef __HIP__
  ls->shader = klight->shader_id;
#endif

  if (costheta < cosangle)
    return false;

  ls->type = type;
#ifndef __HIP__
  ls->shader = klight->shader_id;
#endif
  ls->object = PRIM_NONE;
  ls->prim = PRIM_NONE;
  ls->lamp = lamp;
  /* todo: missing texture coordinates */
  ls->u = 0.0f;
  ls->v = 0.0f;
  ls->t = FLT_MAX;
  ls->P = -ray_D;
  ls->Ng = -ray_D;
  ls->D = ray_D;
  ls->group = lamp_lightgroup(kg, lamp);

  /* compute pdf */
  float invarea = klight->distant.invarea;
  ls->pdf = invarea / (costheta * costheta * costheta);
  ls->eval_fac = ls->pdf;

  return true;
}

ccl_device_forceinline bool distant_light_tree_parameters(const float3 centroid,
                                                          const float theta_e,
                                                          ccl_private float &cos_theta_u,
                                                          ccl_private float2 &distance,
                                                          ccl_private float3 &point_to_centroid)
{
  /* Treating it as a disk light 1 unit away */
  cos_theta_u = fast_cosf(theta_e);

  distance = make_float2(1.0f / cos_theta_u, 1.0f);

  point_to_centroid = -centroid;

  return true;
}

CCL_NAMESPACE_END
