/*
 * Copyright 2019 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#ifndef SDF_ACTOR_HH_
#define SDF_ACTOR_HH_

#include <memory>
#include <string>

#include <ignition/math/Pose3.hh>
#include <ignition/utils/ImplPtr.hh>

#include "sdf/Element.hh"
#include "sdf/Types.hh"
#include "sdf/Link.hh"
#include "sdf/Joint.hh"
#include "sdf/sdf_config.h"
#include "sdf/system_util.hh"

namespace sdf
{
  // Inline bracke to help doxygen filtering.
  inline namespace SDF_VERSION_NAMESPACE {
  //
  /// \brief Animation in Actor.
  class SDFORMAT_VISIBLE Animation
  {
    /// \brief Default constructor
    public: Animation();

    /// \brief Load the animation based on a element pointer. This is *not* the
    /// usual entry point. Typical usage of the SDF DOM is through the Root
    /// object.
    /// \param[in] _sdf The SDF Element pointer
    /// \return Errors, which is a vector of Error objects. Each Error includes
    /// an error code and message. An empty vector indicates no error.
    public: Errors Load(ElementPtr _sdf);

    /// \brief Get the name of the animation.
    /// \return Name of the animation.
    public: const std::string &Name() const;

    /// \brief Set the name of the animation.
    /// \param[in] _name Name of the animation.
    public: void SetName(const std::string &_name);

    /// \brief Get the animation filename.
    /// \return Filename of the animation.
    public: const std::string &Filename() const;

    /// \brief Set the filename of the animation.
    /// \param[in] _filename Path to animation file.
    public: void SetFilename(const std::string &_filename);

    /// \brief The path to the file where this element was loaded from.
    /// \return Full path to the file on disk.
    public: const std::string &FilePath() const;

    /// \brief Set the path to the file where this element was loaded from.
    /// \paramp[in] _filePath Full path to the file on disk.
    public: void SetFilePath(const std::string &_filePath);

    /// \brief Get the scale for the animation skeleton.
    /// \return Scale of the animation skeleton.
    public: double Scale() const;

    /// \brief Set the scale of the animation skeleton.
    /// \param[in] _scale Scale for animation skeleton.
    public: void SetScale(double _scale);

    /// \brief Get whether the animation is interpolated on X.
    /// \return True if interpolated on X.
    public: bool InterpolateX() const;

    /// \brief Set whether the animation is interpolated on X.
    /// \param[in] _interpolateX True to indicate interpolation on X.
    public: void SetInterpolateX(bool _interpolateX);

    /// \brief Private data pointer.
    IGN_UTILS_IMPL_PTR(dataPtr)
  };

  /// \brief Waypoint for Trajectory.
  class SDFORMAT_VISIBLE Waypoint
  {
    /// \brief Default constructor
    public: Waypoint();

    /// \brief Load the waypoint based on a element pointer. This is *not* the
    /// usual entry point. Typical usage of the SDF DOM is through the Root
    /// object.
    /// \param[in] _sdf The SDF Element pointer
    /// \return Errors, which is a vector of Error objects. Each Error includes
    /// an error code and message. An empty vector indicates no error.
    public: Errors Load(ElementPtr _sdf);

    /// \brief Get the time in seconds when the pose should be reached.
    /// \return Time in seconds.
    public: double Time() const;

    /// \brief Set the time in seconds when the pose should be reached.
    /// \param[in] _time Time in seconds for the pose to be reached.
    public: void SetTime(double _time);

    /// \brief Get the pose to be reached.
    /// \return Pose to be reached.
    public: ignition::math::Pose3d Pose() const;

    /// \brief Set the pose to be reached.
    /// \param[in] _pose Pose to be reached.
    public: void SetPose(const ignition::math::Pose3d &_pose);

    /// \brief Private data pointer.
    IGN_UTILS_IMPL_PTR(dataPtr)
  };

  /// \brief Trajectory for Animation.
  class SDFORMAT_VISIBLE Trajectory
  {
    /// \brief Default constructor
    public: Trajectory();

    /// \brief Load the trajectory based on a element pointer. This is *not* the
    /// usual entry point. Typical usage of the SDF DOM is through the Root
    /// object.
    /// \param[in] _sdf The SDF Element pointer
    /// \return Errors, which is a vector of Error objects. Each Error includes
    /// an error code and message. An empty vector indicates no error.
    public: Errors Load(ElementPtr _sdf);

    /// \brief Get the unique id of the trajectory.
    /// \return Trajectory id.
    public: uint64_t Id() const;

    /// \brief Set the ID of the trajectory.
    /// \param[in] _id Trajectory Id.
    public: void SetId(uint64_t _id);

    /// \brief Get the type of the trajectory.
    /// \return Type of the trajectory.
    public: const std::string &Type() const;

    /// \brief Set the animation type of the trajectory.
    /// (should match the animation name).
    /// \param[in] _type Animation type to be played.
    public: void SetType(const std::string &_type);

    /// \brief Get the tension of the trajectory spline.
    /// \return Tension of the trajectory spline.
    public: double Tension() const;

    /// \brief Set the tension of trajectory spline.
    /// \param[in] _tension Tension for the trajectory spline.
    public: void SetTension(double _tension);

    /// \brief Get the number of waypoints.
    /// \return Number of waypoints.
    public: uint64_t WaypointCount() const;

    /// \brief Get a waypoint based on an index.
    /// \param[in] _index Index of the waypoint. The index should be in the
    /// range [0..WaypointCount()).
    /// \return Pointer to the waypoint. Nullptr if the index does not exist.
    /// \sa uint64_t WaypointCount() const
    public: const Waypoint *WaypointByIndex(uint64_t _index) const;

    /// \brief Add a new waypoint.
    /// \param[in] _waypoint Waypoint to be added.
    public: void AddWaypoint(const Waypoint &_waypoint);

    /// \brief Private data pointer.
    IGN_UTILS_IMPL_PTR(dataPtr)
  };


  /// \brief Provides a description of an actor.
  class SDFORMAT_VISIBLE Actor
  {
    /// \brief Default constructor
    public: Actor();

    /// \brief Load the actor based on a element pointer. This is *not* the
    /// usual entry point. Typical usage of the SDF DOM is through the Root
    /// object.
    /// \param[in] _sdf The SDF Element pointer
    /// \return Errors, which is a vector of Error objects. Each Error includes
    /// an error code and message. An empty vector indicates no error.
    public: Errors Load(ElementPtr _sdf);

    /// \brief Get the name of the actor.
    /// \return Name of the actor.
    public: const std::string &Name() const;

    /// \brief Set the name of the actor.
    /// \param[in] _name Name of the actor.
    public: void SetName(const std::string &_name);

    /// \brief Get the pose of the actor. This is the pose of the actor
    /// as specified in SDF (<actor> <pose> ... </pose></actor>), and is
    /// typically used to express the position and rotation of an actor in a
    /// global coordinate frame.
    /// \return The pose of the actor.
    public: const ignition::math::Pose3d &RawPose() const;

    /// \brief Set the pose of the actor.
    /// \sa const ignition::math::Pose3d &RawPose() const
    /// \param[in] _pose The new actor pose.
    public: void SetRawPose(const ignition::math::Pose3d &_pose);

    /// \brief Get the name of the coordinate frame relative to which this
    /// object's pose is expressed. An empty value indicates that the frame is
    /// relative to the world frame.
    /// \return The name of the pose relative-to frame.
    public: const std::string &PoseRelativeTo() const;

    /// \brief Set the name of the coordinate frame relative to which this
    /// object's pose is expressed. An empty value indicates that the frame is
    /// relative to the world frame.
    /// \param[in] _frame The name of the pose relative-to frame.
    public: void SetPoseRelativeTo(const std::string &_frame);

    /// \brief The path to the file where this element was loaded from.
    /// \return Full path to the file on disk.
    public: const std::string &FilePath() const;

    /// \brief Set the path to the file where this element was loaded from.
    /// \paramp[in] _filePath Full path to the file on disk.
    public: void SetFilePath(const std::string &_filePath);

    /// \brief Get the skin filename.
    /// \return Constant skin filename.
    public: const std::string &SkinFilename() const;

    /// \brief Set the skin filename.
    /// \param[in] _skinFilename Skin filename.
    public: void SetSkinFilename(std::string _skinFilename);

    /// \brief Get the skin scale.
    /// \return Constant skin filename.
    public: double SkinScale() const;

    /// \brief Set the skin scale.
    /// \param[in] _skinScale Skin scale.
    public: void SetSkinScale(double _skinScale);

    /// \brief Get the number of animations.
    /// \return Number of animations.
    public: uint64_t AnimationCount() const;

    /// \brief Get an animation based on an index.
    /// \param[in] _index Index of the animation. The index should be in the
    /// range [0..AnimationCount()).
    /// \return Pointer to the animation. Nullptr if the index does not exist.
    /// \sa uint64_t AnimationCount() const
    public: const Animation *AnimationByIndex(uint64_t _index) const;

    /// \brief Get whether an animation name exists.
    /// \param[in] _name Name of the animation to check.
    /// \return True if there exists an animation with the given name.
    public: bool AnimationNameExists(const std::string &_name) const;

    /// \brief Add a new animation.
    /// \param[in] _anim Animation to be added.
    public: void AddAnimation(const Animation &_anim);

    /// \brief Get whether the animation plays in loop.
    /// \return True if the animation plays in loop.
    public: bool ScriptLoop() const;

    /// \brief Set whether the animation plays in loop.
    /// \param[in] _scriptLoop True to indicate that the animation
    /// plays in loop.
    public: void SetScriptLoop(bool _scriptLoop);

    /// \brief Get the time (in seconds) of delay to start.
    /// \return Time of delay to start.
    public: double ScriptDelayStart() const;

    /// \brief Set the delay time to start.
    /// \param[in] _scriptDelayStart Time of delay to start.
    public: void SetScriptDelayStart(double _scriptDelayStart);

    /// \brief Get whether the animation plays when simulation starts.
    /// \return True if the animation plays when simulation starts.
    public: bool ScriptAutoStart() const;

    /// \brief Set whether the animation plays when simulation starts.
    /// \param[in] _staticAutoStart True to indicate that the animation
    /// plays when simulation starts.
    public: void SetScriptAutoStart(bool _scriptAutoStart);

    /// \brief Get the number of trajectories.
    /// \return Number of trajectories.
    public: uint64_t TrajectoryCount() const;

    /// \brief Get a trajectory based on an index.
    /// \param[in] _index Index of the trajectory. The index should be in the
    /// range [0..TrajectoryCount()).
    /// \return Pointer to the trajectory. Nullptr if the index does not exist.
    /// \sa uint64_t TrajectoryCount() const
    public: const Trajectory *TrajectoryByIndex(uint64_t _index) const;

    /// \brief Get whether a trajectory id exists.
    /// \param[in] _id Id of the trajectory to check.
    /// \return True if there exists a trajectory with the given name.
    public: bool TrajectoryIdExists(uint64_t _id) const;

    /// \brief Add a new trajectory.
    /// \param[in] _traj Trajectory to be added.
    public: void AddTrajectory(const Trajectory &_traj);

    /// \brief Get the number of links.
    /// \return Number of links.
    public: uint64_t LinkCount() const;

    /// \brief Get a link based on an index.
    /// \param[in] _index Index of the link. The index should be in the
    /// range [0..LinkCount()).
    /// \return Pointer to the link. Nullptr if the index does not exist.
    /// \sa uint64_t LinkCount() const
    public: const Link *LinkByIndex(uint64_t _index) const;

    /// \brief Get whether a link name exists.
    /// \param[in] _name Name of the link to check.
    /// \return True if there exists a link with the given name.
    public: bool LinkNameExists(const std::string &_name) const;

    /// \brief Get the number of joints.
    /// \return Number of joints.
    public: uint64_t JointCount() const;

    /// \brief Get a joint based on an index.
    /// \param[in] _index Index of the joint. The index should be in the
    /// range [0..JointCount()).
    /// \return Pointer to the joint. Nullptr if the index does not exist.
    /// \sa uint64_t JointCount() const
    public: const Joint *JointByIndex(uint64_t _index) const;

    /// \brief Get whether a joint name exists.
    /// \param[in] _name Name of the joint to check.
    /// \return True if there exists a joint with the given name.
    public: bool JointNameExists(const std::string &_name) const;

    /// \brief Get a pointer to the SDF element that was used during
    /// load.
    /// \return SDF element pointer. The value will be nullptr if Load has
    /// not been called.
    public: sdf::ElementPtr Element() const;

    /// \brief Add a link to the actor.
    /// \param[in] _link Link to add.
    /// \return True if successful, false if a link with the name already
    /// exists.
    public: bool AddLink(const Link &_link);

    /// \brief Add a joint to the actor.
    /// \param[in] _link Joint to add.
    /// \return True if successful, false if a joint with the name already
    /// exists.
    public: bool AddJoint(const Joint &_joint);

    /// \brief Remove all links.
    public: void ClearLinks();

    /// \brief Remove all joints.
    public: void ClearJoints();

    /// \brief Create and return an SDF element filled with data from this
    /// actor.
    /// \return SDF element pointer with updated actor values.
    public: sdf::ElementPtr ToElement() const;

    /// \brief Private data pointer.
    IGN_UTILS_IMPL_PTR(dataPtr)
  };
  }
}
#endif
