/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2024, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */
#pragma once

#include <mrpt/math/CMatrixD.h>
#include <mrpt/math/CProbabilityDensityFunction.h>
#include <mrpt/poses/CPose3D.h>
#include <mrpt/poses/CPose3DQuat.h>

namespace mrpt::poses
{
class CPosePDF;
class CPose3DPDF;

/** Declares a class that represents a Probability Density Function (PDF) of a
 * 3D pose (6D actually), by means of a 7-vector with a translation [x y z] and
 * a quaternion [qr qx qy qz].
 *   This class is just the base class for unifying many different ways this PDF
 * can be implemented.
 *
 *  For convenience, a pose composition is also defined for any
 *    PDF derived class, changeCoordinatesReference, in the form of a method
 * rather than an operator.
 *
 *  - For a similar class for 3D points (without attitude), see CPointPDF.
 *  - For a similar class for 3D poses  (with Euler angles instead of
 * quaternions), see CPose3DPDF.
 *
 *  See also:
 *  [probabilistic spatial representations](tutorial-pdf-over-poses.html)
 *
 * \sa CPose3DQuatPDF, CPose3DPDF
 * \ingroup poses_pdf_grp
 */
class CPose3DQuatPDF :
    public mrpt::serialization::CSerializable,
    public mrpt::math::CProbabilityDensityFunction<CPose3DQuat, 7>
{
  DEFINE_VIRTUAL_SERIALIZABLE(CPose3DQuatPDF, mrpt::poses)

 public:
  /** Copy operator, translating if necessary (for example, between particles
   * and gaussian representations)
   * \sa createFrom2D
   */
  virtual void copyFrom(const CPose3DQuatPDF& o) = 0;

  /** This is a static transformation method from 2D poses to 3D PDFs,
   * preserving the representation type (particles->particles,
   * Gaussians->Gaussians,etc)
   *  It returns a new object of any of the derived classes of
   * CPose3DQuatPDF. This object must be deleted by the user when not required
   * anymore.
   *  \sa copyFrom
   */
  static CPose3DQuatPDF::Ptr createFrom2D(const CPosePDF& o);

  /** Returns a new PDF such as: NEW_PDF = (0,0,0) - THIS_PDF */
  virtual void inverse(CPose3DQuatPDF& o) const = 0;

  virtual void changeCoordinatesReference(const CPose3D& newReferenceBase) = 0;

  /** This static method computes the two Jacobians of a pose composition
   * operation \f$ (x,u)= x \oplus u \f$
   *  \param out_x_oplus_u If set to !=nullptr, the result of "x+u" will be
   * stored here (it will be computed internally anyway).
   *  To see the mathematical derivation of the formulas, refer to the
   * technical report here:
   *   -
   * https://www.mrpt.org/Probability_Density_Distributions_Over_Spatial_Representations
   */
  static void jacobiansPoseComposition(
      const CPose3DQuat& x,
      const CPose3DQuat& u,
      mrpt::math::CMatrixDouble77& df_dx,
      mrpt::math::CMatrixDouble77& df_du,
      CPose3DQuat* out_x_oplus_u = nullptr);

  /** Returns a 3D representation of this PDF (it doesn't clear the current
   * contents of out_obj, but append new OpenGL objects to that list)
   * \note Needs the mrpt-opengl library, and using
   * mrpt::opengl::CSetOfObjects::Ptr as template argument.
   * \note By default, ellipsoids for the confidence intervals of  "q=3" are
   * drawn; for more mathematical details, see
   * CGeneralizedEllipsoidTemplate::setQuantiles()
   */
  template <class OPENGL_SETOFOBJECTSPTR>
  inline void getAs3DObject(OPENGL_SETOFOBJECTSPTR& out_obj) const
  {
    using SETOFOBJECTS = typename OPENGL_SETOFOBJECTSPTR::value_type;
    out_obj->insertCollection(*SETOFOBJECTS::posePDF2opengl(*this));
  }

  /** Returns a 3D representation of this PDF.
   * \note Needs the mrpt-opengl library, and using
   * mrpt::opengl::CSetOfObjects::Ptr as template argument.
   */
  template <class OPENGL_SETOFOBJECTSPTR>
  inline OPENGL_SETOFOBJECTSPTR getAs3DObject() const
  {
    using SETOFOBJECTS = typename OPENGL_SETOFOBJECTSPTR::value_type;
    return SETOFOBJECTS::posePDF2opengl(*this);
  }

};  // End of class def.
}  // namespace mrpt::poses
