/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2010-2011, Willow Garage, 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 Willow Garage, Inc. 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.
 *
 * $Id$
 *
 */
#ifndef PCL_TRACKING_IMPL_HSV_COLOR_COHERENCE_H_
#define PCL_TRACKING_IMPL_HSV_COLOR_COHERENCE_H_

#if defined __GNUC__
#  pragma GCC system_header 
#endif

#include <pcl/tracking/hsv_color_coherence.h>
#include <Eigen/Dense>

namespace pcl
{
  namespace tracking
  {
    // utility
    union RGBValue
    {
      struct /*anonymous*/
      {
        unsigned char Blue; // Blue channel
        unsigned char Green; // Green channel
        unsigned char Red; // Red channel
      };
      float float_value;
      int int_value;
    };

    /** \brief Convert a RGB tuple to an HSV one.
      * \param[in] r the input Red component
      * \param[in] g the input Green component
      * \param[in] b the input Blue component
      * \param[out] fh the output Hue component
      * \param[out] fs the output Saturation component
      * \param[out] fv the output Value component
      */ 
    void 
    RGB2HSV (int r, int g, int b, float& fh, float& fs, float& fv)
    {
      // mostly copied from opencv-svn/modules/imgproc/src/color.cpp
      // revision is 4351
      const int hsv_shift = 12;
        
      static const int div_table[] = 
      {
        0, 1044480, 522240, 348160, 261120, 208896, 174080, 149211,
        130560, 116053, 104448, 94953, 87040, 80345, 74606, 69632,
        65280, 61440, 58027, 54973, 52224, 49737, 47476, 45412,
        43520, 41779, 40172, 38684, 37303, 36017, 34816, 33693,
        32640, 31651, 30720, 29842, 29013, 28229, 27486, 26782,
        26112, 25475, 24869, 24290, 23738, 23211, 22706, 22223,
        21760, 21316, 20890, 20480, 20086, 19707, 19342, 18991,
        18651, 18324, 18008, 17703, 17408, 17123, 16846, 16579,
        16320, 16069, 15825, 15589, 15360, 15137, 14921, 14711,
        14507, 14308, 14115, 13926, 13743, 13565, 13391, 13221,
        13056, 12895, 12738, 12584, 12434, 12288, 12145, 12006,
        11869, 11736, 11605, 11478, 11353, 11231, 11111, 10995,
        10880, 10768, 10658, 10550, 10445, 10341, 10240, 10141,
        10043, 9947, 9854, 9761, 9671, 9582, 9495, 9410,
        9326, 9243, 9162, 9082, 9004, 8927, 8852, 8777,
        8704, 8632, 8561, 8492, 8423, 8356, 8290, 8224,
        8160, 8097, 8034, 7973, 7913, 7853, 7795, 7737,
        7680, 7624, 7569, 7514, 7461, 7408, 7355, 7304,
        7253, 7203, 7154, 7105, 7057, 7010, 6963, 6917,
        6872, 6827, 6782, 6739, 6695, 6653, 6611, 6569,
        6528, 6487, 6447, 6408, 6369, 6330, 6292, 6254,
        6217, 6180, 6144, 6108, 6073, 6037, 6003, 5968,
        5935, 5901, 5868, 5835, 5803, 5771, 5739, 5708,
        5677, 5646, 5615, 5585, 5556, 5526, 5497, 5468,
        5440, 5412, 5384, 5356, 5329, 5302, 5275, 5249,
        5222, 5196, 5171, 5145, 5120, 5095, 5070, 5046,
        5022, 4998, 4974, 4950, 4927, 4904, 4881, 4858,
        4836, 4813, 4791, 4769, 4748, 4726, 4705, 4684,
        4663, 4642, 4622, 4601, 4581, 4561, 4541, 4522,
        4502, 4483, 4464, 4445, 4426, 4407, 4389, 4370,
        4352, 4334, 4316, 4298, 4281, 4263, 4246, 4229,
        4212, 4195, 4178, 4161, 4145, 4128, 4112, 4096
      };
      int hr = 180, hscale = 15;
      int h, s, v = b;
      int vmin = b, diff;
      int vr, vg;
                    
      v = std::max<int> (v, g);
      v = std::max<int> (v, r);
      vmin = std::min<int> (vmin, g);
      vmin = std::min<int> (vmin, r);
                
      diff = v - vmin;
      vr = v == r ? -1 : 0;
      vg = v == g ? -1 : 0;
                    
      s = diff * div_table[v] >> hsv_shift;
      h = (vr & (g - b)) +
          (~vr & ((vg & (b - r + 2 * diff))
          + ((~vg) & (r - g + 4 * diff))));
      h = (h * div_table[diff] * hscale +
          (1 << (hsv_shift + 6))) >> (7 + hsv_shift);
                
      h += h < 0 ? hr : 0;
      fh = static_cast<float> (h) / 180.0f;
      fs = static_cast<float> (s) / 255.0f;
      fv = static_cast<float> (v) / 255.0f;
    }
   
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    template <typename PointInT> double
    HSVColorCoherence<PointInT>::computeCoherence (PointInT &source, PointInT &target)
    {
      // convert color space from RGB to HSV
      RGBValue source_rgb, target_rgb;
      source_rgb.int_value = source.rgba;
      target_rgb.int_value = target.rgba;

      float source_h, source_s, source_v, target_h, target_s, target_v;
      RGB2HSV (source_rgb.Red, source_rgb.Blue, source_rgb.Green,
               source_h, source_s, source_v);
      RGB2HSV (target_rgb.Red, target_rgb.Blue, target_rgb.Green,
               target_h, target_s, target_v);
      // hue value is in 0 ~ 2pi, but circulated.
      const float _h_diff = std::abs (source_h - target_h);
      // Also need to compute distance other way around circle - but need to check which is closer to 0
      float _h_diff2;
      if (source_h < target_h)
        _h_diff2 = std::abs (1.0f + source_h - target_h); //Add 2pi to source, subtract target
      else 
        _h_diff2 = std::abs (1.0f + target_h - source_h); //Add 2pi to target, subtract source
      
      float h_diff;
      //Now we need to choose the smaller distance
      if (_h_diff < _h_diff2)
        h_diff = static_cast<float> (h_weight_) * _h_diff * _h_diff;
      else
        h_diff = static_cast<float> (h_weight_) * _h_diff2 * _h_diff2;

      const float s_diff = static_cast<float> (s_weight_) * (source_s - target_s) * (source_s - target_s);
      const float v_diff = static_cast<float> (v_weight_) * (source_v - target_v) * (source_v - target_v);
      const float diff2 = h_diff + s_diff + v_diff;
      
      return (1.0 / (1.0 + weight_ * diff2));
    }
  }
}

#define PCL_INSTANTIATE_HSVColorCoherence(T) template class PCL_EXPORTS pcl::tracking::HSVColorCoherence<T>;

#endif
