/*
 * RBDL - Rigid Body Dynamics Library
 * Copyright (c) 2011-2018 Martin Felis <martin@fysx.org>
 *
 * Licensed under the zlib license. See LICENSE for more details.
 */

#ifndef RBDL_MODEL_H
#define RBDL_MODEL_H

#include "rbdl/rbdl_math.h"
#include <map>
#include <list>
#include <assert.h>
#include <iostream>
#include <limits>
#include <cstring>

#include "rbdl/Logging.h"
#include "rbdl/Joint.h"
#include "rbdl/Body.h"

// std::vectors containing any objects that have Eigen matrices or vectors
// as members need to have a special allocater. This can be achieved with
// the following macro.

#ifdef RBDL_USE_EIGEN3_MATH
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(RigidBodyDynamics::Joint)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(RigidBodyDynamics::Body)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(RigidBodyDynamics::FixedBody)
#endif

/** \brief Namespace for all structures of the RigidBodyDynamics library
*/
namespace RigidBodyDynamics {

/** \page modeling_page Model 
 *
 * \section model_structure Model Structure
 *
 * RBDL stores the model internally in the \link RigidBodyDynamics::Model
 * Model Structure\endlink. For each \link RigidBodyDynamics::Body Body
 * \endlink it contains spatial velocities, accelerations and other
 * variables that describe the state of the rigid body system. Furthermore
 * it contains variables that are used as temporary variables in the
 * algorithms.
 * 
 * There are multiple ways of creating \link RigidBodyDynamics::Model Models\endlink for RBDL:
 *
 *   \li Loading models from Lua files using the \ref luamodel_introduction 
 *       "LuaModel" addon
 *   \li Loading models from URDF (the Unified Robot Description Format) xml
 *       files or strings using the URDFReader addon
 *   \li using the C++ interface.
 *
 * The first approach requires the addon \ref luamodel_introduction to be
 * activated which is done by enabling BUILD_ADDON_LUAMODEL in CMake and is
 * recommended when one is not interested in the details of RBDL and simply
 * wants to create a model.
 *
 * \section modeling_cpp Modeling using C++
 *
 * The construction of \link RigidBodyDynamics::Model Model Structures
 * \endlink makes use of carefully designed constructors of the classes
 * \link RigidBodyDynamics::Body Body \endlink and \link
 * RigidBodyDynamics::Joint Joint \endlink to ease the process of
 * creating articulated models.
 *
 * \link RigidBodyDynamics::Body Bodies \endlink are created by calling one
 * of its constructors. Usually they are created by specifying the mass,
 * center of mass and the inertia at the center of mass. 
 * \link RigidBodyDynamics::Joint Joints \endlink are similarly created and is
 * described in detail in \ref joint_description.
 *
 * Adding bodies to the model is done by specifying the
 * parent body by its id, the transformation from the parent origin to the
 * joint origin, the joint specification as an object, and the body itself.
 * These parameters are then fed to the function
 * RigidBodyDynamics::Model::AddBody() or 
 * RigidBodyDynamics::Model::AppendBody().
 *
 * To create a model with a floating base (a.k.a a model with a free-flyer
 * joint) it is recommended to use a joint of type JointTypeFloatingBase as
 * the first joint.
 *
 * Once this is done, the model structure can be used with the functions of \ref
 * kinematics_group, \ref dynamics_group, \ref constraints_group, to perform
 * computations.
 *
 * A simple example can be found \ref SimpleExample "here".
 *
 * \section modeling_lua Using LuaModels
 *
 * For this see the documentation of \ref luamodel_introduction,\link
 * RigidBodyDynamics::Addons::LuaModelReadFromFile \endlink, and \link
 * RigidBodyDynamics::Addons::LuaModelReadFromFileWithConstraints \endlink.

 * \section modeling_urdf Using URDF
 *
 * For this see the documentation see \link
 * RigidBodyDynamics::Addons::URDFReadFromFile \endlink.
 */

/** \brief Contains all information about the rigid body model
 *
 * This class contains all information required to perform the forward
 * dynamics calculation. The variables in this class are also used for
 * storage of temporary values. It is designed for use of the Articulated
 * Rigid Body Algorithm (which is implemented in ForwardDynamics()) and
 * follows the numbering as described in Featherstones book.
 * 
 * Please note that body 0 is the root body and the moving bodies start at
 * index 1. This numbering scheme is very beneficial in terms of
 * readability of the code as the resulting code is very similar to the
 * pseudo-code in the RBDA book. The generalized variables q, qdot, qddot
 * and tau however start at 0 such that the first entry (e.g. q[0]) always
 * specifies the value for the first moving body.
 *
 * \note To query the number of degrees of freedom use Model::dof_count.
 */
struct RBDL_DLLAPI Model {
  Model();

  // Structural information

  /// \brief The id of the parents body
  std::vector<unsigned int> lambda;
  /** \brief The index of the parent degree of freedom that is directly
    influencing the current one*/
  std::vector<unsigned int> lambda_q;
  /// \brief Contains the ids of all the children of a given body
  std::vector<std::vector<unsigned int> >mu;

  /** \brief number of degrees of freedoms of the model
   *
   * This value contains the number of entries in the generalized state (q)
   * velocity (qdot), acceleration (qddot), and force (tau) vector.
   */
  unsigned int dof_count;

  /** \brief The size of the \f$\mathbf{q}\f$-vector.
   * For models without spherical joints the value is the same as
   * Model::dof_count, otherwise additional values for the w-component of the
   * Quaternion is stored at the end of \f$\mathbf{q}\f$.
   *
   * \sa \ref joint_description for more details.
   */
  unsigned int q_size;
  /** \brief The size of the
   *
   * (\f$\mathbf{\dot{q}}, \mathbf{\ddot{q}}\f$,
   * and \f$\mathbf{\tau}\f$-vector.
   *
   * \sa \ref joint_description for more details.
   */
  unsigned int qdot_size;

  /// \brief Id of the previously added body, required for Model::AppendBody()
  unsigned int previously_added_body_id;

  /// \brief the cartesian vector of the gravity
  Math::Vector3d gravity;

  // State information
  /// \brief The spatial velocity of the bodies
  std::vector<Math::SpatialVector> v;
  /// \brief The spatial acceleration of the bodies
  std::vector<Math::SpatialVector> a;

  ////////////////////////////////////
  // Joints

  /// \brief All joints

  std::vector<Joint> mJoints;
  /// \brief The joint axis for joint i
  std::vector<Math::SpatialVector> S;

  // Joint state variables
  std::vector<Math::SpatialTransform> X_J;
  std::vector<Math::SpatialVector> v_J;
  std::vector<Math::SpatialVector> c_J;

  std::vector<unsigned int> mJointUpdateOrder;

  /// \brief Transformations from the parent body to the frame of the joint.
  // It is expressed in the coordinate frame of the parent.
  std::vector<Math::SpatialTransform> X_T;
  /// \brief The number of fixed joints that have been declared before
  ///  each joint.
  std::vector<unsigned int> mFixedJointCount;

  ////////////////////////////////////
  // Special variables for joints with 3 degrees of freedom
  /// \brief Motion subspace for joints with 3 degrees of freedom
  std::vector<Math::Matrix63> multdof3_S;
  std::vector<Math::Matrix63> multdof3_U;
  std::vector<Math::Matrix3d> multdof3_Dinv;
  std::vector<Math::Vector3d> multdof3_u;
  std::vector<unsigned int> multdof3_w_index;

  std::vector<CustomJoint*> mCustomJoints;

  ////////////////////////////////////
  // Dynamics variables

  /// \brief The velocity dependent spatial acceleration
  std::vector<Math::SpatialVector> c;
  /// \brief The spatial inertia of the bodies
  std::vector<Math::SpatialMatrix> IA;
  /// \brief The spatial bias force
  std::vector<Math::SpatialVector> pA;
  /// \brief Temporary variable U_i (RBDA p. 130)
  std::vector<Math::SpatialVector> U;
  /// \brief Temporary variable D_i (RBDA p. 130)
  Math::VectorNd d;
  /// \brief Temporary variable u (RBDA p. 130)
  Math::VectorNd u;
  /// \brief Internal forces on the body (used only InverseDynamics())
  std::vector<Math::SpatialVector> f;
  /// \brief The spatial inertia of body i (used only in
  ///  CompositeRigidBodyAlgorithm())
  std::vector<Math::SpatialRigidBodyInertia> I;
  std::vector<Math::SpatialRigidBodyInertia> Ic;
  std::vector<Math::SpatialVector> hc;
  std::vector<Math::SpatialVector> hdotc;

  ////////////////////////////////////
  // Bodies

  /** \brief Transformation from the parent body to the current body
   * \f[
   *	X_{\lambda(i)} = {}^{i} X_{\lambda(i)}
   * \f]
   */
  std::vector<Math::SpatialTransform> X_lambda;
  /// \brief Transformation from the base to bodies reference frame
  std::vector<Math::SpatialTransform> X_base;

  /// \brief All bodies that are attached to a body via a fixed joint.
  std::vector<FixedBody> mFixedBodies;
  /** \brief Value that is used to discriminate between fixed and movable
   * bodies.
   *
   * Bodies with id 1 .. (fixed_body_discriminator - 1) are moving bodies
   * while bodies with id fixed_body_discriminator .. max (unsigned int)
   * are fixed to a moving body. The value of max(unsigned int) is
   * determined via std::numeric_limits<unsigned int>::max() and the
   * default value of fixed_body_discriminator is max (unsigned int) / 2.
   *
   * On normal systems max (unsigned int) is 4294967294 which means there
   * could be a total of 2147483646 movable and / or fixed bodies.
   */
  unsigned int fixed_body_discriminator;

  /** \brief All bodies 0 ... N_B, including the base
   *
   * mBodies[0] - base body <br>
   * mBodies[1] - 1st moveable body <br>
   * ... <br>
   * mBodies[N_B] - N_Bth moveable body <br>
   */
  std::vector<Body> mBodies;

  /// \brief Human readable names for the bodies
  std::map<std::string, unsigned int> mBodyNameMap;

  /** \brief Connects a given body to the model
   *
   * When adding a body there are basically informations required:
   * - what kind of body will be added?
   * - where is the new body to be added?
   * - by what kind of joint should the body be added?
   *
   * The first information "what kind of body will be added" is contained
   * in the Body class that is given as a parameter.
   *
   * The question "where is the new body to be added?" is split up in two
   * parts: first the parent (or successor) body to which it is added and
   * second the transformation to the origin of the joint that connects the
   * two bodies. With these two informations one specifies the relative
   * positions of the bodies when the joint is in neutral position.gk
   *
   * The last question "by what kind of joint should the body be added?" is
   * again simply contained in the Joint class.
   *
   * \param parent_id   id of the parent body
   * \param joint_frame the transformation from the parent frame to the origin
   *                    of the joint frame (represents X_T in RBDA)
   * \param joint       specification for the joint that describes the
   *                    connection
   * \param body        specification of the body itself
   * \param body_name   human readable name for the body (can be used to
   *                    retrieve its id with GetBodyId())
   *
   * \returns id of the added body
   */
  unsigned int AddBody (
      const unsigned int parent_id,
      const Math::SpatialTransform &joint_frame,
      const Joint &joint,
      const Body &body,
      std::string body_name = ""
      );

  unsigned int AddBodySphericalJoint (
      const unsigned int parent_id,
      const Math::SpatialTransform &joint_frame,
      const Joint &joint,
      const Body &body,
      std::string body_name = ""
      );

  /** \brief Adds a Body to the model such that the previously added Body
   * is the Parent.
   *
   * This function is basically the same as Model::AddBody() however the
   * most recently added body (or body 0) is taken as parent.
   */
  unsigned int AppendBody (
      const Math::SpatialTransform &joint_frame,
      const Joint &joint,
      const Body &body,
      std::string body_name = ""
      );

  unsigned int AddBodyCustomJoint (
      const unsigned int parent_id,
      const Math::SpatialTransform &joint_frame,
      CustomJoint *custom_joint,
      const Body &body,
      std::string body_name = ""
      );

  /** \brief Returns the id of a body that was passed to AddBody()
   *
   * Bodies can be given a human readable name. This function allows to
   * resolve its name to the numeric id.
   *
   * \note Instead of querying this function repeatedly, it might be
   * advisable to query it once and reuse the returned id.
   *
   * \returns the id of the body or \c std::numeric_limits\<unsigned
   *          int\>::max() if the id was not found.
   */
  unsigned int GetBodyId (const char *body_name) const {
    if (mBodyNameMap.count(body_name) == 0) {
      return std::numeric_limits<unsigned int>::max();
    }

    return mBodyNameMap.find(body_name)->second;
  }

  /** \brief Returns the name of a body for a given body id */
  std::string GetBodyName (unsigned int body_id) const {
    std::map<std::string, unsigned int>::const_iterator iter
      = mBodyNameMap.begin();

    while (iter != mBodyNameMap.end()) {
      if (iter->second == body_id)
        return iter->first;

      iter++;
    }

    return "";
  }

  /** \brief Checks whether the body is rigidly attached to another body.
  */
  bool IsFixedBodyId (unsigned int body_id) {
    if (body_id >= fixed_body_discriminator
        && body_id < std::numeric_limits<unsigned int>::max()
        && body_id - fixed_body_discriminator < mFixedBodies.size()) {
      return true;
    }
    return false;
  }

  bool IsBodyId (unsigned int id) {
    if (id > 0 && id < mBodies.size())
      return true;
    if (id >= fixed_body_discriminator
        && id < std::numeric_limits<unsigned int>::max()) {
      if (id - fixed_body_discriminator < mFixedBodies.size())
        return true;
    }
    return false;
  }

  /** Determines id the actual parent body.
   *
   * When adding bodies using joints with multiple degrees of
   * freedom, additional virtual bodies are added for each degree of
   * freedom. This function returns the id of the actual
   * non-virtual parent body.
   */
  unsigned int GetParentBodyId (unsigned int id) {
    if (id >= fixed_body_discriminator) {
      return mFixedBodies[id - fixed_body_discriminator].mMovableParent;
    }

    unsigned int parent_id = lambda[id];

    while (mBodies[parent_id].mIsVirtual) {
      parent_id = lambda[parent_id];
    }

    return parent_id;
  }

  /** Returns the joint frame transformtion, i.e. the second argument to
    Model::AddBody().
    */
  Math::SpatialTransform GetJointFrame (unsigned int id) {
    if (id >= fixed_body_discriminator) {
      return mFixedBodies[id - fixed_body_discriminator].mParentTransform;
    }

    unsigned int child_id = id;
    unsigned int parent_id = lambda[id];
    if (mBodies[parent_id].mIsVirtual) {
      while (mBodies[parent_id].mIsVirtual) {
        child_id = parent_id;
        parent_id = lambda[child_id];
      }
      return X_T[child_id];
    } else
      return X_T[id];
  }

  /** Sets the joint frame transformtion, i.e. the second argument to
  Model::AddBody().
    */
  void SetJointFrame (unsigned int id,
      const Math::SpatialTransform &transform) {
    if (id >= fixed_body_discriminator) {
      std::cerr << "Error: setting of parent transform "
        << "not supported for fixed bodies!" << std::endl;
      abort();
    }

    unsigned int child_id = id;
    unsigned int parent_id = lambda[id];
    if (mBodies[parent_id].mIsVirtual) {
      while (mBodies[parent_id].mIsVirtual) {
        child_id = parent_id;
        parent_id = lambda[child_id];
      }
      X_T[child_id] = transform;
    } else if (id > 0) {
      X_T[id] = transform;
    }
  }

  /** Gets the quaternion for body i (only valid if body i is connected by
   * a JointTypeSpherical joint)
   *
   * See \ref joint_singularities for details.
   */
  Math::Quaternion GetQuaternion (unsigned int i,
      const Math::VectorNd &Q) const {
    assert (mJoints[i].mJointType == JointTypeSpherical);
    unsigned int q_index = mJoints[i].q_index;
    return Math::Quaternion ( Q[q_index],
        Q[q_index + 1],
        Q[q_index + 2],
        Q[multdof3_w_index[i]]);
  }

  /** Sets the quaternion for body i (only valid if body i is connected by
   * a JointTypeSpherical joint)
   *
   * See \ref joint_singularities for details.
   */
  void SetQuaternion (unsigned int i,
      const Math::Quaternion &quat,
      Math::VectorNd &Q) const {
    assert (mJoints[i].mJointType == JointTypeSpherical);
    unsigned int q_index = mJoints[i].q_index;

    Q[q_index] = quat[0];
    Q[q_index + 1] = quat[1];
    Q[q_index + 2] = quat[2];
    Q[multdof3_w_index[i]] = quat[3];
  }
};

/** @} */
}

/* _MODEL_H */
#endif
