/*
 * Software License Agreement (BSD License)
 * 
 * Point Cloud Library (PCL) - www.pointclouds.org
 * Copyright (c) 2009-2012, Willow Garage, Inc.
 * Copyright (c) 2012-, Open Perception, Inc.
 * 
 * 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(s) 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.
 *
 */

#ifndef PCL_JOINT_ICP_H_
#define PCL_JOINT_ICP_H_

// PCL includes
#include <pcl/registration/icp.h>
namespace pcl
{
  /** \brief @b JointIterativeClosestPoint extends ICP to multiple frames which
   *  share the same transform. This is particularly useful when solving for 
   *  camera extrinsics using multiple observations. When given a single pair of 
   *  clouds, this reduces to vanilla ICP.
    *
    * \author Stephen Miller
    * \ingroup registration
    */
  template <typename PointSource, typename PointTarget, typename Scalar = float>
  class JointIterativeClosestPoint : public IterativeClosestPoint<PointSource, PointTarget, Scalar>
  {
    public:
      typedef typename IterativeClosestPoint<PointSource, PointTarget, Scalar>::PointCloudSource PointCloudSource;
      typedef typename PointCloudSource::Ptr PointCloudSourcePtr;
      typedef typename PointCloudSource::ConstPtr PointCloudSourceConstPtr;

      typedef typename IterativeClosestPoint<PointSource, PointTarget, Scalar>::PointCloudTarget PointCloudTarget;
      typedef typename PointCloudTarget::Ptr PointCloudTargetPtr;
      typedef typename PointCloudTarget::ConstPtr PointCloudTargetConstPtr;

      typedef pcl::search::KdTree<PointTarget> KdTree;
      typedef typename pcl::search::KdTree<PointTarget>::Ptr KdTreePtr;

      typedef pcl::search::KdTree<PointSource> KdTreeReciprocal;
      typedef typename KdTree::Ptr KdTreeReciprocalPtr;


      typedef PointIndices::Ptr PointIndicesPtr;
      typedef PointIndices::ConstPtr PointIndicesConstPtr;

      typedef boost::shared_ptr<JointIterativeClosestPoint<PointSource, PointTarget, Scalar> > Ptr;
      typedef boost::shared_ptr<const JointIterativeClosestPoint<PointSource, PointTarget, Scalar> > ConstPtr;

      typedef typename pcl::registration::CorrespondenceEstimationBase<PointSource, PointTarget, Scalar> CorrespondenceEstimation;
      typedef typename CorrespondenceEstimation::Ptr CorrespondenceEstimationPtr;
      typedef typename CorrespondenceEstimation::ConstPtr CorrespondenceEstimationConstPtr;


      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::reg_name_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::getClassName;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::setInputSource;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::input_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::indices_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::target_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::nr_iterations_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::max_iterations_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::previous_transformation_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::final_transformation_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::transformation_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::transformation_epsilon_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::converged_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::corr_dist_threshold_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::inlier_threshold_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::min_number_correspondences_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::update_visualizer_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::euclidean_fitness_epsilon_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::correspondences_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::transformation_estimation_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::correspondence_estimation_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::correspondence_rejectors_;
      
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::use_reciprocal_correspondence_;
      
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::convergence_criteria_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::source_has_normals_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::target_has_normals_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::need_source_blob_;
      using IterativeClosestPoint<PointSource, PointTarget, Scalar>::need_target_blob_;


      typedef typename IterativeClosestPoint<PointSource, PointTarget, Scalar>::Matrix4 Matrix4;

      /** \brief Empty constructor. */
      JointIterativeClosestPoint ()
      {
        IterativeClosestPoint<PointSource, PointTarget, Scalar> ();
        reg_name_ = "JointIterativeClosestPoint";
      };

      /** \brief Empty destructor */
      virtual ~JointIterativeClosestPoint () {}


      /** \brief Provide a pointer to the input source 
        * (e.g., the point cloud that we want to align to the target)
        */
      virtual void
      setInputSource (const PointCloudSourceConstPtr& /*cloud*/)
      {
        PCL_WARN ("[pcl::%s::setInputSource] Warning; JointIterativeClosestPoint expects multiple clouds. Please use addInputSource.", 
            getClassName ().c_str ());
        return;
      }

      /** \brief Add a source cloud to the joint solver
        *
        * \param[in] cloud source cloud
        */
      inline void
      addInputSource (const PointCloudSourceConstPtr &cloud)
      {
        // Set the parent InputSource, just to get all cached values (e.g. the existence of normals).
        if (sources_.empty ())
          IterativeClosestPoint<PointSource, PointTarget, Scalar>::setInputSource (cloud);
        sources_.push_back (cloud);
      }
      
      /** \brief Provide a pointer to the input target 
        * (e.g., the point cloud that we want to align to the target)
        */
      virtual void
      setInputTarget (const PointCloudTargetConstPtr& /*cloud*/)
      {
        PCL_WARN ("[pcl::%s::setInputTarget] Warning; JointIterativeClosestPoint expects multiple clouds. Please use addInputTarget.", 
            getClassName ().c_str ());
        return;
      }

      /** \brief Add a target cloud to the joint solver
        *
        * \param[in] cloud target cloud
        */
      inline void
      addInputTarget (const PointCloudTargetConstPtr &cloud)
      {
        // Set the parent InputTarget, just to get all cached values (e.g. the existence of normals).
        if (targets_.empty ())
          IterativeClosestPoint<PointSource, PointTarget, Scalar>::setInputTarget (cloud);
        targets_.push_back (cloud);
      }

      /** \brief Add a manual correspondence estimator
        * If you choose to do this, you must add one for each 
        * input source / target pair. They do not need to have trees 
        * or input clouds set ahead of time.
        *
        * \param[in] ce Correspondence estimation
        */
      inline void
      addCorrespondenceEstimation (CorrespondenceEstimationPtr ce)
      {
        correspondence_estimations_.push_back (ce);
      }

      /** \brief Reset my list of input sources
        */
      inline void
      clearInputSources ()
      { sources_.clear (); }

      /** \brief Reset my list of input targets
        */
      inline void
      clearInputTargets ()
      { targets_.clear (); }

      /** \brief Reset my list of correspondence estimation methods.
        */
      inline void
      clearCorrespondenceEstimations ()
      { correspondence_estimations_.clear (); }


    protected:

      /** \brief Rigid transformation computation method  with initial guess.
        * \param output the transformed input point cloud dataset using the rigid transformation found
        * \param guess the initial guess of the transformation to compute
        */
      virtual void 
      computeTransformation (PointCloudSource &output, const Matrix4 &guess);
      
      /** \brief Looks at the Estimators and Rejectors and determines whether their blob-setter methods need to be called */
      void
      determineRequiredBlobData ();

      std::vector<PointCloudSourceConstPtr> sources_;
      std::vector<PointCloudTargetConstPtr> targets_;
      std::vector<CorrespondenceEstimationPtr> correspondence_estimations_;
  };

}

#include <pcl/registration/impl/joint_icp.hpp>

#endif  //#ifndef PCL_JOINT_ICP_H_


