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

#include "PhysicalModelIO.h"
#include <vector>
#include <algorithm> // for the remove
#include "StructureProperties.h"
class StructuralComponent;
/**
 * @ingroup group_cepmodeling_libraries_pml
 *
 * @brief
 * Pure virtual class that represent an element of the structure.
 * This implies that every structure could be represented in 3D and
 * is a part of a structural component.
 *
 **/
class Structure {
public:
    /** Base constructor */
    Structure()  {
        hasIndex = false;
    }
    /** Virtual destructor needed here as this is an abstract class (pure virtual) */
    virtual ~Structure() = default;

    /** print to an output stream in "pseaudo" XML format.
     *  this method is called by the structural component that includes this structure.
       */
    virtual void xmlPrint(std::ostream&, const StructuralComponent*) = 0;

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

    /// indicate if the Structure has an index (which is not the case all the time)
    bool hasIndex;

    /// get the structure unique index (stored in its property)
    unsigned int getIndex() const;

    /** set the index.
     *  The index <b>have to be unique</b> otherwise this method
     *  has no effect.
     *  The sub-classes method will check that this index is not in use.
     *  @return true only if the index of the structure was changed
     */
    virtual bool setIndex(const unsigned int);

    /// get the type of index
    StructureProperties::GeometricType getType() const;

    /// get the list of all the StructuralComponent that are using this structure
    std::vector <StructuralComponent*> getAllStructuralComponents();

    /// get the number of StructuralComponent that are using this structure
    unsigned int getNumberOfStructuralComponents() const;

    /// get a particular StructuralComponent that is using this structure
    StructuralComponent* getStructuralComponent(unsigned int i);

    /// add a particular StructuralComponent in the list
    virtual void addStructuralComponent(StructuralComponent*);

    /// remove a particular StructuralComponent from the list
    void removeStructuralComponent(StructuralComponent*);

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

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

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

protected:

    /** Property of the current structure */
    StructureProperties* properties{nullptr};

private:

    /// list of StructuralComponent that are using this structure
    std::vector <StructuralComponent*> mySCs;

};

// -------------------- inline ---------------------
inline std::vector <StructuralComponent*> Structure::getAllStructuralComponents() {
    return mySCs;
}
inline unsigned int Structure::getNumberOfStructuralComponents() const {
    return (unsigned int) mySCs.size();
}
inline StructuralComponent* Structure::getStructuralComponent(unsigned int i) {
    if (i < mySCs.size()) {
        return mySCs[i];
    }
    else {
        return nullptr;
    }
}
inline void Structure::addStructuralComponent(StructuralComponent* sc) {
    mySCs.push_back(sc);
}

inline void Structure::removeStructuralComponent(StructuralComponent* sc) {
    auto it = std::find(mySCs.begin(), mySCs.end(), sc);
    if (it != mySCs.end()) {
        mySCs.erase(it);
    }
}


#endif     //  STRUCTURE_H
