/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#ifndef mitkPixelTypeTraits_h
#define mitkPixelTypeTraits_h

#include <itkDiffusionTensor3D.h>
#include <itkImage.h>
#include <itkImageIOBase.h>
#include <itkRGBAPixel.h>
#include <itkRGBPixel.h>
#include <itkVectorImage.h>

/** \file mitkPixelTypeTraits.h
  * \brief Compile-time type traits for resolving pixel component types, composite pixel types,
  *        and ITK image types from MITK pixel type information.
  *
  * The pixel type traits are in general used for compile time resolution of the component type and
  * the number of components for compound types like the ones in ItkImageType.
  * The default values are used to define the corresponding variable also for scalar types.
  *
  * \ingroup Core
  */

namespace itk
{
  /** Forward declaration of the Variable Length Vector class from ITK */
  template <typename TValueType>
  class VariableLengthVector;
}

/**
 * \brief Register a custom pixel component type with the MITK type system.
 *
 * Creates a specialization of MapPixelComponentType for \a type mapping it to the
 * integer constant \a ctype, and a specialization of PixelComponentTypeToString
 * returning \a name.
 *
 * \param type  The C++ type to register (e.g. \c MyScalarType).
 * \param ctype An integer constant identifying this component type.
 * \param name  A human-readable string name for the type.
 */
#define MITK_PIXEL_COMPONENT_TYPE(type, ctype, name)                                                                   \
  template <>                                                                                                          \
  struct mitk::MapPixelComponentType<type>                                                                             \
  {                                                                                                                    \
    static const int value = ctype;                                                                                    \
  } template <>                                                                                                        \
  std::string mitk::PixelComponentTypeToString()                                                                       \
  {                                                                                                                    \
    return name;                                                                                                       \
  }

namespace mitk
{
  /** \brief Starting integer value for user-defined pixel types (beyond the built-in ITK pixel types). */
  static const int PixelUserType = static_cast<int>(itk::IOPixelEnum::MATRIX) + 1;

  /** \brief Starting integer value for user-defined pixel component types (beyond the built-in ITK component types). */
  static const int PixelComponentUserType = static_cast<int>(itk::IOComponentEnum::DOUBLE) + 1;

  /**
   * Maps pixel component types (primitive types like int, short, double, etc. and custom
   * types) to and integer constant. Specialize this template for custom types by using the
   * #MITK_PIXEL_COMPONENT_TYPE macro.
   */
  template <typename T>
  struct MapPixelComponentType
  {
    static const itk::IOComponentEnum value = itk::ImageIOBase::MapPixelType<T>::CType;
  };

  /**
    \brief This is an implementation of a type trait to provide a compile-time check for PixelType used in
    the instantiation of an itk::Image
  */
  template <typename T>
  struct isPrimitiveType
  {
    static const bool value = false;
  };

/** \brief Provides a partial specialization for the \sa isPrimitiveType object */
#define DEFINE_TYPE_PRIMITIVE(_TYPEIN)                                                                                 \
  template <>                                                                                                          \
  struct isPrimitiveType<_TYPEIN>                                                                                      \
  {                                                                                                                    \
    static const bool value = true;                                                                                    \
  }

  /** \brief Partial specialization (unsigned char) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(unsigned char);
  /** \brief Partial specialization (char) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(char);
  /** \brief Partial specialization (signed char) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(signed char);
  /** \brief Partial specialization (unsigned short) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(unsigned short);
  /** \brief Partial specialization (short) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(short);
  /** \brief Partial specialization (unsigned int) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(unsigned int);
  /** \brief Partial specialization (int) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(int);
  /** \brief Partial specialization (long int) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(long int);
  /** \brief Partial specialization (long unsigned int) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(long unsigned int);
  /** \brief Partial specialization (float) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(float);
  /** \brief Partial specialization (double) for the isPrimitiveType object */
  DEFINE_TYPE_PRIMITIVE(double);

  /**
   * \brief Compile-time trait that maps a pixel type and dimension to the corresponding ITK image type.
   *
   * For scalar and fixed-length compound pixel types, maps to \c itk::Image.
   * For \c itk::VariableLengthVector pixel types, maps to \c itk::VectorImage.
   *
   * \tparam TPixelType  The pixel type.
   * \tparam VDimension  The image dimension (0 means the type itself is an image type).
   */
  template <typename TPixelType, unsigned int VDimension = 0>
  struct ImageTypeTrait
  {
    typedef itk::Image<TPixelType, VDimension> ImageType;
    static const bool IsVectorImage = false;
  };

  /** \brief Specialization of ImageTypeTrait for itk::VariableLengthVector pixel types (vector images). */
  template <typename TPixelType, unsigned int VDimension>
  struct ImageTypeTrait<itk::VariableLengthVector<TPixelType>, VDimension>
  {
    typedef itk::VectorImage<TPixelType, VDimension> ImageType;
    static const bool IsVectorImage = true;
  };

  /** \brief Specialization of ImageTypeTrait when VDimension is 0 (type is already an image type). */
  template <typename T>
  struct ImageTypeTrait<T, 0>
  {
    typedef T ImageType;
    static const bool IsVectorImage = false;
  };

  /** \brief Specialization of ImageTypeTrait for itk::VectorImage when VDimension is 0. */
  template <typename TPixelType, unsigned int VDimension>
  struct ImageTypeTrait<itk::VectorImage<TPixelType, VDimension>, 0>
  {
    typedef itk::VectorImage<TPixelType, VDimension> ImageType;
    static const bool IsVectorImage = true;
  };

  /** \brief Compile-time trait for resolving the ValueType from an ItkImageType */
  template <bool flag, typename T>
  struct PixelTypeTrait
  {
    typedef T ValueType;
  };

  /** \brief Partial specialization for the PixelTypeTrait
  *
  * Specialization for the false value. Used to define the value type for non-primitive pixel types
  */
  template <typename T>
  struct PixelTypeTrait<false, T>
  {
    typedef typename T::ValueType ValueType;
  };

  /** \brief Compile time resolving of the type of a component */
  template <typename T>
  struct GetComponentType
  {
    typedef typename PixelTypeTrait<isPrimitiveType<T>::value, T>::ValueType ComponentType;
  };

  /** \brief Object for compile-time resolving of the number of components for given type.
    *
    * Default value for the component number is 1
  */
  template <bool V, typename T>
  struct ComponentsTrait
  {
    static const size_t Size = 1;
  };

  /** \brief Partial specialization for the ComponentsTraits in case of compound types */
  template <typename T>
  struct ComponentsTrait<false, T>
  {
    static const size_t Size = T::ValueType::Length;
  };

  /** \brief Type alias for the ITK pixel type enumeration. */
  typedef itk::IOPixelEnum itkIOPixelType;

  /** \brief Type alias for the ITK component type enumeration. */
  typedef itk::IOComponentEnum itkIOComponentType;

  /** \brief Object for compile-time translation of a composite pixel type into an itk::ImageIOBase::IOPixelType
   * information
   *
   * The default value of the IOCompositeType is the UNKNOWNPIXELTYPE, the default value will be used for all but the
   * types below with own partial specialization. The values of the IOCompositeType member in the specializations
   * correspond
   * to the values of the itk::ImageIOBase::IOPixelType enum values.
   */
  template <class T>
  struct MapCompositePixelType
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::UNKNOWNPIXELTYPE;
  };

  //------------------------
  // Partial template specialization for fixed-length types
  //------------------------

  template <class C>
  struct MapCompositePixelType<itk::RGBPixel<C>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::RGB;
  };

  template <class C>
  struct MapCompositePixelType<itk::RGBAPixel<C>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::RGBA;
  };

  template <class C>
  struct MapCompositePixelType<itk::DiffusionTensor3D<C>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::DIFFUSIONTENSOR3D;
  };

  template <class C>
  struct MapCompositePixelType<itk::VariableLengthVector<C>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR;
  };

  //------------------------
  // Partial template specialization for variable-length types
  //------------------------
  template <class C, unsigned int N>
  struct MapCompositePixelType<itk::Vector<C, N>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR;
  };

  template <class C, unsigned int N>
  struct MapCompositePixelType<itk::FixedArray<C, N>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR;
  };

  template <class C, unsigned int N>
  struct MapCompositePixelType<itk::CovariantVector<C, N>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR;
  };

  template <class C, unsigned int N>
  struct MapCompositePixelType<itk::Matrix<C, N>>
  {
    static const itkIOPixelType IOCompositeType = itkIOPixelType::MATRIX;
  };

  /** \brief Object for compile-time translation of a pixel type into an itk::ImageIOBase::IOPixelType information
   *
   * The first template parameter is the pixel type to be translated, the second parameter determines the processing
   * way. For non-primitive types the first template parameter is passed to the MapCompositePixelType object to be
   * resolved there
   * for primitive types the value is set to SCALAR.
   *
   * To initialize the flag correctly in compile-time use the \sa isPrimitiveType<T> trait.
   */
  template <class T, bool Primitive>
  struct MapPixelType
  {
    static const itkIOPixelType IOPixelType = MapCompositePixelType<T>::IOCompositeType;
    static const itkIOComponentType IOComponentType = MapPixelComponentType<typename GetComponentType<T>::ComponentType>::value;
  };

  /** \brief Partial specialization for setting the IOPixelType for primitive types to SCALAR */
  template <class T>
  struct MapPixelType<T, true>
  {
    static const itkIOPixelType IOPixelType = itkIOPixelType::SCALAR;
    static const itkIOComponentType IOComponentType = MapPixelComponentType<T>::value;
  };

} // end namespace mitk

#endif
