/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2017, Locus Robotics
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the copyright holder nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ROBOT_NAV_CORE_ADAPTER_LOCAL_PLANNER_ADAPTER_H
#define ROBOT_NAV_CORE_ADAPTER_LOCAL_PLANNER_ADAPTER_H

#include <robot/node_handle.h>
#include <robot_nav_core/base_local_planner.h>
#include <robot_nav_core2/local_planner.h>
#include <robot_nav_core_adapter/costmap_adapter.h>
#include <robot_nav_2d_utils/odom_subscriber.h>
#include <boost/dll/import.hpp>
#include <memory>
#include <string>
#include <vector>

namespace robot_nav_core_adapter
{
  struct NavCoreAdapterConfig
  {
    std::string planner_name;
    bool use_odom;
    bool use_costmap;
    bool use_tf;
    bool use_dynamic_reconfigure;
  };

  /**
   * @class LocalPlannerAdapter
   * @brief used for employing a robot_nav_core2 local planner (such as dwb) as a robot_nav_core2 plugin, like in move_base.
   */
  class LocalPlannerAdapter : public robot_nav_core::BaseLocalPlanner
  {
  public:

    using Ptr = boost::shared_ptr<LocalPlannerAdapter>;

    /**
     * @brief Constructor
     */
    LocalPlannerAdapter();

    /**
     * @brief Destructor
     */
    virtual ~LocalPlannerAdapter();

    // Standard ROS Local Planner Interface
    void initialize(std::string name, tf3::BufferCore *tf, robot_costmap_2d::Costmap2DROBOT *costmap_robot) override;

    /**
     * @brief  Given the current position, orientation, and velocity of the robot, compute velocity commands to send to the base
     * @param velocity The velocity of the robot
     * @param cmd_vel Will be filled with the velocity command to be passed to the robot base
     * @return True if a valid velocity command was found, false otherwise
     */
    bool computeVelocityCommands(const robot_geometry_msgs::Twist &velocity, robot_geometry_msgs::Twist &cmd_vel) override;

    /**
     * @brief Swap object will be injected interjace robot_nav_core2::BaseLocalPlanner
     * @param  planner_name  The object name
     * @return True if instance object is successed, False otherwise
     */
    virtual bool swapPlanner(std::string planner_name) override;

    /**
     * @brief set velocity for x, y asix of the robot
     * @param linear The velocity command
     * @return True if set is done, false otherwise
     */
    virtual bool setTwistLinear(robot_geometry_msgs::Vector3 linear) override;

    /**
     * @brief get velocity for x, y asix of the robot
     * @param linear The velocity command
     * @return True if set is done, false otherwise
     */
    virtual robot_geometry_msgs::Vector3 getTwistLinear(bool direct) override;

    /**
     * @brief Set velocity theta for z asix of the robot
     * @param angular the command velocity
     * @return True if set is done, false otherwise
     */
    virtual bool setTwistAngular(robot_geometry_msgs::Vector3 angular) override;

    /**
     * @brief Get velocity theta for z asix of the robot
     * @param direct The velocity command
     * @return True if set is done, false otherwise
     */
    virtual robot_geometry_msgs::Vector3 getTwistAngular(bool direct) override;

    /**
     * @brief Get actual velocity for x, y asix of the robot
     * @return The current velocity
     */
    virtual robot_geometry_msgs::Twist getActualTwist() override;

    /**
     * @brief Check if the kinematic parameters are currently locked
     * @return True if the kinematic parameters are locked, false otherwise
     *
     * This function indicates whether the kinematic parameters (e.g., velocities, accelerations, or direction settings) are in a locked state, preventing modifications. If the parameters are locked during in-place rotation, it could prevent updates to the direction or velocity, potentially causing inconsistencies in motion commands that lead to odometry errors and localization jumps in AMCL.
     */
    virtual bool islock() override;

    /**
     * @brief  Lock the kinematic parameters to prevent modifications
     */    
    virtual void lock() override;

    /**
     * @brief  Unlock the kinematic parameters to allow modifications
     */    
    virtual void unlock() override;

    /**
     * @brief  Check if the goal pose has been achieved by the local planner
     * @return True if achieved, false otherwise
     */
    bool isGoalReached() override;

    /**
     * @brief  Set the plan that the local planner is following
     * @param plan The plan to pass to the local planner
     * @return True if the plan was updated successfully, false otherwise
     */
    bool setPlan(const std::vector<robot_geometry_msgs::PoseStamped> &plan) override;


    /**
     * @brief Gets the global plan for this local planner.
     *
     * @param path The global plan
     */ 
    virtual void getPlan(std::vector<robot_geometry_msgs::PoseStamped> &path) override;

    /**
     * @brief Gets the current global plan
     * @param path The global plan
     */
    virtual void getGlobalPlan(std::vector<robot_geometry_msgs::PoseStamped> &path) override;

    /**
     * @brief Create a new LocalPlannerAdapter
     * @return A shared pointer to the new LocalPlannerAdapter
     */
    static robot_nav_core::BaseLocalPlanner::Ptr create();

  protected:
    /**
     * @brief Get the robot pose from the costmap and store as Pose2DStamped
     */
    bool getRobotPose(robot_nav_2d_msgs::Pose2DStamped &pose2d);

    /**
     * @brief See if the back of the global plan matches the most recent goal pose
     * @return True if the plan has changed
     */
    bool hasGoalChanged(const robot_nav_2d_msgs::Pose2DStamped &new_goal);

    // The most recent goal pose
    robot_nav_2d_msgs::Pose2DStamped last_goal_;
    bool has_active_goal_;

    // Plugin handling
    boost::function<robot_nav_core2::LocalPlanner::Ptr()> planner_loader_;
    robot_nav_core2::LocalPlanner::Ptr planner_;
    
    bool initialized_;
    // Pointer Copies
    TFListenerPtr tf_;

    std::shared_ptr<CostmapAdapter> costmap_adapter_;
    robot_costmap_2d::Costmap2DROBOT *costmap_robot_;

    boost::recursive_mutex configuration_mutex_;

    robot::NodeHandle private_nh_;
    robot::NodeHandle adapter_nh_;

    robot_nav_core_adapter::NavCoreAdapterConfig last_config_;
    robot_nav_core_adapter::NavCoreAdapterConfig default_config_;
  };

} // namespace robot_nav_core_adapter

#endif // ROBOT_NAV_CORE_ADAPTER_LOCAL_PLANNER_ADAPTER_H
