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

/** \file
 * \ingroup edsculpt
 */
#pragma once

#include <memory>

#include "BKE_paint.hh"

#include "BLI_array.hh"
#include "BLI_offset_indices.hh"
#include "BLI_set.hh"

#include "DNA_brush_enums.h"

namespace blender {

struct BMVert;
struct Brush;
struct CurveMapping;
struct Depsgraph;
struct Object;
struct Sculpt;
struct SculptSession;
namespace bke::pbvh {
struct MeshNode;
struct GridsNode;
struct BMeshNode;
}  // namespace bke::pbvh

namespace ed::sculpt_paint::auto_mask {

struct Settings {
  /* eAutomasking_flag. */
  int flags;
  int initial_face_set;
  std::array<int, PAINT_SYMM_AREAS> initial_island_nr = {-1, -1, -1, -1, -1, -1, -1, -1};

  float cavity_factor;
  int cavity_blur_steps;
  CurveMapping *cavity_curve;

  float start_normal_limit, start_normal_falloff;
  float view_normal_limit, view_normal_falloff;

  bool topology_use_brush_limit;
};

struct Cache {
  Settings settings;

  /** Cached factor for auto-masking modes that are implemented to process the entire mesh. */
  Array<float> factor;

  enum class OcclusionValue : int8_t {
    Unknown = 0,
    Visible = 1,
    Occluded = 2,
  };
  /**
   * Cached occlusion values for each vertex. Since calculating the occlusion is so expensive,
   * it's only calculated at the beginning of a stroke and stored for later.
   *
   * \todo Ideally the "unknown" state would be stored per node rather than per vertex, with a
   * lock-protected `Map<const bke::pbvh::Node , BitVector<>>` for example. Currently complications
   * with face domain auto-masking prevent this though. This array can't be a bitmap because it's
   * written to from multiple threads at the same time.
   */
  Array<OcclusionValue> occlusion;

  /**
   * Cached cavity factor values for each vertex.
   *
   * \note -1 means the vertex value still needs to be calculated.
   */
  Array<float> cavity_factor;

  /**
   * Calculates the cavity factor for a set of nodes.
   *
   * Has no effect on an individual node level if the factor for a vertex has already been
   * calculated. This data is best calculated outside of any loops that affect the stroke's
   * position, as the curvature calculation is sensitive to small changes, meaning processing
   * inside the normal brush update step may result in odd artifacts from ordering of position
   * updates.
   *
   * \note Should be called prior to any call that may use the cavity mode.
   */
  void calc_cavity_factor(const Depsgraph &depsgraph,
                          const Object &object,
                          const IndexMask &node_mask);
};

/**
 * Returns the auto-masking cache depending on the active tool. Used for code that can run both for
 * brushes and filter.
 */
const Cache *active_cache_get(const SculptSession &ss);

/**
 * Creates and initializes an auto-masking cache.
 *
 * For auto-masking modes that cannot be calculated in real time,
 * data is also stored at the vertex level prior to the stroke starting.
 */
std::unique_ptr<Cache> cache_init(const Depsgraph &depsgraph,
                                  const Paint &paint,
                                  const Brush *brush,
                                  Object &ob);

/** If the FilterCache#automask cache doesn't exist, create and return it. */
Cache &filter_cache_ensure(const Depsgraph &depsgraph, const Paint &paint, Object &ob);
/** If the StrokeCache#automask cache doesn't exist, create and return it. */
Cache &stroke_cache_ensure(const Depsgraph &depsgraph,
                           const Paint &paint,
                           const Brush *brush,
                           Object &ob);

bool mode_enabled(const Sculpt &sd, const Brush *br, eAutomasking_flag mode);
bool is_enabled(const Paint &paint, const Object &object, const Brush *br);

/**
 * Calculate all auto-masking influence on each vertex.
 */
void calc_vert_factors(const Depsgraph &depsgraph,
                       const Object &object,
                       const Cache &automasking,
                       const bke::pbvh::MeshNode &node,
                       Span<int> verts,
                       MutableSpan<float> factors);
inline void calc_vert_factors(const Depsgraph &depsgraph,
                              const Object &object,
                              const Cache *cache,
                              const bke::pbvh::MeshNode &node,
                              Span<int> verts,
                              MutableSpan<float> factors)
{
  PRF_scope(ProfileCategory::Editor);
  if (cache) {
    calc_vert_factors(depsgraph, object, *cache, node, verts, factors);
  }
}
void calc_grids_factors(const Depsgraph &depsgraph,
                        const Object &object,
                        const Cache &automasking,
                        const bke::pbvh::GridsNode &node,
                        Span<int> grids,
                        MutableSpan<float> factors);
inline void calc_grids_factors(const Depsgraph &depsgraph,
                               const Object &object,
                               const Cache *cache,
                               const bke::pbvh::GridsNode &node,
                               Span<int> grids,
                               MutableSpan<float> factors)
{
  if (cache) {
    calc_grids_factors(depsgraph, object, *cache, node, grids, factors);
  }
}
void calc_vert_factors(const Depsgraph &depsgraph,
                       const Object &object,
                       const Cache &automasking,
                       const bke::pbvh::BMeshNode &node,
                       const Set<BMVert *, 0> &verts,
                       MutableSpan<float> factors);
inline void calc_vert_factors(const Depsgraph &depsgraph,
                              const Object &object,
                              const Cache *cache,
                              const bke::pbvh::BMeshNode &node,
                              const Set<BMVert *, 0> &verts,
                              MutableSpan<float> factors)
{
  if (cache) {
    calc_vert_factors(depsgraph, object, *cache, node, verts, factors);
  }
}

/**
 * Calculate all auto-masking influence on each face.
 */
void calc_face_factors(const Depsgraph &depsgraph,
                       const Object &object,
                       OffsetIndices<int> faces,
                       Span<int> corner_verts,
                       const Cache &automasking,
                       const bke::pbvh::MeshNode &node,
                       Span<int> face_indices,
                       MutableSpan<float> factors);

}  // namespace ed::sculpt_paint::auto_mask

}  // namespace blender
