/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  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 itkKLMSegmentationRegion_h
#define itkKLMSegmentationRegion_h

#include "itkKLMSegmentationBorder.h"
#include "itkSegmentationRegion.h"
#include "ITKKLMRegionGrowingExport.h"

#include "vnl/vnl_vector.h"

namespace itk
{
/**
 * \class KLMSegmentationRegion
 * \brief Base class for KLMSegmentationRegion object
 *
 * itkKLMSegmentationRegion is the base class for the KLMSegmentationRegion
 * objects. It provides the basic function definitions that are inherent to
 * KLMSegmentationRegion objects.
 *
 * This object supports data handling of multiband images. The object
 * accepts images in vector format, where each pixel is a vector and each
 * element of the vector corresponds to an entry from 1 particular band of
 * a multiband dataset.
 *
 * We expect the user to provide the input to the routine in vector format.
 * A single band image is treated as a vector image with a single element
 * for every vector.
 *
 * Data structure for a region:
 * A region is defined as a closed area in the image that is surrounded
 * by a list of borders objects (see itkKLMSegmentationBorder class).
 *
 * Shown below is an initial two-dimensional 8x9 image with a 4x3 grid size
 * partition. The initial region blocks are labelled in hexadecimal.
 * Below the grid partition, the borders are shown as E, where C
 * is an image pixel from the initial image.  Note that each border is
 * placed in between two regions.  Each two-dimensional region is
 * surrounded by four borders.
 *
 * Initial regions of a 8 by 9 image with a 4 by 3 grid partition.
 * \f[\begin{tabular}{|c|c|c|c|c|c|c|c|c|}
 * \hline
 *   1 & 1 & 1 & 2 & 2 & 2 & 3 & 3 & 3 \\ \hline
 *   1 & 1 & 1 & 2 & 2 & 2 & 3 & 3 & 3 \\ \hline
 *   4 & 4 & 4 & 5 & 5 & 5 & 6 & 6 & 6 \\ \hline
 *   4 & 4 & 4 & 5 & 5 & 5 & 6 & 6 & 6 \\ \hline
 *   7 & 7 & 7 & 8 & 8 & 8 & 9 & 9 & 9 \\ \hline
 *   7 & 7 & 7 & 8 & 8 & 8 & 9 & 9 & 9 \\ \hline
 *   a & a & a & b & b & b & c & c & c \\ \hline
 *   a & a & a & b & b & b & c & c & c \\ \hline
 *  \end{tabular}\f]
 *
 * Region borders are shown as "E".
 * \f[\begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|}
 * \hline
 *   C & C & C &   & C & C & C &   & C & C & C \\ \hline
 *   C & C & C & E & C & C & C & E & C & C & C \\ \hline
 *     & E &   &   &   & E &   &   &   & E &   \\ \hline
 *   C & C & C &   & C & C & C &   & C & C & C \\ \hline
 *   C & C & C & E & C & C & C & E & C & C & C \\ \hline
 *     & E &   &   &   & E &   &   &   & E &   \\ \hline
 *   C & C & C &   & C & C & C &   & C & C & C \\ \hline
 *   C & C & C & E & C & C & C & E & C & C & C \\ \hline
 *     & E &   &   &   & E &   &   &   & E &   \\ \hline
 *   C & C & C &   & C & C & C &   & C & C & C \\ \hline
 *   C & C & C & E & C & C & C & E & C & C & C \\ \hline
 *  \end{tabular}\f]
 *
 * \ingroup RegionGrowingSegmentation
 * \ingroup ITKKLMRegionGrowing
 */

// Forward reference because of circular dependencies
class ITK_FORWARD_EXPORT KLMSegmentationBorder;

class ITKKLMRegionGrowing_EXPORT KLMSegmentationRegion : public SegmentationRegion
{
public:
  ITK_DISALLOW_COPY_AND_MOVE(KLMSegmentationRegion);

  /** Standard class type aliases. */
  using Self = KLMSegmentationRegion;
  using Superclass = SegmentationRegion;
  using Pointer = SmartPointer<Self>;
  using ConstPointer = SmartPointer<const Self>;

  /** Method for creation through the object factory. */
  itkNewMacro(Self);

  /** \see LightObject::GetNameOfClass() */
  itkOverrideGetNameOfClassMacro(KLMSegmentationRegion);

  /** Type definition for an double vector. */
  using MeanRegionIntensityType = vnl_vector<double>;

  /** Type definition for vector container that stores the borders
   * associated with a current region. */
  using RegionBorderVectorType = std::vector<KLMSegmentationBorder *>;
  using RegionBorderVectorSizeType = RegionBorderVectorType::size_type;

  /** Type definition for the region border vector iterators to be used. */
  using RegionBorderVectorIterator = RegionBorderVectorType::iterator;

  /** Type definition for the const region border vector iterators to be used.
   */
  using RegionBorderVectorConstIterator = RegionBorderVectorType::const_iterator;

  /** type definition for the region label type. */
  using RegionLabelType = Superclass::RegionLabelType;

  /** Get a head pointer to the vector container storing the borders
   * associated with a region. */
  RegionBorderVectorIterator
  GetRegionBorderItBegin();

  RegionBorderVectorConstIterator
  GetRegionBorderConstItBegin();

  /** Get a tail pointer to the vector container storing the borders
   * associated with a region. */
  RegionBorderVectorIterator
  GetRegionBorderItEnd();

  RegionBorderVectorConstIterator
  GetRegionBorderConstItEnd();

  /** Get the number of borders in the vector container storing the borders
   * associated with a region. */
  RegionBorderVectorSizeType
  GetRegionBorderSize() const;

  /** Set/Get the mean pixel intensity in the region. */
  itkSetMacro(MeanRegionIntensity, MeanRegionIntensityType);
  itkGetConstReferenceMacro(MeanRegionIntensity, MeanRegionIntensityType);

  /** Set the region with parameter values
   * defining the region. */
  void
  SetRegionParameters(MeanRegionIntensityType meanRegionIntensity, double regionArea, RegionLabelType label);

  /** Function to print the region parameters using std::cout. */
  void
  PrintRegionInfo();

  /** Insert a region border to the front of the list. */
  void
  PushFrontRegionBorder(KLMSegmentationBorder * pBorderCandidate);

  /** Insert a region border to the back of the list. */
  void
  PushBackRegionBorder(KLMSegmentationBorder * pBorderCandidate);

  /** Insert a region border to the list, where position is unknown
   * (sorting based on region labels is done to maintain consistency). */
  void
  InsertRegionBorder(KLMSegmentationBorder * pBorderCandidate);

  /** Insert a region border into the border list at a given location. */
  void
  InsertRegionBorder(RegionBorderVectorIterator RegionBorderVectorIt, KLMSegmentationBorder * pBorderCandidate);

  /** Delete a region border from the border list. */
  void
  DeleteRegionBorder(KLMSegmentationBorder * pBorderCandidate);

  /** Delete all region borders in the border list. */
  void
  DeleteAllRegionBorders();

  /** Set the region with parameter values (mean and area)
   * defining the region when merged with the new region. */
  void
  CombineRegionParameters(const Self * region);

  /** Compute the energy cost (mean squared difference scaled by area)
   * that would result if this region is merged with another region. */
  double
  EnergyFunctional(const Self * region);

  /** Reset a region's label to that of the supplied region and update
   * the regions borders to that of the supplied region. */
  void
  ResetRegionLabelAndUpdateBorders(Self * region);

  /** Splice the regions borders from the new region into the current
   * region.  If duplicate borders are found, the duplicate border
   * region is not inserted into the new region borders list, rather,
   * it has its pointers to region1 and region2 set to nullptr and
   * Lambda set to -1.0.
   *
   * For example, take an image with 3 regions A, B, C
   * \f[\begin{tabular}{|c|c|}
   * \hline
   *   A & A \\ \hline
   *   B & C \\ \hline
   *  \end{tabular}\f]
   * where region A has region borders A-B and A-C;
   * region B has region borders A-B and B-C; and
   * region C has region borders A-C and B-C.
   *
   * Suppose region border A-B has been removed, so that region B
   * can be merged into region A.  When splicing the region
   * borders from A and B into the new region A, duplicate
   * region borders A-C and A-C (one of which was formerly B-C)
   * will be present.  In this case, one of the region borders
   * A-C is given the combined length of the two former borders
   * and is put into the region borders list.  The other is
   * nullified by having its pointers to region1 and region2 set
   * to nullptr and its Lambda value set to -1.0.
   */
  void
  SpliceRegionBorders(Self * region);

  /** Recalculate the Lambda values using EvaluateLambda() for all
   * the borders defining the region. */
  void
  UpdateRegionBorderLambda();

protected:
  KLMSegmentationRegion();
  ~KLMSegmentationRegion() override;
  void
  PrintSelf(std::ostream & os, Indent indent) const override;

private:
  RegionBorderVectorType  m_RegionBorderVector{};
  MeanRegionIntensityType m_MeanRegionIntensity{};
}; // class SegmentationRegion
} // namespace itk

#endif
