/*
 * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * 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 otbLabelObjectToPolygonFunctor_h
#define otbLabelObjectToPolygonFunctor_h

#include <vector>

//TODO change this include  have to define from what inherate this class
#include "otbPolyLineParametricPathWithValue.h" //for vcl_abs

#include "itkPoint.h"
#include "itkIndex.h"

namespace otb
{

namespace Functor
{

/** \class LabelObjectToPolygonFunctor
 *  \brief This class vectorizes a LabelObject to a Polygon.
 *
 * The algorithm follows a finite states machine described in the
 * following paper:
 *
 * "An algorithm for the rapid computation of boundaries of run-length
 * encoded regions", Francis K. H. Queck, in Pattern Recognition 33
 * (2000), p 1637-1649.
 *
 * Lines in the RLE are first sorted in lexicographical order (to
 * ensure ordered RLE), and the finite states machine tracks the edge
 * following 8 canonical states given by the configuration of two
 * consecutive raws of lines.
 *
 * Iterations are done until convergence which is guaranteed to
 * happen.
 *
 * Please be aware that this functor is not thread-safe.
 *
 *
 * \ingroup OTBLabelMap
 */
template <class TLabelObject, class TPolygon>
class LabelObjectToPolygonFunctor
{
public:
  /** Template parameters typedefs */
  typedef TLabelObject                                LabelObjectType;
  /** Const iterator over LabelObject lines */
  typedef typename LabelObjectType::ConstLineIterator  ConstLineIteratorType;

  typedef typename LabelObjectType::LineType          LineType;
  typedef typename LineType::IndexType                IndexType;
  typedef TPolygon                                    PolygonType;
  typedef typename PolygonType::Pointer               PolygonPointerType;
  typedef typename PolygonType::VertexType            VertexType;
  typedef itk::Point<double, 2>                       PointType;
  typedef itk::Vector<double, 2>                      SpacingType;
  typedef itk::Index<2>                               RegionIndexType;

  /**
   * \param labelObject the label object to vectorize
   * \return The vectorized label object as a polygon.
   */
  inline PolygonType * operator ()(LabelObjectType * labelObject);

  /** Get  name of class. */
  const char * GetNameOfClass()
    {
      return "LabelObjectToPolygonFunctor";
    }

  /** Set the start index of the underlying image */
  void SetStartIndex(const RegionIndexType& index)
  {
    m_StartIndex = index;
  }
  /** Get the start index */
  const RegionIndexType& GetStartIndex() const
  {
    return m_StartIndex;
  }
  /** Set the origin of the underlying image */
  void SetOrigin(const PointType& origin)
  {
    m_Origin = origin;
  }
  /** Get the origin */
  const PointType& GetOrigin() const
  {
    return m_Origin;
  }
  /** Set the spacing of the underlying image */
  void SetSpacing(const SpacingType& spacing)
  {
    m_Spacing = spacing;
  }
  /** Get the spacing */
  const SpacingType& GetSpacing() const
  {
    return m_Spacing;
  }

  /** Constructor */
  LabelObjectToPolygonFunctor() : m_Polygon(ITK_NULLPTR),
    m_CurrentState(UP_LEFT),
    m_PositionFlag(LEFT_END),
    m_StartingPoint(),
    m_CurrentPoint(),
    m_CurrentRun(),
    m_CurrentLine(0),
    m_Solution(),
    m_LineOffset(0),
    m_StartIndex(),
    m_Origin(),
    m_Spacing(1.)
  {
    m_StartingPoint.Fill(0);
    m_CurrentPoint.Fill(0);
    m_CurrentRun.Fill(0);
    m_StartIndex.Fill(0);
    m_Origin.Fill(0);
}

  /** Destructor */
  virtual ~LabelObjectToPolygonFunctor(){}

private:
  /// Internal structures
  typedef std::vector<LineType>        RunsPerLineType;
  typedef std::vector<RunsPerLineType> RunsPerLineVectorType;
  typedef std::vector<IndexType>       IndexVectorType;

  /// Internal enums
  enum StateType {UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT};
  enum PositionFlagType {LEFT_END, RIGHT_END};

  /// Compare two line in the lexicographical order
  static bool LexicographicalLineCompare(const LineType&  l1, const LineType& l2);

  /// Check if the given run index (index in line, line) is valid
  inline bool IsRunIndexValid(const IndexType& index) const;

  /// Check if the point lies within the range of the line
  inline IndexType Within(const IndexType& point, unsigned int line) const;

  /// Return the left-end of the run
  inline IndexType LeftEnd(const IndexType& runIndex) const;

  /// Return the right-end of the run
  inline IndexType RightEnd(const IndexType& runIndex) const;

  /** Return the right-most run whose left end lies within the range of
   * the given run.
   */
  inline IndexType RightMostLeftEndInside(unsigned int line, const IndexType& point, const IndexType& run) const;

  /** Return the left-most run whose right end lies within the range of
   * the given run.
   */
  inline IndexType LeftMostRightEndInside(unsigned int line, const IndexType& point, const IndexType& run) const;

  /// Walk left to update the finite states machine.
  inline void WalkLeft(unsigned int line,
                       const IndexType& startPoint,
                       const IndexType& endPoint,
                       PolygonType * polygon,
                       const StateType state);

  /// Walk right to update the finite states machine.
  inline void WalkRight(unsigned int line,
                        const IndexType& startPoint,
                        const IndexType& endPoint,
                        PolygonType * polygon,
                        const StateType state);

  // Apply origin and spacing
  VertexType IndexToPoint(const VertexType& index) const;

  PolygonPointerType m_Polygon;

  // Internal structure to store runs
  RunsPerLineVectorType m_InternalDataSet;

  /// The current state
  StateType m_CurrentState;
  /// The position flag
  PositionFlagType m_PositionFlag;

  // The starting point for vectorization
  IndexType m_StartingPoint;

  /// The current point for vectorization
  IndexType m_CurrentPoint;

  /// The current run for vectorization
  IndexType m_CurrentRun;

  /// The current line for vectorization
  int m_CurrentLine;

  /// The vector of vectorized boundaries
  IndexVectorType m_Solution;

  /// The line offset from start of the region
  unsigned int m_LineOffset;

  // The following will be used for coordinate transform
  RegionIndexType m_StartIndex;
  PointType       m_Origin;
  SpacingType     m_Spacing;

}; // end class LabelObjectToPolygonFunctor

} // end namespace Functor

} // end namespace otb

#ifndef OTB_MANUAL_INSTANTIATION
#include "otbLabelObjectToPolygonFunctor.txx"
#endif

#endif
