#ifndef __LSGRM_GRAPH_OPERATIONS_H
#define __LSGRM_GRAPH_OPERATIONS_H

#include "lsgrmHeader.h"
#include "grmGraphOperations.h"
#include "otbVectorImage.h"
#include "otbMultiChannelExtractROI.h"
#include "itkGrayscaleFillholeImageFilter.h"

namespace lsgrm
{

struct ProcessingTile
{
  long int rows[2]; // lower and upper rows (-1 means that the row has not be considered)
  long int columns[2]; // lower and upper columns (-1 means that the row has not be considered)
  long int tileNeighbors[8]; // tile Neighbors at (top, top right, right, bottom right, bottom, bottom left, left, top left)
  long int margin[4]; // Is there a margin at top, left, bottom or right
  otb::VectorImage<double>::RegionType region; // The image region

  // Temporary files
  std::string nodeFileName;
  std::string edgeFileName;
  std::string nodeMarginFileName;
  std::string edgeMarginFileName;
};

// Read an image region
template<class TSegmenter>
typename TSegmenter::ImageType::Pointer ReadImageRegion(
    typename TSegmenter::ImageType * inputPtr,
    typename TSegmenter::ImageType::RegionType region);

template<class TSegmenter>
typename TSegmenter::LabelImageType::Pointer
MergeAllGraphsAndAchieveSegmentation(
    const typename TSegmenter::ParamType& params,
    const float& threshold,
    std::vector<ProcessingTile>& tiles,
    const unsigned int nbTilesX,
    const unsigned int nbTilesY,
    const unsigned int imageWidth,
    const unsigned int imageHeight,
    const unsigned int imageBands,
    unsigned int numberOfIterations);

template<class TSegmenter>
long long unsigned int RunFirstPartialSegmentation(
    typename TSegmenter::ImageType * inputPtr,
    const typename TSegmenter::ParamType& params,
    const float& threshold,
    const unsigned int niter,
    const unsigned int niter2,
    std::vector<ProcessingTile>& tiles,
    const unsigned int nbTilesX,
    const unsigned int nbTilesY,
    const unsigned int tileWidth,
    const unsigned int tileHeight,
    bool& isFusion);

template<class TSegmenter>
long long unsigned int RunPartialSegmentation(
    const typename TSegmenter::ParamType& params,
    const float& threshold,
    const unsigned int niter,
    std::vector<ProcessingTile>& tiles,
    const unsigned int nbTilesX,
    const unsigned int nbTilesY,
    const unsigned int imageWidth,
    const unsigned int imageHeight,
    const unsigned int imageBands,
    bool& isFusion);

template<class TSegmenter>
void RemoveUselessNodes(ProcessingTile& tile,
    typename TSegmenter::GraphType& graph,
    const unsigned int imageWidth,
    const unsigned int numberOfLayers);


template<class TSegmenter>
void UpdateNeighborsOfNoneDuplicatedNodes(std::unordered_map<long unsigned int,
    std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap,
    const unsigned int imageWidth,
    const unsigned int imageHeight);

template<class TSegmenter>
void RemoveDuplicatedNodes(std::unordered_map<long unsigned int,
    std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap,
    typename TSegmenter::GraphType& graph,
    const unsigned int imageWidth);

template<class TSegmenter>
void BuildBorderPixelMap(typename TSegmenter::GraphType& graph,
    ProcessingTile& tile,
    const unsigned int rowTile,
    const unsigned int colTile,
    const unsigned int nbTilesX,
    const unsigned int nbTilesY,
    std::unordered_map<long unsigned int,
    std::vector<typename TSegmenter::NodePointerType> >& borderPixelMap,
    const unsigned int imageWidth);

template<class TSegmenter>
void InsertNodesFromTile(typename TSegmenter::GraphType& graph,
    ProcessingTile& tile, bool margin = true);

template<class TSegmenter>
void AddStabilityMargin(typename TSegmenter::GraphType& graph,
    std::vector<ProcessingTile>& tiles,
    const unsigned int row,
    const unsigned int col,
    const unsigned int nbTilesX,
    const unsigned int nbTilesY);

template<class TSegmenter>
void WriteStabilityMargin(std::unordered_map<
    typename TSegmenter::NodePointerType,
    unsigned int>& stabilityMargin,
    const std::string& nodesPath,
    const std::string& edgesPath);

template<class TSegmenter>
void ExtractStabilityMargin(std::unordered_map<typename TSegmenter::NodePointerType, unsigned int>& nodeMap,
    const unsigned int pmax);

template<class TSegmenter>
void ExploreDFS(typename TSegmenter::NodePointerType s,
    const unsigned int p,
    std::unordered_map<typename TSegmenter::NodePointerType, unsigned int>& Cb,
    const unsigned int pmax);

template<class TSegmenter>
void DetectBorderNodes(typename TSegmenter::GraphType& graph,
    ProcessingTile& tile,
    std::unordered_map<typename TSegmenter::NodePointerType, unsigned int>& borderNodeMaps,
    const unsigned int imageWidth,
    const unsigned int imageHeight);

template<class TSegmenter>
void ReadGraph(TSegmenter& segmenter,
    const std::string& nodesPath,
    const std::string& edgesPath);

template<class TSegmenter>
void ReadGraph(typename TSegmenter::GraphType& graph,
    const std::string& nodesPath,
    const std::string& edgesPath);

template<class TSegmenter>
void WriteGraph(typename TSegmenter::GraphType& graph,
    const std::string& nodesFile,
    const std::string& edgesFile);

template<class TSegmenter>
void RemoveUnstableSegments(typename TSegmenter::GraphType& graph,
    ProcessingTile& tile,
    const unsigned int imageWidth);

template<class TSegmenter>
void RemoveEdgeToUnstableNode(typename TSegmenter::NodePointerType nodePtr);

template<class TSegmenter>
void RescaleGraph(typename TSegmenter::GraphType& graph,
    ProcessingTile& tile,
    const unsigned int rowTile,
    const unsigned int colTile,
    const unsigned int tileWidth,
    const unsigned int tileHeight,
    const unsigned int imageWidth);
}

#include "lsgrmGraphOperations.txx"
#endif