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

//pmlschema forward declarations
namespace physicalModel {
class Atom;
class AtomProperties;
}

#include "PhysicalModelIO.h"
#include "StructureProperties.h"

/**
  * @ingroup group_cepmodeling_libraries_pml
  *
  * @brief
  * This class manages all the properties attached to an atom.
  *
 **/
class AtomProperties : public StructureProperties {
public:
    /** Default constructor : set the position to the origin, and generate an unique index
     *  @param myPM the physical model the atom belongs to
     */
    AtomProperties(PhysicalModel* myPM);

    /** constructor from xml node: try to read and get the properties from xml
     *  @param myPM the physical model the atom belongs to
     *  @param xmlAtomProp the xsd node of properties for this atom.
     */
    AtomProperties(PhysicalModel* myPM, physicalModel::AtomProperties xmlAtomProp);

    /** set the position to the origin
     *  @param myPM the physical model the atom belongs to
     *  @param ind an unique index
     */
    AtomProperties(PhysicalModel* myPM, const unsigned int ind);

    /** generate an unique index.
     *  @param myPM the physical model the atom belongs to
     *  @param pos the initial position
     */
    AtomProperties(PhysicalModel* myPM, const double pos[3]);

    /** everything is given here
     *  @param myPM the physical model the atom belongs to
     *  @param pos the initial position
     *  @param ind an unique index
     */
    AtomProperties(PhysicalModel* myPM, const unsigned int ind, const double pos[3]);

    /// the destructor...
    ~AtomProperties() override;

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

    /** Reinitialize the unique index to zero (usually that what you want to do when you
        * start to load a new PhysicalModel
        */
    static void resetUniqueIndex();

    /// get the position of the atom (array of 3 doubles)
    void getPosition(double pos[3]) const;

    /// set the position of the atom
    void setPosition(const double [3]);

    /// set the position of the atom
    void setPosition(const double, const double, const double);

    /** change the position pointer.
      * This is useful to allocate a big bunch of memory with all the position
      * in order to improve memory cache usage.
      * \note this memory is not deleted/cleaned in ~AtomProperties (the destructor)
      *
      * @param ptr the pointer to the memory (should have enough space for storing double[3]
      * @param update update the new memory space using the previously stored position
      */
    void setPositionPointer(double* ptr, bool update = true);

private:
    /// unique number (used to generate unique index for atoms if not given at the instantiation)
    static unsigned int maxUniqueIndex;

    /** allocate the memory needed for the position (double[3]).
      * This place in memory is a default, it can be changed using setPositionPointer(..).
      * This method is called in all the constructors, and only there.
      */
    void allocate();

    /// Pointer to the memory triplet that stores the atom's position
    double* X;

    /// true only if the memory used for the position was allocated in the constructor and not changed afterwards
    bool allocated;
};

// --------------- inlines ---------------
inline void AtomProperties::getPosition(double pos[3]) const {
    pos[0] = X[0];
    pos[1] = X[1];
    pos[2] = X[2];
}

inline void AtomProperties::setPosition(const double pos[3]) {
    X[0] = pos[0];
    X[1] = pos[1];
    X[2] = pos[2];
}

inline void AtomProperties::setPosition(const double x, const double y, const double z) {
    X[0] = x;
    X[1] = y;
    X[2] = z;
}

#endif // ATOMPROPERTIES_H
