/* Distributed under the Apache License, Version 2.0.
   See accompanying NOTICE file for details.*/

#pragma once

#include "LBM.h"

#include "itkAffineTransform.h"
#include "itkAutoCropLabelMapFilter.h"
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkImageIOBase.h"
#include "itkImageRegionIteratorWithIndex.h"
#include "itkLabelMapToLabelImageFilter.h"
#include "itkLabelMap.h"
#include "itkLabelImageToLabelMapFilter.h"
#include "itkNearestNeighborInterpolateImageFunction.h"
#include "itkResampleImageFilter.h"

#define LBMITK_TEMPLATE typename PixelType, typename ImageType
#define LBMITK_TYPES PixelType, ImageType

namespace LBM
{
namespace ITK
{

  enum class DownsampleType { Fraction = 0, Length = 1 };

  struct LBM_DECL Configuration
  {
    std::string image_filename;
    DownsampleType downsample_type = DownsampleType::Fraction;
    double downsample_value = 0;
    double grid_spacing_conversion = 1;
  };

  /// Main driving  method that creates and runs a LBMITK based class
  LBM_DECL bool Compute(Configuration const& cfg, LBM::Run& lbm);


  LBM_DECL bool LoadConfiguration(std::string const& config_file, LBM::Run& lbm, Configuration& itk_cfg);

  // ITK Methods for extracting a running from a mesh from a medical image
  template <LBMITK_TEMPLATE>
  class LBM_DECL Run
  {
  public:
    Run(Configuration const& cfg, LBM::Run& lbm);
    virtual ~Run();

    virtual bool Compute();

    virtual bool ReadImage(std::string const& filename);

    virtual bool WriteImage(std::string const& filename);

    virtual bool CropImage();

    virtual bool OrientImageToX();

    virtual bool NormalizeImage();

    virtual bool DownsampleImage();

  protected:

    // Basic types
    using Image = typename ImageType::Pointer;
    using Size = typename ImageType::SizeType;
    using Spacing = typename ImageType::SpacingType;
    using Origin = typename ImageType::PointType;
    using TransformType = itk::AffineTransform<double, 3>;
    // Filter types
    using InterpType = itk::NearestNeighborInterpolateImageFunction<ImageType>;
    using ResampleFilterType = itk::ResampleImageFilter<ImageType, ImageType>;
    // Label Types
    using LabelObjectType = itk::LabelObject<PixelType,3>;
    using LabelMapType = itk::LabelMap<LabelObjectType>;
    using CropType = itk::AutoCropLabelMapFilter<LabelMapType>;
    using CropSize = typename CropType::SizeType;
    using LabelImageToMapFilterType = itk::LabelImageToLabelMapFilter<ImageType, LabelMapType>;
    using LabelMapToImageFilterType = itk::LabelMapToLabelImageFilter<LabelMapType, ImageType>;

    LBM::ITK::Configuration const& m_cfg;
    LBM::Run&                      m_lbm;
    Image                          m_image;
    std::stringstream              m_ss;
  };

}
}
