/************************************************************************
 *
 * Copyright (C) 2014-2024 IRCAD France
 * Copyright (C) 2014-2020 IHU Strasbourg
 *
 * This file is part of Sight.
 *
 * Sight is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Sight is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
 *
 ***********************************************************************/

#pragma once

#include "modules/viz/scene3d/adaptor/material.hpp"
#include "modules/viz/scene3d/adaptor/transform.hpp"

#include <data/material.hpp>
#include <data/point_list.hpp>

#include <viz/scene3d/adaptor.hpp>
#include <viz/scene3d/mesh.hpp>
#include <viz/scene3d/text.hpp>
#include <viz/scene3d/transformable.hpp>

#include <OGRE/OgreEntity.h>

namespace sight::data
{

class Material;

} // namespace sight::data
namespace sight::data
{

class mesh;

} // namespace sight::data

namespace sight::module::viz::scene3d::adaptor
{

/**
 * @brief This adaptor shows a point list using billboards generated by a geometry shader.
 *
 * This class handles the display of a data::point_list or a data::mesh. Both are exclusive you can only specify
 * one of the two.
 *
 * @section Slots Slots
 * - \b update_visibility(bool): Sets whether the points are visible or not.
 * - \b toggle_visibility(): Toggle whether the points are visible or not.
 * - \b show(): shows the video.
 * - \b hide(): hides the video.
 *
 * @section XML XML Configuration
 * @code{.xml}
    <service uid="..." type="sight::module::viz::scene3d::adaptor::point_list" >
        <in key="pointList" uid="..." />
        <config transform="..." textureName="..." radius="1.0" fontSource="DejaVuSans.ttf" fontSize="16"
               labelColor="#0xFFFFFF" visible="true" fixedSize="false" queryFlags="0x40000000" displayLabel="false"/>
    </service>
   @endcode
 *
 * @subsection Input Input
 * - \b pointList [sight::data::point_list] (optional): point list to display.
 * - \b mesh [sight::data::mesh] (optional): point based mesh to display. If the mesh contains any topology, it will be
 *      ignored and only raw vertices will be displayed. or add some fields.
 *
 * @subsection Configuration Configuration:
 * - \b autoresetcamera (optional, true/false, default=true): reset the camera when this mesh is modified, "true" or
 *"false".
 * - \b transform (optional, string, default=""): the name of the Ogre transform node where to attach the mesh, as it
 *      was specified in the transform adaptor.
 *      Either of the following (whether a material is configured in the XML scene or not) :
 * - \b materialTemplate (optional, string, default='Billboard_Default'): the name of the base Ogre material for the
 *      internally created material.
 * - \b textureName (optional, string, default=""): the name of the Ogre texture that the mesh will use.
 * - \b radius (optional, float, default=1.f): billboard radius.
 * - \b displayLabel (optional, bool, default=false): display the label points (default = false).
 * - \b labelColor (optional, hexadecimal, default=0xFFFFFF): color of the label in hexadecimal.
 * - \b color (optional, hexadecimal, default=#FFFFFFFF): color of the texture in hexadecimal.
 * - \b fixedSize (optional, bool, default=false): if true, the billboard will have a fixed size in screen space.
 * - \b queryFlags (optional, uint32, default=0x40000000): Picking flags. Points can be picked by pickers with a
 *      matching mask.
 * - \b visible (optional, bool, default=true): the pointlist visibility at start.
 * - \b fontSource (optional, string, default=DejaVuSans.ttf): true_t font (*.ttf) source file.
 * - \b fontSize (optional, unsigned int, default=16): font size in points.
 */
class point_list final :
    public sight::viz::scene3d::adaptor,
    public sight::viz::scene3d::transformable
{
public:

    /// Generates default methods as New, dynamicCast, ...
    SIGHT_DECLARE_SERVICE(point_list, sight::viz::scene3d::adaptor);

    /// Creates the adaptor, sets default parameters and initializes necessary members.
    point_list() noexcept;

    /// Destroys Ogre resource.
    ~point_list() noexcept final;

protected:

    /// Configures the adaptor.
    void configuring() final;

    /// Creates a mesh in the default Ogre resource group.
    void starting() final;

    /**
     * @brief Proposals to connect service slots to associated object signals.
     * @return A map of each proposed connection.
     *
     * Connect data::point_list::POINT_ADDED_SIG of s_POINTLIST_INPUT to service::slots::UPDATE
     * Connect data::point_list::POINT_REMOVED_SIG of s_POINTLIST_INPUT to service::slots::UPDATE
     * Connect data::point_list::MODIFIED_SIG of s_POINTLIST_INPUT to service::slots::UPDATE
     * Connect data::mesh::VERTEX_MODIFIED_SIG of s_MESH_INPUT to service::slots::UPDATE
     * Connect data::mesh::MODIFIED_SIG of s_MESH_INPUT to service::slots::UPDATE
     */
    service::connections_t auto_connections() const final;

    /// Updates the generated mesh.
    void updating() final;

    /// Deletes the mesh after unregistering the service, and shutting connections.
    void stopping() final;

    /**
     * @brief Sets the point list visibility.
     * @param _visible the visibility status of the point list.
     */
    void set_visible(bool _visible) final;

private:

    /**
     * @brief Get the point list visibility.
     * @return True if the point list is visible.
     */
    bool get_visibility() const;

    /**
     * @brief Updates the point list from a point list, checks if color, number of vertices have changed, and updates
     * them.
     * @param _point_list point list used for the update.
     */
    void update_mesh(const data::point_list::csptr& _point_list);

    /**
     * @brief Updates the point list from a mesh, checks if color, number of vertices have changed, and updates them.
     * @param _mesh mesh used for the update.
     */
    void update_mesh(const data::mesh::csptr& _mesh);

    /**
     * @brief Instantiates a new material adaptor.
     */
    module::viz::scene3d::adaptor::material::sptr create_material_service(const std::string& _mesh_id);

    /// Associates a new material to the managed point_list.
    /// With this method, point_list is responsible for creating a material.
    void update_material_adaptor(const std::string& _mesh_id);

    /**
     * @brief Attaches a node in the scene graph.
     * @param _node node to attach.
     */
    void attach_node(Ogre::MovableObject* _node);

    /// Detaches and destroy @ref m_entity from the scene graph.
    void detach_and_destroy_entity();

    /**
     * @brief Creates all the labels and attach them to the sceneNode vector.
     * @param _point_list point list used to retreive each point informations.
     */
    void create_label(const data::point_list::csptr& _point_list);

    /// Destroys all the labels and delete them from the sceneNode vector.
    void destroy_label();

    /// Defines whether the camera must be auto reset when a mesh is updated or not.
    bool m_auto_reset_camera {true};

    /// Defines whether the material was set by the user or not.
    bool m_custom_material {false};

    /// Contains the node in the scene graph.
    Ogre::Entity* m_entity {nullptr};

    /// Contains the material attached to the mesh.
    module::viz::scene3d::adaptor::material::sptr m_material_adaptor {nullptr};

    /// Contains the Ogre material related to the mesh.
    data::material::sptr m_material {nullptr};

    /// Defines the attached material's name.
    std::string m_material_template_name {"Billboard_Default"};

    /// Defines the attached texture adaptor UID.
    std::string m_texture_name;

    /// Contains the mesh support used to render the pointlist.
    sight::viz::scene3d::mesh::sptr m_mesh_geometry {nullptr};

    /// Defines the billboards radius.
    float m_radius {1.F};

    /// Defines if label numbers are displayed.
    bool m_display_label {false};

    /// Contains the RGB color for the label point color.
    data::color::sptr m_label_color {nullptr};

    /// Defines the mask for picking requests.
    std::uint32_t m_query_flags {Ogre::SceneManager::ENTITY_TYPE_MASK};

    /// Stores label of each point.
    std::vector<sight::viz::scene3d::text::sptr> m_labels;

    /// Stores label points nodes.
    std::vector<Ogre::SceneNode*> m_nodes;

    /// Contains the scene node where all of our manual objects are attached.
    Ogre::SceneNode* m_scene_node {nullptr};

    /// Defines the true_t font source file.
    std::string m_font_source {"DejaVuSans.ttf"};

    /// Defines the font size in points.
    std::size_t m_font_size {16};

    static constexpr std::string_view POINTLIST_INPUT = "pointList";
    static constexpr std::string_view MESH_INPUT      = "mesh";

    data::ptr<data::point_list, data::access::in> m_point_list {this, POINTLIST_INPUT, true};
    data::ptr<data::mesh, data::access::in> m_mesh {this, MESH_INPUT, true};
};

//------------------------------------------------------------------------------

inline bool point_list::get_visibility() const
{
    return m_entity != nullptr ? m_entity->getVisible() : visible();
}

} // namespace sight::module::viz::scene3d::adaptor.
