/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK 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 version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
#ifndef PML_COMPONENT_H
#define PML_COMPONENT_H

// stl includes
#include <map>

// QT forward declaration
class QString;

// Qt includes
#include <QObject>

// lib pml forward declaration
class PhysicalModel;
class StructuralComponent;
class MultiComponent;
class Atom;
class Cell;

// vtk forward declarations
class vtkUnstructuredGrid;
class vtkPolyVertex;
class vtkSelection;
class vtkIdTypeArray;

// CamiTK forward declaration
namespace camitk {
class AbortException;
}

// CamiTK include
#include <MeshComponent.h>
#include "PMLComponentAPI.h"

// VTK includes
// disable warning generated by clang about the surrounded headers
#include <CamiTKDisableWarnings>
#include <vtkExtractSelection.h>
#include <CamiTKReEnableWarnings>

#include <vtkSelectionNode.h>

namespace std {
/** As the PhysicalModel atom index can be different to the node index (continuity in id is not mandatory in PML)
 *  a map is needed to link the atom index with its corresponding vtkPoint Id
 * definition of a couple (=STL pair) [Atom *, vtkIdType]
 * this associates an atom to its vtkPoint Id
  */
using AtomPointIdPair = std::pair<const Atom*, const vtkIdType>;
/** definition of the association set (=map in STL) AtomPointIdMap.
  * AtomPointIdMap associate all the Atom with their vtkPoint Id.
  * The key is the atom, so that it is simple to retrieve its corresponding point Id
  * (which is supposed to be the most often used functionnality).
  */
using AtomPointIdMap = std::map <const Atom*, const vtkIdType>;
/** the iterator corresponding to the AtomPointIdMap map */
using AtomPointIdMapIterator = std::map <const Atom*, const vtkIdType>::iterator;
}

/**
 * @ingroup group_cepmodeling_components_pml
 *
 * @brief
 * This class manages a physical model (PML) CamiTK component.
 *
 * This class inherits from camitk::MeshComponent.
 * It creates the 3D VTK structures corresponding to the PML structures (atoms, cells, structural components),
 * allows one to quickly selecting PML element and highlight them in the 3D structure (using vtkSelection extractors) and
 * display usefull PML properties (given as XML attributes in the PML files) as @see camitk::Property.
 *
 * @note There are several vtkSelection extractor (2 per structural component in the PML structure).
 * One for the POINTS selection (the atoms) and one for the CELLS (the PML Cells).
 * They are configured to display the corresponding selection in the Structural Component color property (if provided).
 *
 * @note The action PMLExploreAction uses this selection methods to allows selecting PML structure elements throught a simple
 * tree view widget.
 *
 **/
class PML_COMPONENT_API PMLComponent : public camitk::MeshComponent {
    Q_OBJECT

public:
    /** Construct the PML component directly from the .pml xml file.
     *  This method may throw an AbortException if a problem occurs.
     *
     * @param file The input .pml xml file to build the physical model from.
     */
    PMLComponent(const QString& file);

    /** Create the PML component directly from the PhysicalModel (also have to give the original pml filename)
      * @param p the physicalModel (already instantiated and build)
      * @param originalFile the filename from which p was build
      */
    PMLComponent(PhysicalModel* p, const QString& originalFile);

    /// destructor
    virtual ~PMLComponent();

    /// get the pixmap for physical model component
    virtual QPixmap getIcon();

    /// update top-level properties and synchronize the physical model values
    virtual void propertyValueChanged(QString name);

    /** Update the selection flag (this method is overridden in order to show the default
     *  modeling action when the component is selected for the first time).
     *
     * @param b the value of the flag (true means "is selected")
     * @param recursive if true (default), also updates the children Component selection flags.
     */
    virtual void setSelected(const bool b, const bool recursive = true);

    ///@name Specific methods for PML component
    ///@{
    /// Return the Physical Model object associated to the component
    PhysicalModel* getPhysicalModel();

    /// get the point Id from the corresponding atom, this is the opposite of pml->getAtom(id)
    vtkIdType getPointId(const Atom* a);

    /// initialize information (properties) and geometric representation from the physical model
    void init();
    ///@}

    /// @name Selection methods
    ///@{

    /// Select the given cell in the 3D structure
    /// @param cell The cell to highlight
    /// @param showAtomGlyph if true show the atom glyph
    void selectCell(Cell* cell, bool showAtomGlyph);

    /// Select the given atom in the 3D structure
    /// @param atom The atom to highlight
    void selectAtom(const Atom* atom);

    /// Select the given structural component in the 3D structure by highlighting all its atoms and cells
    /// @param sc The structural component to highlight
    /// @param showAtomGlyph if true show the atom glyph
    void selectSC(StructuralComponent* sc, bool showAtomGlyph);

    /// Select the given multi component in the 3D structure by highlighting all its structural components
    /// @param mc The multi component to highlight
    /// @param showAtomGlyph if true show the atom glyph
    void selectMC(MultiComponent* mc, bool showAtomGlyph);

    /// Unselect all the SC, MC, Atoms and Cells previously selected on the 3D structure
    void unselectItems();

    /// Update the POINTS and CELLS selection for the whole 3D structure by telling the corresponding extractors
    /// new vtkIds have been added to the selection lists.
    void updateSelection();

    /// Refresh the display of the component according to the selected 3D items by the user.
    /// It also shades the component so that selection can be visible.
    /// Warning: this methods contains a forced/active refresh (breaking CamiTK programming guidelines)
    /// DO NOT CALL this method from a component, this method SHOULD ONLY be called from an action
    /// Raison d'être : this method force the vtk pipeline updates (needed when, for example, the points moved)
    ///                 and then refresh the 3D viewer. The later is against CamiTK programming guidelines.
    ///                 It is written here (in the component) because it is a helper method for actions
    ///                 that would need to refresh the mesh.
    void refreshDisplay();

    /**
     * Add a selection (inherited from MeshComponent inorder to show a glyph on selected atoms).
     *
     * If the name of the selection already exists, the data of the existing
     * selection are updated according to the SelectionPolicy flag.
     *
     * @param name name of the selection
     * @param fieldType field type of the selection (one of vtkSelectionNode::SelectionField)
     * @param contentType content type (one of vtkSelectionNode::SelectionContent)
     * @param array array of the selection
     * @param policy policy to update the existing selection
     * @return the index of the added selection in the selection list
     */
    virtual int addSelection(const QString& name, int fieldType, int contentType,  vtkSmartPointer< vtkAbstractArray > array, camitk::MeshSelectionModel::InsertionPolicy policy = camitk::MeshSelectionModel::REPLACE);


    ///@}

protected:
    /// create and initialize dynamic properties using the global xml attributes from the PhysicalModel object
    virtual void initDynamicProperties();

private:

    /// The library pml object that uses pmlschema to read the pml information for the xml file.
    PhysicalModel* physicalModel;

    /// the PMLComponent icon
    static QPixmap* myPixmap;

    /// the first selection should trigger the pml exporer action (default action)
    bool neverSelected;

    /// @name 3D structure creation
    ///@{

    /// The 3D VTK cloud points of the atoms read
    vtkSmartPointer<vtkPoints> thePoints;

    /// The 3D grid containing all the cells of the PML
    vtkSmartPointer<vtkUnstructuredGrid> mainGrid;

    /// the Atom / vtkPoint Id map
    std::AtomPointIdMap atomPointIdMap;

    /// Create the 3D VTK structure representing the mesh.
    void create3DStructure();

    /// Create the 3D structure of the given MultiComponent and its children (recursively).
    /// @param mc: The MultiComponent to build a 3D structure from.
    void parseMultiComponent(MultiComponent* mc);

    /// Create a 3D vtkCell corresponding to a structural component's cell
    /// @param cell: The PML Cell, created by the library pml
    /// @return A pointor to the vtkCell structure.
    /// @note thePoints must not be nullptr before calling this method (we create the vtkCell using points references)
    vtkSmartPointer<vtkCell> cellToVTK(Cell* cell);

    /// Create a 3D vtkPolyVertex of a structural component which ONLY contains atoms ref
    /// @param sc: The PML structural component object that only contains atoms references.
    /// @return a pointor to the vtkCell structure.
    /// @note this method may be removed as soon as we change the PML XSD schema.
    /// @note thePoints must not be nullptr before calling this method (we create the vtkPolyVertex using points references)
    vtkSmartPointer<vtkPolyVertex> atomSCToVTK(StructuralComponent* sc);

    ///@}

    /// @name 3D structure element selection
    ///@{

    /// The vtkIdTypeArray for atom selection (contains all the id array that corresponds to a selected atom)
    vtkSmartPointer<vtkIdTypeArray> selectedAtomIdArray;

    /// The vtkSelection for atom selection
    vtkSmartPointer<vtkSelection> selectedAtomSelection;

    /// The association structural component <-> vtkIdTypeArray for Cells selection.
    /// Structural component name is the key.
    /// scCellIdArrayMap(sc->getName().c_str()) contains the id of all the cells selected in the StructuralComponent sc
    QMap<QString, vtkSmartPointer<vtkIdTypeArray> > scCellIdArrayMap;

    /// The association structural component <-> vtkSelection for Cells selection.
    /// Structural component name is the key.
    /// scCellSelectionMap(sc->getName().c_str()) manages the vtkSelection of the StructuralComponent sc
    QMap<QString, vtkSmartPointer<vtkSelection> > scCellSelectionMap;

    /// Create a new VTK selection pipeline (selection, extractor, mapper, actor) for a given SC
    /// if it does not allready exists.
    /// A VTK selection pipeline is needed to represent the given structural component
    /// There is one VTK selection pipeline per SC.
    /// @param sc the input structural component to create the vtk extract selection from.
    void createCellSelectionVTKPipeline(const StructuralComponent* sc);

    /// Create the VTK selection pipeline (selection, extractor, mapper, actor) needed to
    /// represent the selection of atoms.
    void createAtomSelectionVTKPipeline();
    ///@}


};

// -------------------- Atom / VtkPoint Id Map --------------------
inline vtkIdType PMLComponent::getPointId(const Atom* a) {
    if (!a) {
        return 0;
    }
    std::AtomPointIdMapIterator result = atomPointIdMap.find(a);
    return (result == atomPointIdMap.end()) ? 0 : (result->second);
}


#endif


