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

#include <vector>
#include <algorithm>

#include "Component.h"
/**
  * @ingroup group_cepmodeling_libraries_pml
  *
  * @brief
  * A multi-component stores other components, hence providing a way to have
  * an tree representation of components. Isn't that tricky?
  *
  * \note To delete and free the memory of all the sub components, you have to
  * call the deleteAllSubComponents() method.
  *
 **/
class MultiComponent : public Component {
public:
    /** Default Constructor */
    MultiComponent(PhysicalModel*);
    /// constructor that allows one to name the structure (provides a name)
    MultiComponent(PhysicalModel*, std::string);
    /// delete all the subcomponents (call the deleteAllSubComponents method)
    ~MultiComponent() override;

    /// return the number of subcomponents
    unsigned int getNumberOfSubComponents() const;

    /// get a subcomponent by its order number (index in the list of subcomponents)
    Component* getSubComponent(const unsigned int) const;

    /// add a component in the list of subcomponents (and set the isExclusive flag accordingly to the state of this MultiComponent)
    void addSubComponent(Component*);

    /**
     * Remove a component from the list.
     * Becareful: this method DOES NOT delete the object and/or free the memory.
      * This method ask the component c to remove this multicomponent from the list of its parent component
     * @param c the ptr to the structure to remove
     * @see removeAllSubComponent()
     */
    void removeSubComponent(Component* c);

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

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

    /** print to an output stream in "pseaudo" XML format (do nothing if there are no sub components).
      */
    void xmlPrint(std::ostream&) 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;

    /// conveniant method to get the sub component of the name given in parameter
    Component* getComponentByName(const std::string);

    /// return the state of a visibility mode in all the sub component (if at least one sub component is visible for this mode, it will return true; if none are visible it will return false).
    bool isVisible(const RenderingMode::Mode mode) const override;

    /// set the state of a visibility mode in all the sub component.
    void setVisible(const RenderingMode::Mode mode, const bool b) override;

    /// set the physical model (recursively)
    void setPhysicalModel(PhysicalModel*) override;

protected:
    /**
     * List of sub component
     */
    std::vector <Component*> components;
};

// -------------------- inline methods -------------------
inline unsigned int MultiComponent::getNumberOfSubComponents() const {
    return (unsigned int) components.size();
}
inline Component* MultiComponent::getSubComponent(const unsigned int i) const {
    if (i < components.size()) {
        return components[i];
    }
    else {
        return nullptr;
    }
}
inline void MultiComponent::addSubComponent(Component* c) {
    // add c in the list
    components.push_back(c);
    // add this in the list of c's composing component
    c->addParentMultiComponent(this);
    // set the isExclusive flag accordingly
    c->setExclusive(isExclusive());
}
inline void MultiComponent::removeSubComponent(Component* c) {
    auto it = std::find(components.begin(), components.end(), c);
    if (it != components.end()) {
        components.erase(it);
        c->removeParentMultiComponent(this);
    }
}
inline Component* MultiComponent::getComponentByName(const std::string n) {
    auto it = components.begin();
    Component* foundC = nullptr;
    while (it != components.end() && !foundC) {
        foundC = ((*it)->getName() == n) ? (*it) : nullptr;
        // look inside the component if it is a MultiComponent
        if (!foundC && (*it)->isInstanceOf("MultiComponent")) {
            foundC = ((MultiComponent*)(*it))->getComponentByName(n);
        }
        it++;
    }
    return foundC;
}
inline bool MultiComponent::isInstanceOf(const char* className) const {
    return (std::string(className) == std::string("MultiComponent"));
}

#endif //MULTICOMPONENT_H
