/*
 *  Copyright (c) 2000-2022 Inria
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  * Neither the name of the ALICE Project-Team nor the names of its
 *  contributors may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  Contact: Bruno Levy
 *
 *     https://www.inria.fr/fr/bruno-levy
 *
 *     Inria,
 *     Domaine de Voluceau,
 *     78150 Le Chesnay - Rocquencourt
 *     FRANCE
 *
 */

#ifndef GEOGRAM_VORONOI_RVD_CALLBACK
#define GEOGRAM_VORONOI_RVD_CALLBACK

#include <geogram/basic/common.h>
#include <geogram/voronoi/generic_RVD_vertex.h>
#include <geogram/mesh/mesh.h>
#include <geogram/basic/numeric.h>
#include <geogram/basic/attributes.h>
#include <geogram/basic/thread_sync.h>

namespace GEOGen {
    class SymbolicVertex;
    class Polygon;
    class ConvexCell;
}

namespace GEO {
    class RVDVertexMap;
    class Mesh;
}

/**
 * \file geogram/voronoi/RVD_callback.h
 * \brief Declaration of base types for implementing user-defined
 *  code that queries the cells of a restricted Voronoi diagram.
 */

namespace GEO {

    /***************************************************************/

    /**
     * \brief Baseclass for user functions called for each
     *  element (polygon or polyhedron) of a restricted Voronoi
     *  diagram traversal.
     */
    class GEOGRAM_API RVDCallback {
    public:

    /**
     * \brief RVDCallback constructor.
     */
    RVDCallback();

    /**
     * \brief RVDCallback destructor.
     */
    virtual ~RVDCallback();

    /**
     * \brief Called at the beginning of the RVD traversal.
     */
    virtual void begin();

    /**
     * \brief Called at the end of the RVD traversal.
     */
    virtual void end();

    /**
     * \brief Gets the index of the seed that corresponds to the current
     *  polygon/polyhedron.
     * \return The index of the seed that corresponds to the
     *  current polygon/polyhedron.
     * \details The current polygon/polyhedron is the intersection
     *  between a Voronoi cell (associted with a seed) and a simplex.
     */
    index_t seed() const {
        return seed_;
    }

    /**
     * \brief Gets the index of the simplex that corresponds to the
     *  current polygon/polyhedron.
     * \return The index of the simplex that corresponds to the
     *  current polygon/polyhedron. Points to a simplex in the mesh that the
     *  Voronoi diagram is restricted to.
     * \details The current polygon/polyhedron is the intersection between
     *  a Voronoi cell and a simplex.
     */
    index_t simplex() const {
        return simplex_;
    }

    /**
     * \brief Sets the spinlocks array.
     * \details In multithreading mode, a spinlocks array can
     *  be used to manage concurrent accesses.
     * \param[in] spinlocks a pointer to the Process::SpinLockArray
     *  or nullptr if no spinlocks are used.
     */
    void set_spinlocks(Process::SpinLockArray* spinlocks) {
        spinlocks_ = spinlocks;
    }

    protected:
    index_t seed_;
    index_t simplex_;
    Process::SpinLockArray* spinlocks_;
    };

    /***************************************************************/

    /**
     * \brief Baseclass for user functions called for each
     *  polygon of a surfacic restricted Voronoi diagram.
     * \details A surfacic restricted Voronoi diagram is the
     *  intersection between Voronoi cells and the triangles of
     *  a given triangulated mesh. The member functions of this
     *  class are called for each intersection between a Voronoi
     *  cell and a triangle.
     */

    class GEOGRAM_API RVDPolygonCallback : public RVDCallback {
    public:

        /**
         * \brief PolyhedronCallback constructor.
         */
        RVDPolygonCallback();

        /**
         * \brief PolyhedronCallback destructor.
         */
        ~RVDPolygonCallback() override;

        /**
         * \copydoc RVDCallback::begin()
         */
        void begin() override;

        /**
         * \copydoc RVDCallback::end()
         */
        void end() override;

        /**
         * \brief The default callback called for each polygon
         * \param[in] v index of current Delaunay seed
         * \param[in] t index of current mesh triangle
         * \param[in] C intersection between current mesh triangle
         *  and the Voronoi cell of \p v
         */
        virtual void operator() (
            index_t v,
            index_t t,
            const GEOGen::Polygon& C
        ) const;

    };

    /***************************************************************/

    /**
     * \brief Baseclass for user functions called for each
     *  polyhedron of a volumetric restricted Voronoi diagram.
     * \details A volumetric restricted Voronoi diagram is the
     *  intersection between Voronoi cells and the tetrahedra of
     *  a given tetrahedral mesh. The member functions of this
     *  class are called for each intersection between a Voronoi
     *  cell and a tetrahedron.
     */
    class GEOGRAM_API RVDPolyhedronCallback : public RVDCallback {
    public:

        /**
         * \brief PolyhedronCallback constructor.
         */
        RVDPolyhedronCallback();

        /**
         * \brief PolyhedronCallback destructor.
         */
        ~RVDPolyhedronCallback() override;

        /**
         * \copydoc RVDCallback::begin()
         */
        void begin() override;

        /**
         * \copydoc RVDCallback::end()
         */
        void end() override;


        /**
         * \brief The default callback called for each polyhedron
         * \details This default implementation routes the callback to the
         *  begin_polyhedron_internal(), end_polyhedron_internal(),
         *  begin_facet_internal(), end_facet_internal() and vertex_internal()
         *  functions (that in turn route the callbacks to their without
         *  "_internal" counterparts).
         * \param[in] v index of current Delaunay seed
         * \param[in] t index of current mesh tetrahedron
         * \param[in] C intersection between current mesh tetrahedron
         *  and the Voronoi cell of \p v
         */
        virtual void operator() (
            index_t v,
            index_t t,
            const GEOGen::ConvexCell& C
        ) const;


        /**
         * \brief Called at the beginning of each intersection polyhedron.
         * \details Each intersection polyhedron is defined as the intersection
         *   between a Voronoi cell and a tetrahedron.
         * \param[in] seed index of the seed associated with the Voronoi cell
         * \param[in] tetrahedron index of the tetrahedron
         */
        virtual void begin_polyhedron(index_t seed, index_t tetrahedron);

        /**
         * \brief Called at the beginning of each facet of each intersection
         *   polyhedron.
         * \details A facet can be a subset of either a bisector (defined by
         *   two seeds), or it can be a subset of a facet of a tetrahedron.
         * \param[in] facet_seed if the facet corresponds to a bisector,
         *   the index of the seed that defines the bisector, or index_t(-1)
         *   otherwise
         * \param[in] facet_tet if the facet corresponds to a facet
         *   of the current tetrahedron, the index of the tetrahedron
         *   adjacent to that facet, or index_t(-1) otherwise
         */
        virtual void begin_facet(index_t facet_seed, index_t facet_tet);

        /**
         * \brief Called for each vertex of the current facet.
         * \param[in] geometry a pointer to the coordinates of the vertex
         * \param[in] symb the symbolic representation of the vertex
         */
        virtual void vertex(
            const double* geometry, const GEOGen::SymbolicVertex& symb
        );

        /**
         * \brief Called at the end of each polyhedron facet.
         */
        virtual void end_facet();

        /**
         * \brief Called at the end of each polyhedron.
         */
        virtual void end_polyhedron();

        /**
         * \brief Gets the index of the tetrahedron that corresponds to the
         *  current polyhedron.
         * \return The index of the tetrahedron that corresponds to the
         *  current polyhedron.
         * \details The current polyhedron is the intersection between a Voronoi
         *  cell and a tetrahedron.
         */
        index_t tet() const {
            return simplex();
        }

        /**
         * \brief Gets the index of the seed that defines the bisector on which
         *  the current facet lies, or index_t(-1).
         * \return The index of the seed that defines the bisector on which
         *  the current facet lies, or index_t(-1).
         * \details Each facet is either on a bisector or on a tetrahedron
         *  facet. If the current facet is on a bisector, it is defined by
         *  seed() and facet_seed(), otherwise facet_seed() returns index_t(-1).
         */
        index_t facet_seed() const {
            return facet_seed_;
        }

        /**
         * \brief Gets the index of the tetrahedron adjacent to the current
         *  facet or index_t(-1) if there is no such facet.
         * \return the index of the tetrahedron adjacent to the current
         *  facet or index_t(-1).
         * \details Each facet is either on a bisector or on a tetrahedron
         *  facet. If the current facet is on a tetrahedron facet, then it
         *  is defined by tet() and facet_tet(), otherwise
         *  facet_tet() returns index_t(-1).
         */
        index_t facet_tet() const {
            return facet_tet_;
        }

        /**
         * \brief Specifies whether internal tetrahedron facets should be
         *  removed.
         * \details If set, a single polyhedron is generated for each
         *  (connected component) of the restricted Voronoi cells. If not
         *  set (default), each tetrahedron-Voronoi cell intersection
         *  generates a new polyhedron.
         * \param[in] x true if internal facets should be removed, false
         *  otherwise
         */
        void set_simplify_internal_tet_facets(bool x) {
            simplify_internal_tet_facets_ = x;
        }

        /**
         * \brief Specifies whether Voronoi facets should be simplified.
         * \details By default, the computed Voronoi facets are composed
         *  of multiple polyhedra that correspond to the intersection with
         *  the tetrahedra of the input volume mesh. They can be simplified
         *  and replaced by a single polygon. This implies simplifying the
         *  internal tetrahedron facets and using a mesh.
         * \param[in] x true if Voronoi facets should be simplified,
         *  false otherwise.
         */
        void set_simplify_voronoi_facets(bool x) {
            simplify_voronoi_facets_ = x;
            if(x) {
                set_simplify_internal_tet_facets(true);
                set_use_mesh(true);
            }
        }

        /**
         * \brief Specifies whether boundary facets should be simplified.
         * \details By default, the intersection between a Voronoi cell and
         *  the boundary is possibly composed of multiple polygons, that
         *  correspond to the initial polygons of the boundary. They can be
         *  simplified as a single polygon per Voronoi cell. This implies
         *  simplifying the internal tetrahedron facets, simplifying the
         *  Voronoi facets and using a mesh.
         * \param[in] x true if boundary facets should be simplified,
         *  false otherwise.
         * \param[in] angle_threshold an edge shared by two adjacent facets
         *  is suppressed if the angle between the facet normals is smaller
         *  than \p angle_threshold
         */
        void set_simplify_boundary_facets(bool x, double angle_threshold=45.0) {
            simplify_boundary_facets_ = x;
            if(x) {
                set_simplify_voronoi_facets(true);
                simplify_boundary_facets_angle_threshold_ = angle_threshold;
            } else {
                simplify_boundary_facets_angle_threshold_ = 0.0;
            }
        }

        /**
         * \brief Specifies whether non-convex facets should be tessellated.
         * \param[in] x true if non-convex facets should be tessellated,
         *  false otherwise.
         * \details Only taken into account if set_use_mesh(true) was called.
         */
        void set_tessellate_non_convex_facets(bool x) {
            tessellate_non_convex_facets_ = x;
        }


        /**
         * \brief Specifies whether a mesh should be built for each
         *  traversed polyhedron.
         * \details The build mesh can then be modified (e.g., simplified)
         *  by overloading process_mesh().
         * \param[in] x true if a mesh should be build, false otherwise
         */
        void set_use_mesh(bool x);

        /**
         * \brief Sets the dimension of the internal mesh if need be.
         * \details This function is called automatically by
         *  RestrictedVoronoiDiagram::for_each_polyhedron().
         * \param[in] dim the dimension of the mesh (3 for 3d).
         */
        void set_dimension(index_t dim) {
            if(use_mesh_) {
                mesh_.vertices.set_dimension(dim);
            }
        }

    protected:
        /**
         * \brief Filters callbacks between operator() and client callbacks.
         * \details This is used to implement cells simplifications (remove
         *  internal boundaries and intersections with tetrahedra).
         * \see begin_polyhedron()
         */
        virtual void begin_polyhedron_internal(
            index_t seed, index_t tetrahedron
        );

        /**
         * \brief Filters callbacks between operator() and client callbacks.
         * \details This is used to implement cells simplifications (remove
         *  internal boundaries and intersections with tetrahedra).
         * \see begin_facet()
         */
        virtual void begin_facet_internal(
            index_t facet_seed, index_t facet_tet
        );

        /**
         * \brief Filters callbacks between operator() and client callbacks.
         * \details This is used to implement cells simplifications (remove
         *  internal boundaries and intersections with tetrahedra).
         * \see vertex()
         */
        virtual void vertex_internal(
            const double* geometry, const GEOGen::SymbolicVertex& symb
        );

        /**
         * \brief Filters callbacks between operator() and client callbacks.
         * \details This is used to implement cells simplifications (remove
         *  internal boundaries and intersections with tetrahedra).
         * \see end_facet()
         */
        virtual void end_facet_internal();

        /**
         * \brief Filters callbacks between operator() and client callbacks.
         * \details This is used to implement cells simplifications (remove
         *  internal boundaries and intersections with tetrahedra).
         * \see end_polyhedron()
         */
        virtual void end_polyhedron_internal();

        /**
         * \brief If use_mesh is set, then this function is called for
         *  each generated mesh.
         * \details Default implementation simplifies the mesh based on
         *  sipmlify_xxx flags, then it calls user callbacks
         *  begin_facet(), end_facet(), vertex() for each facet of the mesh,
         *  as well as begin_polyhedron() and end_polyhedron() once per mesh.
         *  Derived classes may modify (e.g., simplify) the mesh before calling
         *  user callbacks.
         */
        virtual void process_polyhedron_mesh();

    protected:

        index_t facet_seed_;
        index_t facet_tet_;
        index_t last_seed_;

        bool simplify_internal_tet_facets_;
        bool simplify_voronoi_facets_;
        bool simplify_boundary_facets_;
        double simplify_boundary_facets_angle_threshold_;
        bool tessellate_non_convex_facets_;

        bool use_mesh_;
        bool facet_is_skipped_;

        Mesh mesh_;
        Attribute<GEOGen::SymbolicVertex> mesh_vertex_sym_;
        Attribute<index_t> mesh_facet_seed_;
        Attribute<index_t> mesh_facet_tet_;
        RVDVertexMap* vertex_map_;
        vector<index_t> base_current_facet_;
    };

    /***************************************************************/

    /**
     * \brief Constructs a polyhedral mesh from a restricted Voronoi diagram.
     * \details Its member functions are called for each RVD polyhedron,
     *  i.e. the intersections between the volumetric mesh tetrahedra and
     *  the Voronoi cells. Based on set_simplify_xxx(), a smaller number of
     *  polyhedra can be generated.
     */
    class GEOGRAM_API BuildRVDMesh : public RVDPolyhedronCallback {
    public:

        /**
         * \brief BuildRVDMesh constructor.
         * \param[out] output_mesh a reference to the generated mesh
         */
        BuildRVDMesh(Mesh& output_mesh);

        /**
         * \brief BuildRVDMesh destructor.
         */
        ~BuildRVDMesh() override;

        /**
         * \brief Specifies whether ids should be generated.
         * \details If enabled, unique vertex ids, seed ids and cell ids are
         *  generated and attached to the mesh vertices ("vertex_id" attribute)
         *  and mesh facets ("seed_id" and "cell_id" attributes) respectively.
         *  There is a cell_id per generated polyhedron, and seed_id refers to
         *  the Voronoi seed (the point that the Voronoi cell is associated
         *  with).
         * \param[in] x true if ids should be generated, false
         *  otherwise (default)
         */
        void set_generate_ids(bool x);

        /**
         * \brief Defines the optional shrink factor for cells.
         * \param[in] x shrink factor, 0.0 means no shrink, 1.0 means
         *  maximum shrink (cell reduced to a point).
         */
        void set_shrink(double x);

        /**
         * \brief Called at the beginning of RVD traversal.
         */
        void begin() override;

        /**
         * \brief Called at the end of RVD traversal.
         */
        void end() override;

        /**
         * \brief Called at the beginning of each RVD polyhedron.
         * \param[in] seed , tetrahedron the (seed,tetrahedron) pair that
         *  defines the RVD polyhedron, as the intersection between the Voronoi
         *  cell of the seed and the tetrahedron.
         */
        void begin_polyhedron(index_t seed, index_t tetrahedron) override;

        /**
         * \copydoc RVDPolyhedronCallback::begin_facet()
         */
        void begin_facet(index_t facet_seed, index_t facet_tet_facet) override;

        /**
         * \copydoc RVDPolyhedronCallback::vertex()
         */
        void vertex(
            const double* geometry, const GEOGen::SymbolicVertex& symb
        ) override;

        /**
         * \copydoc RVDPolyhedronCallback::end_facet()
         */
        void end_facet() override;

        /**
         * \copydoc RVDPolyhedronCallback::end_polyhedron()
         */
        void end_polyhedron() override;

        /**
         * \copydoc RVDPolyhedronCallback::process_polyhedron_mesh()
         */
        void process_polyhedron_mesh() override;

    private:
        vector<index_t> current_facet_;
        Mesh& output_mesh_;
        RVDVertexMap* global_vertex_map_;
        RVDVertexMap* cell_vertex_map_;
        double shrink_;
        bool generate_ids_;
        Attribute<int> cell_id_;
        Attribute<int> seed_id_;
        Attribute<int> vertex_id_;
        Attribute<int> facet_seed_id_;
        index_t current_cell_id_;
    };


    /***************************************************************/

}

#endif
