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

#pragma once

/** \file
 * \ingroup bmesh
 */

#include "BKE_customdata.hh"

#include "bmesh_class.hh"

/**
 * When copying between different BMesh objects,
 * `copy_verts` & `copy_edges` should always be true.
 */
BMFace *BM_face_copy(BMesh *bm,
                     const BMCustomDataCopyMap &cd_face_map,
                     const BMCustomDataCopyMap &cd_loop_map,
                     BMFace *f,
                     bool copy_verts,
                     bool copy_edges);
BMFace *BM_face_copy(BMesh *bm, BMFace *f, bool copy_verts, bool copy_edges);

enum eBMCreateFlag {
  BM_CREATE_NOP = 0,
  /** Faces and edges only. */
  BM_CREATE_NO_DOUBLE = (1 << 1),
  /**
   * Skip custom-data - for all element types data,
   * use if we immediately write custom-data into the element so this skips copying from 'example'
   * arguments or setting defaults, speeds up conversion when data is converted all at once.
   */
  BM_CREATE_SKIP_CD = (1 << 2),
};

/**
 * \brief Main function for creating a new vertex.
 */
BMVert *BM_vert_create(BMesh *bm,
                       const float co[3],
                       const BMVert *v_example,
                       eBMCreateFlag create_flag);
/**
 * \brief Main function for creating a new edge.
 *
 * \note Duplicate edges are supported by the API however users should _never_ see them.
 * so unless you need a unique edge or know the edge won't exist,
 * you should call with \a no_double = true.
 */
BMEdge *BM_edge_create(
    BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, eBMCreateFlag create_flag);
/**
 * Main face creation function
 *
 * \param bm: The mesh
 * \param verts: A sorted array of verts size of len
 * \param edges: A sorted array of edges size of len
 * \param len: Length of the face
 * \param create_flag: Options for creating the face
 */
BMFace *BM_face_create(BMesh *bm,
                       BMVert *const *verts,
                       BMEdge *const *edges,
                       int len,
                       const BMFace *f_example,
                       eBMCreateFlag create_flag);
/**
 * Wrapper for #BM_face_create when you don't have an edge array
 */
BMFace *BM_face_create_verts(BMesh *bm,
                             BMVert **vert_arr,
                             int len,
                             const BMFace *f_example,
                             eBMCreateFlag create_flag,
                             bool create_edges);

/**
 * Kills all edges associated with \a f, along with any other faces containing those edges.
 */
void BM_face_edges_kill(BMesh *bm, BMFace *f);
/**
 * kills all verts associated with \a f, along with any other faces containing
 * those vertices
 */
void BM_face_verts_kill(BMesh *bm, BMFace *f);

/**
 * A version of #BM_face_kill which removes edges and verts
 * which have no remaining connected geometry.
 */
void BM_face_kill_loose(BMesh *bm, BMFace *f);

/**
 * Kills \a f and its loops.
 */
void BM_face_kill(BMesh *bm, BMFace *f);
/**
 * Kills \a e and all faces that use it.
 */
void BM_edge_kill(BMesh *bm, BMEdge *e);
/**
 * Kills \a v and all edges that use it.
 */
void BM_vert_kill(BMesh *bm, BMVert *v);

/**
 * \brief Splice Edge
 *
 * Splice two unique edges which share the same two vertices into one edge.
 *  (\a e_src into \a e_dst, removing e_src).
 *
 * \return Success
 *
 * \note Edges must already have the same vertices.
 */
bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src);
/**
 * \brief Splice Vert
 *
 * Merges two verts into one
 * (\a v_src into \a v_dst, removing \a v_src).
 *
 * \return Success
 *
 * \warning This doesn't work for collapsing edges,
 * where \a v and \a vtarget are connected by an edge
 * (assert checks for this case).
 */
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src);
/**
 * Check if splicing vertices would create any double edges.
 *
 * \note assume caller will handle case where verts share an edge.
 */
bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b);

/**
 * \brief Loop Reverse
 *
 * Changes the winding order of a face from CW to CCW or vice versa.
 *
 * \param cd_loop_mdisp_offset: Cached result of `CustomData_get_offset(&bm->ldata, CD_MDISPS)`.
 * \param use_loop_mdisp_flip: When set, flip the Z-depth of the mdisp,
 * (use when flipping normals, disable when mirroring, eg: symmetrize).
 */
void bmesh_kernel_loop_reverse(BMesh *bm,
                               BMFace *f,
                               int cd_loop_mdisp_offset,
                               bool use_loop_mdisp_flip);

/**
 * Avoid calling this where possible,
 * low level function so both face pointers remain intact but point to swapped data.
 * \note must be from the same bmesh.
 */
void bmesh_face_swap_data(BMFace *f_a, BMFace *f_b);

/**
 * \brief Join Connected Faces
 *
 * Joins a collected group of faces into one. Only restriction on
 * the input data is that the faces must be connected to each other.
 *
 * \return The newly created combine BMFace.
 *
 * \note If a pair of faces share multiple edges,
 * the pair of faces will be joined at every edge.
 *
 * \param bm: The bmesh.
 * \param faces: An array of faces to join.
 * \param totfaces: The length of the face array to join.
 * \param do_del: if true, remove the original faces, internal edges, and internal verts such that
 *    they are replaced by the new face.
 * \param r_double: A pointer to a BMFace* that is controls processing of doubled faces.
 * - When `r_double` is nullptr:
 *   - If a new face would be made which would double an existing face, then instead of creating a
 *     new face, the existing face will be reused and returned instead.
 *   - The calling function must not make ANY assumption about whether the returned BMFace* is
 *     new, or a reused face that may already have set header flags, contain custom data, etc.
 * - When `r_double` is a pointer to a BMFace*:
 *   - If the new join face is not a double of an existing face, then `r_double` is set to nullptr.
 *   - If the new join face doubles an existing face, then `r_double` is set to the existing face,
 *     and the return value is the newly created face. The double will NOT be removed, meaning the
 *     BMesh is in an invalid state, and the calling function must fix that inconsistency.
 *   - If an error occurs and nullptr is returned, `r_double` will be set to nullptr as well.
 *
 * \note this is a generic, flexible join faces function,
 * almost everything uses this, including #BM_faces_join_pair
 *
 * \note On callers asserting when `*r_double != nullptr`.
 * For some callers the existing algorithm does not check for or handle double faces.
 * This can result in invalid meshes being returned.
 * The returned value in `r_double` should be examined and if found,
 * the algorithm should be adjusted. Until this is changed, at least warn.
 * This comment can be removed when all callers handle this case.
 */
BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, bool do_del, BMFace **r_double);
/**
 * High level function which wraps both #bmesh_kernel_vert_separate and #bmesh_kernel_edge_separate
 */
void BM_vert_separate(BMesh *bm,
                      BMVert *v,
                      BMEdge **e_in,
                      int e_in_len,
                      bool copy_select,
                      BMVert ***r_vout,
                      int *r_vout_len);
/**
 * A version of #BM_vert_separate which takes a flag.
 */
void BM_vert_separate_hflag(
    BMesh *bm, BMVert *v, char hflag, bool copy_select, BMVert ***r_vout, int *r_vout_len);
void BM_vert_separate_tested_edges(
    BMesh *bm, BMVert *v_dst, BMVert *v_src, bool (*testfn)(BMEdge *, void *arg), void *arg);

/**
 * BMesh Kernel: For modifying structure.
 *
 * Names are on the verbose side but these are only for low-level access.
 */
/**
 * \brief Separate Vert
 *
 * Separates all disjoint fans that meet at a vertex, making a unique
 * vertex for each region. returns an array of all resulting vertices.
 *
 * \note this is a low level function, bm_edge_separate needs to run on edges first
 * or, the faces sharing verts must not be sharing edges for them to split at least.
 *
 * \return Success
 */
void bmesh_kernel_vert_separate(
    BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, bool copy_select);
/**
 * \brief Separate Edge
 *
 * Separates a single edge into two edge: the original edge and
 * a new edge that has only \a l_sep in its radial.
 *
 * \return Success
 *
 * \note Does nothing if \a l_sep is already the only loop in the
 * edge radial.
 */
void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, bool copy_select);

/**
 * \brief Split Face Make Edge (SFME)
 *
 * \warning this is a low level function, most likely you want to use #BM_face_split()
 *
 * Takes as input two vertices in a single face.
 * An edge is created which divides the original face into two distinct regions.
 * One of the regions is assigned to the original face and it is closed off.
 * The second region has a new face assigned to it.
 *
 * \par Examples:
 * <pre>
 *     Before:               After:
 *      +--------+           +--------+
 *      |        |           |        |
 *      |        |           |   f1   |
 *     v1   f1   v2          v1======v2
 *      |        |           |   f2   |
 *      |        |           |        |
 *      +--------+           +--------+
 * </pre>
 *
 * \note the input vertices can be part of the same edge. This will
 * result in a two edged face. This is desirable for advanced construction
 * tools and particularly essential for edge bevel. Because of this it is
 * up to the caller to decide what to do with the extra edge.
 *
 * \note If \a holes is NULL, then both faces will lose
 * all holes from the original face.  Also, you cannot split between
 * a hole vert and a boundary vert; that case is handled by higher-
 * level wrapping functions (when holes are fully implemented, anyway).
 *
 * \note that holes represents which holes goes to the new face, and of
 * course this requires removing them from the existing face first, since
 * you cannot have linked list links inside multiple lists.
 *
 * \return A BMFace pointer
 */
BMFace *bmesh_kernel_split_face_make_edge(BMesh *bm,
                                          BMFace *f,
                                          BMLoop *l_v1,
                                          BMLoop *l_v2,
                                          BMLoop **r_l,
#ifdef USE_BMESH_HOLES
                                          ListBase *holes,
#endif
                                          BMEdge *example,
                                          bool no_double);

/**
 * \brief Split Edge Make Vert (SEMV)
 *
 * Takes \a e edge and splits it into two, creating a new vert.
 * \a tv should be one end of \a e : the newly created edge
 * will be attached to that end and is returned in \a r_e.
 *
 * \par Examples:
 *
 * <pre>
 *                     E
 *     Before: OV-------------TV
 *                 E       RE
 *     After:  OV------NV-----TV
 * </pre>
 *
 * \return The newly created BMVert pointer.
 */
BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e);
/**
 * \brief Join Edge Kill Vert (JEKV)
 *
 * Takes an edge \a e_kill and pointer to one of its vertices \a v_kill
 * and collapses the edge on that vertex.
 *
 * \par Examples:
 *
 * <pre>
 *     Before:    e_old  e_kill
 *              +-------+-------+
 *              |       |       |
 *              v_old   v_kill  v_target
 *
 *     After:           e_old
 *              +---------------+
 *              |               |
 *              v_old           v_target
 * </pre>
 *
 * \par Restrictions:
 * KV is a vertex that must have a valance of exactly two. Furthermore
 * both edges in KV's disk cycle (OE and KE) must be unique (no double edges).
 *
 * \return The resulting edge, NULL for failure.
 *
 * \note This euler has the possibility of creating
 * faces with just 2 edges. It is up to the caller to decide what to do with
 * these faces.
 */
BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
                                         BMEdge *e_kill,
                                         BMVert *v_kill,
                                         bool do_del,
                                         bool check_edge_exists,
                                         bool kill_degenerate_faces,
                                         bool kill_duplicate_faces);
/**
 * \brief Join Vert Kill Edge (JVKE)
 *
 * Collapse an edge, merging surrounding data.
 *
 * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert
 * which only handle 2 valence verts,
 * this can handle any number of connected edges/faces.
 *
 * <pre>
 * Before: -> After:
 * +-+-+-+    +-+-+-+
 * | | | |    | \ / |
 * +-+-+-+    +--+--+
 * | | | |    | / \ |
 * +-+-+-+    +-+-+-+
 * </pre>
 */
BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm,
                                         BMEdge *e_kill,
                                         BMVert *v_kill,
                                         bool do_del,
                                         bool check_edge_exists,
                                         bool kill_degenerate_faces);
/**
 * \brief Join Face Kill Edge (JFKE)
 *
 * Takes two faces joined by a single 2-manifold edge and fuses them together.
 * The edge shared by the faces must not be connected to any other edges which have
 * Both faces in its radial cycle
 *
 * \par Examples:
 * <pre>
 *           A                   B
 *      +--------+           +--------+
 *      |        |           |        |
 *      |   f1   |           |   f1   |
 *     v1========v2 = Ok!    v1==V2==v3 == Wrong!
 *      |   f2   |           |   f2   |
 *      |        |           |        |
 *      +--------+           +--------+
 * </pre>
 *
 * In the example A, faces \a f1 and \a f2 are joined by a single edge,
 * and the euler can safely be used.
 * In example B however, \a f1 and \a f2 are joined by multiple edges and will produce an error.
 * The caller in this case should call #bmesh_kernel_join_edge_kill_vert on the extra edges
 * before attempting to fuse \a f1 and \a f2.
 *
 * \note The order of arguments decides whether or not certain per-face attributes are present
 * in the resultant face. For instance vertex winding, material index, smooth flags,
 * etc are inherited from \a f1, not \a f2.
 *
 * \return A BMFace pointer
 */
BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e);

/**
 * \brief Un-glue Region Make Vert (URMV)
 *
 * Disconnects a face from its vertex fan at loop \a l_sep
 *
 * \return The newly created BMVert
 *
 * \note Will be a no-op and return original vertex if only two edges at that vertex.
 */
BMVert *bmesh_kernel_unglue_region_make_vert(BMesh *bm, BMLoop *l_sep);
/**
 * A version of #bmesh_kernel_unglue_region_make_vert that disconnects multiple loops at once.
 * The loops must all share the same vertex, can be in any order
 * and are all moved to use a single new vertex - which is returned.
 *
 * This function handles the details of finding fans boundaries.
 */
BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int larr_len);
/**
 * This function assumes l_sep is a part of a larger fan which has already been
 * isolated by calling #bmesh_kernel_edge_separate to segregate it radially.
 */
BMVert *bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep);
