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

#include "Structure.h"
#include "StructuralComponent.h"

//pmlschema forward declarations
namespace physicalModel {
class Cell;
}

class CellProperties;
/**
  * @ingroup group_cepmodeling_libraries_pml
  *
  * @brief
  * A cell has an unique index in the physical model object, is composed by atoms, and different basic properties.
  * It is the most basic component composing a physical model.
  *
 **/
class Cell : public Structure, public StructuralComponent {
public:
    /** constructor that generates a unique index
     *  @param myPM the physical model the atom belongs to
     *  @param t the type of the cell
     */
    Cell(PhysicalModel* myPM, const StructureProperties::GeometricType t);

    /** constructor from xml node: try to read and get the parameters from xml
        *  @param myPM the physical model the atom belongs to
        *  @param xmlCell the xml node to read to get the information
        *  @param sc the direct parent structural component that owns this cell
        */
    Cell(PhysicalModel* myPM, physicalModel::Cell xmlCell, StructuralComponent* sc);

    /** When you know the index of the cell, use this constructor.
     *  @param myPM the physical model the atom belongs to
     *  @param t the type of the cell
     *  @param ind give the unique index
     */
    Cell(PhysicalModel* myPM, const StructureProperties::GeometricType t, const unsigned int ind);

    /// the destructor, my tailor. BECAREFUL: the atoms should not not be deleted here...
    ~Cell() override;

    /** print to an output stream in "pseudo" XML format.
     * If the StructuralComponent that calls this method is not the first in the
     *  list of composing SC, then a cellRef tag is printed (otherwise the list
     *  of atom is printed).
     */
// as Cell inherits both Structure and StructuralComponent, it is correct to override
// the xmlPrint in StructuralComponent while reimplementing xmlPrint from Structure
#if defined(__has_warning)
#  pragma GCC diagnostic push
#  if __has_warning("-Woverloaded-virtual")
#     pragma GCC diagnostic ignored "-Woverloaded-virtual"
#  endif
#endif
    void xmlPrint(std::ostream&, const StructuralComponent*) override;
#if defined(__has_warning)
#  pragma GCC diagnostic pop
#endif


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

    /**
     * This method overload the one defined in StructuralComponent.
     * The difference here is that the atoms composing the cell ARE NOT delete, still the
     * list is cleared.
     * After a call to this method getNumberOfSubStructures() should return 0
     */
    void deleteAllStructures() override;

    /// return the property
    CellProperties* getProperties();

    /// overloaded from Structural component, always return StructuralComponent::ATOMS
    StructuralComponent::ComposedBy composedBy() override;

    /** is this sc the one that will be the one that will make the cell to print out all its data
      * or is this a sc that will just print out the cell ref?
      */
    bool makePrintData(const StructuralComponent*);

    /** 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
     */
    bool setIndex(const unsigned int) override;

    /** compute the normal of the facet
     *  Warning : Only available for QUAD and TRIANGLE type cells
     */
    double* normal();

    /// Return a new structural component directly representing the Cell as a list of quad faces (when possible)
    StructuralComponent* getQuadFaces();

    /// Return a new structural component directly representing the Cell as a list of triangular faces (when possible)
    StructuralComponent* getTriangleFaces();

    ///Compute the surface of the cell
    double surface();

    /// Compute the volume of the cell
    double volume();

    /**
     * Compute the volume of a triangle with the origin of the coordinate system.
     * If the triangle is A,B,C it computes the volume of the tetrahedron OABC.
     * @see Zhang, C., and Chen, T., Efficient feature extraction for 2D/3D objects in mesh representation
     *
     * @param p1 position of Triangle Atom
     * @param p2 position of Triangle Atom
     * @param p3 position of Triangle Atom
     *
     * @return double a signed volume
     */
    double signedVolumeTriangle(double p1[3], double p2[3], double p3[3]);

    /// Get the direct parent structural that own this cell
    const StructuralComponent* getParentSC() {
        return this->parentSC;
    }

    /// add a particular StructuralComponent in the list (and set parentSC if null)
    void addStructuralComponent(StructuralComponent*) override;

private:
    /// Keep a reference to the direct parent structural component
    StructuralComponent* parentSC;

};

// ------- INLINE -----------
inline bool Cell::isInstanceOf(const char* className) const {
    return (std::string(className) == std::string("Cell"));
}

inline Cell::ComposedBy Cell::composedBy() {
    return StructuralComponent::ATOMS;
}

inline void Cell::addStructuralComponent(StructuralComponent* sc) {
    Structure::addStructuralComponent(sc);
    if (!parentSC) {
        parentSC = sc;
    }
}

#endif //CELL_H
