#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
#pragma BLENDER_REQUIRE(renderpass_lib.glsl)

struct ClosureInputDiffuse {
  vec3 N;      /** Shading normal. */
  vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */
};

#ifdef GPU_METAL
/* C++ struct initialization. */
#  define CLOSURE_INPUT_Diffuse_DEFAULT \
    { \
      vec3(0.0), vec3(0.0) \
    }
#else
#  define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
#endif

struct ClosureEvalDiffuse {
  vec3 probe_sampling_dir; /** Direction to sample probes from. */
  float ambient_occlusion; /** Final occlusion for distant lighting. */
};

/* Stubs. */
#define ClosureOutputDiffuse ClosureOutput
#define closure_Diffuse_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
#define closure_Diffuse_cubemap_eval(cl_in, cl_eval, cl_common, data, cl_out)

ClosureEvalDiffuse closure_Diffuse_eval_init(inout ClosureInputDiffuse cl_in,
                                             ClosureEvalCommon cl_common,
                                             out ClosureOutputDiffuse cl_out)
{
  cl_in.N = safe_normalize(cl_in.N);
  cl_out.radiance = vec3(0.0);

  ClosureEvalDiffuse cl_eval;
  cl_eval.ambient_occlusion = diffuse_occlusion(cl_common.occlusion_data,
                                                cl_common.V,
                                                cl_in.N,
                                                cl_common.Ng,
                                                cl_in.albedo,
                                                cl_eval.probe_sampling_dir);
  return cl_eval;
}

void closure_Diffuse_light_eval(ClosureInputDiffuse cl_in,
                                ClosureEvalDiffuse cl_eval,
                                ClosureEvalCommon cl_common,
                                ClosureLightData light,
                                inout ClosureOutputDiffuse cl_out)
{
  float radiance = light_diffuse(light.data, cl_in.N, cl_common.V, light.L);
  /* TODO(@fclem): We could try to shadow lights that are shadowless with the ambient_occlusion
   * factor here. */
  cl_out.radiance += light.data.l_color *
                     (light.data.l_diff * light.vis * light.contact_shadow * radiance);
}

void closure_Diffuse_grid_eval(ClosureInputDiffuse cl_in,
                               ClosureEvalDiffuse cl_eval,
                               ClosureEvalCommon cl_common,
                               ClosureGridData grid,
                               inout ClosureOutputDiffuse cl_out)
{
  vec3 probe_radiance = probe_evaluate_grid(
      grid.data, cl_common.P, cl_eval.probe_sampling_dir, grid.local_pos);
  cl_out.radiance += grid.attenuation * probe_radiance;
}

void closure_Diffuse_indirect_end(ClosureInputDiffuse cl_in,
                                  ClosureEvalDiffuse cl_eval,
                                  ClosureEvalCommon cl_common,
                                  inout ClosureOutputDiffuse cl_out)
{
  /* If not enough light has been accumulated from probes, use the world specular cubemap
   * to fill the remaining energy needed. */
  if (cl_common.diffuse_accum > 0.0) {
    vec3 probe_radiance = probe_evaluate_world_diff(cl_eval.probe_sampling_dir);
    cl_out.radiance += cl_common.diffuse_accum * probe_radiance;
  }
  /* Apply occlusion on radiance before the light loop. */
  cl_out.radiance *= cl_eval.ambient_occlusion;
}

void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in,
                              ClosureEvalDiffuse cl_eval,
                              ClosureEvalCommon cl_common,
                              inout ClosureOutputDiffuse cl_out)
{
  cl_out.radiance = render_pass_diffuse_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
  /* This makes shader resources become unused and avoid issues with samplers. (see #59747) */
  cl_out.radiance = vec3(0.0);
  return;
#endif
}
