//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#ifndef ITKPROCESS_HPP
#define ITKPROCESS_HPP

#include <ITKConfig.hpp>
#include <Process.hpp>

#include <itkObject.h>
#include <itkImage.h>
#include <itkImportImageFilter.h>
#include <itkImageSliceConstIteratorWithIndex.h>
#include <itkImageRegionConstIterator.h>
#include <itkImageFileWriter.h>
#include <itkAdaptImageFilter.h>

namespace mgx 
{
  typedef itk::Image<unsigned short int, 3> UImageType;
  typedef itk::Image<signed short int, 3> SImageType;
  typedef itk::Image<float, 3> FImageType;
  
  struct mgxITKutil_EXPORT UnsignedToSignedAccessor {
    typedef signed short ExternalType;
    typedef unsigned short InternalType;
  
    static ExternalType Get(const InternalType& value) {
      return ExternalType(value >> 1);
    }
  
    static void Set(InternalType& output, const ExternalType& value)
    {
      if(value > 0)
        output = InternalType(value) << 1;
      else
        output = 0;
    }
  };
  
  struct mgxITKutil_EXPORT SignedToUnsignedAccessor {
    typedef unsigned short ExternalType;
    typedef signed short InternalType;
  
    static ExternalType Get(const InternalType& value)
    {
      if(value > 0)
        return ExternalType(value) << 1;
      return 0;
    }
  
    static void Set(InternalType& output, const ExternalType& value) {
      output = InternalType(value >> 1);
    }
  };
  
  struct mgxITKutil_EXPORT UnsignedToFloatAccessor {
    typedef float ExternalType;
    typedef unsigned short InternalType;
  
    static ExternalType Get(const InternalType& value) {
      return float(value) / 65535.;
    }
  
    static void Set(InternalType& output, const ExternalType& value)
    {
      if(value > 0)
        output = InternalType(value * 65535.);
      else
        output = 0;
    }
  };
  
  struct mgxITKutil_EXPORT FloatToUnsignedAccessor {
    typedef unsigned short ExternalType;
    typedef float InternalType;
  
    static ExternalType Get(const InternalType& value)
    {
      if(value > 0)
        return ExternalType(value * 65535.);
      else
        return 0u;
    }
  
    static void Set(InternalType& output, const ExternalType& value) {
      output = InternalType(float(value) / 65535.);
    }
  };
  
  class mgxITKutil_EXPORT FImageConverter : public itk::Object {
    FImageConverter();
  
    typedef itk::ImportImageFilter<ushort, 3> ImportFilterType;
    typedef itk::AdaptImageFilter<UImageType, FImageType, UnsignedToFloatAccessor> AdapterType;
  
  public:
    ~FImageConverter();
  
    typedef FImageConverter Self;
    typedef itk::SmartPointer<const Self> ConstPointer;
    typedef itk::SmartPointer<Self> Pointer;
  
    virtual itk::SmartPointer<itk::LightObject> CreateAnother() const;
    virtual const char* GetNameOfClass() const;
  
    void SetStore(const Store* store);
    void Update();
    FImageType::Pointer GetOutput();
  
    static bool TransferImage(const Stack* stack, Store* store, FImageType::ConstPointer image);
    static bool TransferImage(Stack* stack, Store* store, FImageType::ConstPointer image);
  
    static Pointer New();
  
  protected:
    const Store* store;
    ImportFilterType::Pointer importer;
    AdapterType::Pointer adapter;
  };
  
  class mgxITKutil_EXPORT SImageConverter : public itk::Object {
    SImageConverter();
  
    typedef itk::ImportImageFilter<ushort, 3> ImportFilterType;
    typedef itk::AdaptImageFilter<UImageType, SImageType, UnsignedToSignedAccessor> AdapterType;
  
  public:
    ~SImageConverter();
  
    typedef SImageConverter Self;
    typedef itk::SmartPointer<const Self> ConstPointer;
    typedef itk::SmartPointer<Self> Pointer;
  
    virtual itk::SmartPointer<itk::LightObject> CreateAnother() const;
    virtual const char* GetNameOfClass() const;
  
    void SetStore(const Store* store);
    void Update();
    SImageType::Pointer GetOutput();
  
    static bool TransferImage(const Stack* stack, Store* store, SImageType::ConstPointer image);
    static bool TransferImage(Stack* stack, Store* store, SImageType::ConstPointer image);
  
    static Pointer New();
  
  protected:
    const Store* store;
    ImportFilterType::Pointer importer;
    AdapterType::Pointer adapter;
  };
  
  class mgxITKutil_EXPORT UImageConverter : public itk::Object {
    UImageConverter();
  
    typedef itk::ImportImageFilter<ushort, 3> ImportFilterType;
  
  public:
    ~UImageConverter();
  
    typedef UImageConverter Self;
    typedef itk::SmartPointer<const Self> ConstPointer;
    typedef itk::SmartPointer<Self> Pointer;
    typedef ImportFilterType::SpacingType SpacingType;
    typedef ImportFilterType::RegionType RegionType;
    typedef ImportFilterType::OriginType OriginType;
  
    virtual itk::SmartPointer<itk::LightObject> CreateAnother() const;
    virtual const char* GetNameOfClass() const;
  
    void SetStore(const Store* store);
    void Update();
    UImageType::Pointer GetOutput();
  
    const SpacingType& GetSpacing() const {
      return importer->GetSpacing();
    }
    const RegionType& GetRegion() const {
      return importer->GetRegion();
    }
    const OriginType& GetOrigin() const {
      return importer->GetOrigin();
    }
  
    static bool TransferImage(Stack* stack, Store* store, UImageType::ConstPointer image);
    static bool TransferImage(const Stack* stack, Store* store, UImageType::ConstPointer image);
  
    static Pointer New();
  
  protected:
    const Store* store;
    ImportFilterType::Pointer importer;
  };
}

#endif
