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

#include "RenderingMode.h"
#include "Properties.h"
#include <string>
#include <vector>
#include <algorithm> // for remove
class Cell;
class MultiComponent;
/**
  * @ingroup group_cepmodeling_libraries_pml
  *
  * @brief
  * A component is something that composed something and could also be
  *  a part of something.
  *
  *  (just in case you don't really understand, a good reference is "The hitch
  *  hiker's guide to the galaxy", Douglas Adams, 1952-2001. Thanks for reading
  *  this absolutely clear documentation!!!)
  *
 **/
class Component {
public:
    /** Default constructor, a component needs to know the PM it is in.
      * If not given name is initialized to the empty string
      */
    Component(PhysicalModel*, std::string n = "");

    /** Virtual destructor needed here as this is an abstract class (pure virtual) */
    virtual ~Component();

    /// tell if this component is exclusive or not
    bool isExclusive() const;

    /// set the exclusive flag
    void setExclusive(const bool);

    /// pure virtual method, implemented in the child-class
    virtual bool isInstanceOf(const char*) const = 0;

    /// get the name of the component
    const std::string getName() const;

    /// set the name of the component
    void setName(const std::string);

    /** print to an output stream in "pseudo" XML format.
     */
    virtual void xmlPrint(std::ostream&) const = 0;

    /// get the total nr of cell of the component
    virtual unsigned int getNumberOfCells() const = 0;

    /// conveniant method to get cell by order number (not cell index)
    virtual Cell* getCell(unsigned int) const = 0;

    /// return the state of a visibility mode
    virtual bool isVisible(const RenderingMode::Mode mode) const = 0;

    /// set the state of a visibility mode
    virtual void setVisible(const RenderingMode::Mode mode, const bool b) = 0;

    /** @name parent multi component admin
      */
    /*@{*/
    /// get the list of all the Multi Component that are using this Component
    std::vector <MultiComponent*> getAllParentMultiComponents();

    /// get the number of MultiComponent that are using this Component (= nr of parent component)
    unsigned int getNumberOfParentMultiComponents() const;

    /// get a particular MultiComponent that is using this Component (a particular parent component)
    MultiComponent* getParentMultiComponent(unsigned int);

    /// add a particular parent MultiComponent in the list
    void addParentMultiComponent(MultiComponent*);

    /// remove a particular parent MultiComponent
    void removeParentMultiComponent(MultiComponent*);
    /*@}*/

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

    /// get the physical model
    PhysicalModel* getPhysicalModel() const;

    /// get the component structural properties (guarantied to be non nullptr)
    Properties* getProperties();

protected:
    Properties* properties;

    /** this tell the parent components that this component is
     *  removed from memory.
     *  As the destructor is virtual, this method has to be called
     *  in all sub-classes destructors.
     */
    void removeFromParents();

    /// delete the "properties" pointer and set it to nullptr
    void deleteProperties();

private:
    bool exclusive;

    /** list of Component that are using this component
      *  (if another component is using this component, it is in this list)
      */
    std::vector <MultiComponent*> parentMultiComponentList;

};

// -------------- inline -------------
inline void Component::setExclusive(const bool b) {
    exclusive = b;
}
inline bool Component::isExclusive() const {
    return exclusive;
}
inline const std::string Component::getName() const {
    return properties->getName();
}
inline void Component::setName(const std::string n) {
    properties->setName(n);
}

// -------------- parent Multi Component admin  -------------

inline std::vector <MultiComponent*> Component::getAllParentMultiComponents() {
    return parentMultiComponentList;
}
inline unsigned int Component::getNumberOfParentMultiComponents() const {
    return (unsigned int) parentMultiComponentList.size();
}
inline MultiComponent* Component::getParentMultiComponent(unsigned int i) {
    if (i < parentMultiComponentList.size()) {
        return parentMultiComponentList[i];
    }
    else {
        return nullptr;
    }
}
inline void Component::addParentMultiComponent(MultiComponent* c) {
    parentMultiComponentList.push_back(c);
}
inline void Component::removeParentMultiComponent(MultiComponent* c) {
    auto it = std::find(parentMultiComponentList.begin(), parentMultiComponentList.end(), c);
    if (it != parentMultiComponentList.end()) {
        parentMultiComponentList.erase(it);
    }
}

inline void Component::setPhysicalModel(PhysicalModel* pm) {
    properties->setPhysicalModel(pm);
}

inline PhysicalModel* Component::getPhysicalModel() const {
    return properties->getPhysicalModel();
}

inline Properties* Component::getProperties() {
    return properties;
}


#endif //COMPONENT_H
