/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2019, PickNik, LLC.
 *  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 PickNik 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 OWNER 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.
 *********************************************************************/

/* Author: Itamar Mishani (imishani@andrew.cmu)
 * Date: Sep 7, 2023
 *
   Desc: Interface to manipulation_planning and search.
 */
#ifndef SRMP_INTERFACE_H
#define SRMP_INTERFACE_H

#include <ros/ros.h>
#include <moveit/planning_interface/planning_interface.h>

// include all the search algorithms
#include <search/planners/bfs.hpp>
#include <search/planners/astar.hpp>
#include <search/planners/wastar.hpp>
#include <search/planners/arastar.hpp>
#include <search/planners/dijkstra.hpp>

#include "srmp_moveit_plugin/common/utils.hpp"
#include "srmp_moveit_plugin/heuristics/manip_heuristics.hpp"
#include "srmp_moveit_plugin/common/moveit_scene_interface.hpp"
#include "manipulation_planning/action_space/manipulation_action_space.hpp"
#include "srmp_moveit_plugin/common/factories.hpp"

namespace srmp_interface
{
MOVEIT_CLASS_FORWARD(SRMPInterface);

class SRMPInterface
{
protected:
    // ============= Args =============
    ros::NodeHandle nh_;
    std::string name_;
    std::string group_name_;
    int dof_;
    moveit::core::RobotModelConstPtr robot_model_;

    std::shared_ptr<ims::MoveitInterface> scene_interface_ = nullptr;

    // The planning world.
    std::shared_ptr<ims::PlanningWorld> planning_world_ = nullptr;

    // The planner configuration.
    planning_interface::PlannerConfigurationMap pconfig_;
    // The planner configuration settings.
    planning_interface::PlannerConfigurationSettings configuration_settings_;

public:
    // ====================
    // Public Methods.
    // ====================
    SRMPInterface(const std::string& context_name,
                 std::string  group_name,
                 const moveit::core::RobotModelConstPtr& robot_model,
                 const ros::NodeHandle& nh = ros::NodeHandle("~"));

    void setPlannerConfigurations(const planning_interface::PlannerConfigurationMap& pconfig);

    bool loadPlannerConfiguration(const std::string& group_name, const std::string& planner_id,
                                  const std::map<std::string, std::string>& group_params,
                                  planning_interface::PlannerConfigurationSettings& planner_config);

    void loadPlannerConfigurations();

    bool solve(const planning_scene::PlanningSceneConstPtr& planning_scene,
         const planning_interface::MotionPlanRequest& req,
         moveit_msgs::MotionPlanDetailedResponse& res);

    bool solveMultiAgent(const planning_scene::PlanningSceneConstPtr& planning_scene,
         const planning_interface::MotionPlanRequest& req,
         moveit_msgs::MotionPlanDetailedResponse& res);

    bool solveSingleAgent(const planning_scene::PlanningSceneConstPtr& planning_scene,
         const planning_interface::MotionPlanRequest& req,
         moveit_msgs::MotionPlanDetailedResponse& res);

    void clear();

    /***
     * @brief Get the names of the subgroups of a given group. This is used to infer the single-robot group names from
     * a multi-robot group name.
     * @param group_name The name of the multi-robot group.
     * @return A vector of the names of the subgroups.
     */
    std::vector<std::string> getSubgroupNames(const std::string& group_name);

    /***
     * @brief Split composite start and goal state (in the request) into subgroups.
     * @param req
     * @param subgroups
     * @param start_states
     * @param goal_states
     */
    void splitCompositeStartGoal(
            const planning_scene::PlanningSceneConstPtr& planning_scene,
            const planning_interface::MotionPlanRequest& req,
            std::string group_name,
            const std::vector<std::string>& subgroups,
            std::vector<std::vector<double>>& start_states,
            std::vector<std::vector<double>>& goal_states);
};
}  // namespace srmp_interface

#endif
