/* SPDX-FileCopyrightText: 2005 Blender Authors
 *
 * SPDX-License-Identifier: GPL-2.0-or-later */

#include "BLI_string.h"

#include "node_shader_util.hh"

#include "UI_interface_layout.hh"
#include "UI_resources.hh"

#include "BKE_node_runtime.hh"

namespace blender {

namespace nodes::node_shader_volume_coefficients_cc {

static void node_declare(NodeDeclarationBuilder &b)
{
  const bNodeTree *ntree = b.tree_or_null();
  const bool is_gpu_internal = ntree && (ntree->flag & NTREE_IS_GPU_SHADER_INTERNAL);

  b.use_custom_socket_order();

  b.add_output<decl::Shader>("Volume"_ustr).translation_context(BLT_I18NCONTEXT_ID_ID);

  b.add_input<decl::Float>("Weight"_ustr).available(is_gpu_internal);
#define SOCK_WEIGHT_ID 0

  PanelDeclarationBuilder &abs = b.add_panel("Absorption"_ustr).default_closed(false);
  abs.add_input<decl::Vector>("Absorption Coefficients"_ustr)
      .default_value({1.0f, 1.0f, 1.0f})
      .min(0.0f)
      .max(1000.0f)
      .description(
          "Probability density per color channel that light is absorbed per unit distance "
          "traveled in the medium");
#define SOCK_ABSORPTION_COEFFICIENTS_ID 1
  PanelDeclarationBuilder &sca = b.add_panel("Scatter"_ustr).default_closed(false);
  sca.add_layout([](ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr) {
    layout.prop(ptr, "phase", ui::ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
  });
  sca.add_input<decl::Vector>("Scatter Coefficients"_ustr)
      .default_value({1.0f, 1.0f, 1.0f})
      .min(0.0f)
      .max(1000.0f)
      .description(
          "Probability density per color channel of an out-scattering event occurring per unit "
          "distance");
#define SOCK_SCATTER_COEFFICIENTS_ID 2
  sca.add_input<decl::Float>("Anisotropy"_ustr)
      .default_value(0.0f)
      .min(-1.0f)
      .max(1.0f)
      .subtype(PROP_FACTOR)
      .description(
          "Directionality of the scattering. Zero is isotropic, negative is backward, "
          "positive is forward")
      .make_available([](bNode &node) { node.custom1 = SHD_PHASE_HENYEY_GREENSTEIN; });
#define SOCK_SCATTER_ANISOTROPY_ID 3
  sca.add_input<decl::Float>("IOR"_ustr)
      .default_value(1.33f)
      .min(1.0f)
      .max(2.0f)
      .subtype(PROP_FACTOR)
      .description("Index Of Refraction of the scattering particles")
      .make_available([](bNode &node) { node.custom1 = SHD_PHASE_FOURNIER_FORAND; });
#define SOCK_SCATTER_IOR_ID 4
  sca.add_input<decl::Float>("Backscatter"_ustr)
      .default_value(0.1f)
      .min(0.0f)
      .max(0.5f)
      .subtype(PROP_FACTOR)
      .description("Fraction of light that is scattered backwards")
      .make_available([](bNode &node) { node.custom1 = SHD_PHASE_FOURNIER_FORAND; });
#define SOCK_SCATTER_BACKSCATTER_ID 5
  sca.add_input<decl::Float>("Alpha"_ustr)
      .default_value(0.5f)
      .min(0.0f)
      .max(500.0f)
      .make_available([](bNode &node) { node.custom1 = SHD_PHASE_DRAINE; });
#define SOCK_SCATTER_ALPHA_ID 6
  sca.add_input<decl::Float>("Diameter"_ustr)
      .default_value(20.0f)
      .min(0.0f)
      .max(50.0f)
      .description("Diameter of the water droplets, in micrometers")
      .make_available([](bNode &node) { node.custom1 = SHD_PHASE_MIE; });
#define SOCK_SCATTER_DIAMETER_ID 7
  PanelDeclarationBuilder &emi = b.add_panel("Emission"_ustr).default_closed(false);
  emi.add_input<decl::Vector>("Emission Coefficients"_ustr)
      .default_value({0.0f, 0.0f, 0.0f})
      .min(0.0f)
      .max(1000.0f)
      .description("Emitted radiance per color channel that is added to a ray per unit distance");
#define SOCK_EMISSION_COEFFICIENTS_ID 8
}

static void node_shader_init_coefficients(bNodeTree * /*ntree*/, bNode *node)
{
  node->custom1 = SHD_PHASE_HENYEY_GREENSTEIN;
}

static void node_shader_update_coefficients(bNodeTree *ntree, bNode *node)
{
  const int phase_function = node->custom1;

  for (bNodeSocket &sock : node->inputs) {
    if (STR_ELEM(sock.name, "IOR", "Backscatter")) {
      bke::node_set_socket_availability(*ntree, sock, phase_function == SHD_PHASE_FOURNIER_FORAND);
    }
    else if (STR_ELEM(sock.name, "Anisotropy")) {
      bke::node_set_socket_availability(
          *ntree, sock, ELEM(phase_function, SHD_PHASE_HENYEY_GREENSTEIN, SHD_PHASE_DRAINE));
    }
    else if (STR_ELEM(sock.name, "Alpha")) {
      bke::node_set_socket_availability(*ntree, sock, phase_function == SHD_PHASE_DRAINE);
    }
    else if (STR_ELEM(sock.name, "Diameter")) {
      bke::node_set_socket_availability(*ntree, sock, phase_function == SHD_PHASE_MIE);
    }
  }
}

static int node_shader_gpu_volume_coefficients(GPUMaterial *mat,
                                               bNode *node,
                                               bNodeExecData * /*execdata*/,
                                               GPUNodeStack *in,
                                               GPUNodeStack *out)
{
  if (in[SOCK_SCATTER_COEFFICIENTS_ID].socket_not_black()) {
    GPU_material_flag_set(mat, GPU_MATFLAG_VOLUME_SCATTER | GPU_MATFLAG_VOLUME_ABSORPTION);
  }
  if (in[SOCK_ABSORPTION_COEFFICIENTS_ID].socket_not_black()) {
    GPU_material_flag_set(mat, GPU_MATFLAG_VOLUME_ABSORPTION);
  }
  return GPU_stack_link(mat, node, "node_volume_coefficients", in, out);
}

#undef SOCK_WEIGHT_ID
#undef SOCK_ABSORPTION_COEFFICIENTS_ID
#undef SOCK_SCATTER_COEFFICIENTS_ID
#undef SOCK_SCATTER_ANISOTROPY_ID
#undef SOCK_SCATTER_IOR_ID
#undef SOCK_SCATTER_BACKSCATTER_ID
#undef SOCK_SCATTER_ALPHA_ID
#undef SOCK_SCATTER_DIAMETER_ID
#undef SOCK_EMISSION_COEFFICIENTS_ID

}  // namespace nodes::node_shader_volume_coefficients_cc

/* node type definition */
void register_node_type_sh_volume_coefficients()
{
  namespace file_ns = nodes::node_shader_volume_coefficients_cc;

  static bke::bNodeType ntype;

  sh_node_type_base(&ntype, "ShaderNodeVolumeCoefficients"_ustr, SH_NODE_VOLUME_COEFFICIENTS);
  ntype.ui_name = "Volume Coefficients";
  ntype.ui_description =
      "Model all three physical processes in a volume, represented by their coefficients";
  ntype.enum_name_legacy = "VOLUME_COEFFICIENTS";
  ntype.nclass = NODE_CLASS_SHADER;
  ntype.declare = file_ns::node_declare;
  ntype.gather_link_search_ops = search_link_ops_for_shader_bsdf_node;
  ntype.add_ui_poll = object_shader_nodes_poll;
  ntype.default_width = bke::NodeWidth::_240;
  ntype.initfunc = file_ns::node_shader_init_coefficients;
  ntype.gpu_fn = file_ns::node_shader_gpu_volume_coefficients;
  ntype.updatefunc = file_ns::node_shader_update_coefficients;

  bke::node_register_type(ntype);
}

}  // namespace blender
