/*****************************************************************************
 * $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 STRUCTURALCOMPONENT_H
#define STRUCTURALCOMPONENT_H

#include "PhysicalModelIO.h"

// pml includes
#include "Component.h"
#include "Structure.h"
class Object3D;
#include "StructuralComponentProperties.h"

// other includes
#include <algorithm> // for the remove
#include <string>

//pmlschema forward declarations
namespace physicalModel {
class StructuralComponent;
}
/**
 * @ingroup group_cepmodeling_libraries_pml
 *
 * @brief
 * A structural component is composed either by cell or by atoms.
 *
 **/
class StructuralComponent : public Component {
public:
    /** Default Constructor.
     */
    StructuralComponent(PhysicalModel*);

    /** constructor from xml node: try to read and get the parmaters from xml */
    StructuralComponent(PhysicalModel* p, physicalModel::StructuralComponent xmlSC);

    /** constructor that allows one to name the structure.
     */
    StructuralComponent(PhysicalModel*, std::string);

    /// delete all the structures (call the deleteAllStructures method)
    ~StructuralComponent() override;

    /// get the number of structures
    unsigned int getNumberOfStructures() const;

    /**
    * Add a Structure in the list (and tells the structure to add this structural
    * component in its list).
    * @param s the structure to add
    * @param check (default value: true) tell if the method should call isCompatible(Structure *s) before inserting s
    */
    void addStructure(Structure* s, bool check = true);

    /**
    * Add a Structure in the list, only if it is not already in
    * (and in this case tells the structure to add this structural
    * component to its list).
    * @param s the structure to add
    * @return a boolean telling if s was added or not
    */
    bool addStructureIfNotIn(Structure* s);

    /** Check if a given structure is present in the list
     * @param s the structure to check
     * @return true if and only if the structure is already in the list
     */
    bool isStructureIn(Structure* s);

    /**
     * Remove a structure from the list (and tells the structure to remove this structural
      * component from its list).
      * Becareful: this method DOES NOT delete the object and/or free the memory.
     * @param s the ptr to the structure to remove
     */
    virtual void removeStructure(Structure* s);

    /**
     * this method free all the sub-components (i.e. delete all the sub component
     *  and clear the list).
     * After this methode getNumberOfSubStructures() should return 0
     */
    virtual void deleteAllStructures();

    /**
    * get a structure by its index (fisrt structure is at index 0)
    */
    Structure* getStructure(const unsigned int) const;

    /**
     *  get a structure by its name
     */
    Structure* getStructureByName(const std::string);

    /**
     *  get a structure by its unique index
     */
    Structure* getStructureByIndex(const unsigned int);

    /** print to an output stream in "pseudo" XML format (do nothing if there are no sub structures).
    */
    void xmlPrint(std::ostream&) const override;

    /** return true only if the parameter is equal to "StructuralComponent" */
    bool isInstanceOf(const char*) const override;

    /// get the total nr of cell of the component
    unsigned int getNumberOfCells() const override;

    /// get cell by order number (not cell index)
    Cell* getCell(unsigned int) const override;

    /** Return a StructuralComponent with all the atoms of this structural component.
     *	If this structural component is already a composed of atoms, return this.
     *	If it is composed of cells or mixed atoms and cells, return all the atoms
     *	used. Each atom is present only once in the resulting SC.
     */
    StructuralComponent* getAtoms();

    /// get the structural component properties of this SC
    StructuralComponentProperties* getProperties();

    /// Set the new color (using a StructuralComponentProperties::Color enum)
    void setColor(const StructuralComponentProperties::Color c);
    /// Set the new RGBA color
    void setColor(const double r, const double b, const double g, const double a);
    /// Set the new RGB color
    void setColor(const double r, const double b, const double g);
    /** Get the color
    	* @return an array of 4 doubles (red, blue, green and alpha values)
    	*/
    double* getColor() const;
    /** Get the color by its 4 componants r,g,b and a */
    void getColor(double* r, double* g, double* b, double* a) const;

    /// Return the color as a code (see StructuralComponentProperties::Color enum)
    StructuralComponentProperties::Color getStructuralComponentPropertiesColor() const;

    /// set the rendering mode
    void setMode(const RenderingMode::Mode);
    /// get the rendering mode
    RenderingMode::Mode getMode() const;
    /// tell if a specific rendering mode is visible or not
    bool isVisible(const RenderingMode::Mode mode) const override;
    /// set the visibility of a specific rendering mode
    void setVisible(const RenderingMode::Mode mode, const bool b) override;

    /** What this structural component is made of */
    enum ComposedBy {
        NOTHING, /**< there are no structure yet, so everything is possible */
        CELLS, /**< the structural component is made of cells */
        ATOMS /**< the structural component is made of atoms */
    };

    /** return the type of structure composing the structural component:
      * a structural component is either a list of cells or atoms, or of nothing if it is empty
      * (see enum ComposedBy).
      */
    virtual ComposedBy composedBy();

    /** return true if the given structure is compatible with what composes this structural component.
      * E.g. if the structural is made of cell, and the structure is a cell.
      */
    bool isCompatible(Structure*);

    /** optimize the I/O of the std:vector structures.
      * If you know the nr of structures to be in the SC, please give it here,
      * it will greatly speed the building of the structure
      */
    void plannedNumberOfStructures(const unsigned int);

    /// set the physical model (recursively to all cells or to all atoms)
    void setPhysicalModel(PhysicalModel*) override;

protected:
    /**
    * List of the structure representing this component, all the structure in this list are either all Atom or all Cell (no mix!)
    */
    std::vector <Structure*> structures;

    /** List of all the atoms of this structural component, build the first time.
     *	Return a StructuralComponent is called. */
    StructuralComponent* atomList;

};

// ------- INLINE -----------
inline void StructuralComponent::addStructure(Structure* s, bool check) {
    // add the structure in the list, only if it is compatible
    if (!check || isCompatible(s)) {
        structures.push_back(s);
        // tell the structure that it is a part of this sc
        s->addStructuralComponent(this);
    }
}
inline Structure* StructuralComponent::getStructure(const unsigned int i) const {
    if (i < structures.size()) {
        return structures[i];
    }
    else {
        return nullptr;
    }
}
inline Structure* StructuralComponent::getStructureByIndex(const unsigned int i) {
    auto it = structures.begin();
    while (it != structures.end() && (*it)->getIndex() != i) {
        it++;
    }
    if (it == structures.end()) {
        return nullptr;
    }
    else {
        return (*it);
    }
}
inline Structure* StructuralComponent::getStructureByName(const std::string n) {
    auto it = structures.begin();
    while (it != structures.end() && (*it)->getName() != n) {
        it++;
    }
    if (it == structures.end()) {
        return nullptr;
    }
    else {
        return (*it);
    }
}
inline unsigned int StructuralComponent::getNumberOfStructures() const {
    return (unsigned int) structures.size();
}
inline	void StructuralComponent::removeStructure(Structure* s) {
    if (s) {
        // remove it from the list
        auto it = std::find(structures.begin(), structures.end(), s);
        if (it != structures.end()) {
            structures.erase(it);
            // tell s that it is no more used by this structural component
            s->removeStructuralComponent(this);
        }
    }
}
inline bool StructuralComponent::isInstanceOf(const char* className) const {
    return (std::string(className) == std::string("StructuralComponent"));
}

inline void StructuralComponent::plannedNumberOfStructures(const unsigned int size) {
    structures.reserve(size);
}

inline StructuralComponentProperties* StructuralComponent::getProperties() {
    return ((StructuralComponentProperties*)properties);
}

#endif //STRUCTURALCOMPONENT_H
