/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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
 *
 *         http://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 itkMeshConvertPixelTraits_h
#define itkMeshConvertPixelTraits_h

#include "itkArray.h"
#include "itkMatrix.h"
#include "itkOffset.h"
#include "itkCovariantVector.h"
#include "itkVariableLengthVector.h"
#include "itkVector.h"
#include "itkPoint.h"

namespace itk
{

/** \class MeshConvertPixelTraits
 *  \brief Traits class used to by ConvertPixels to convert blocks of pixels.
 *
 *  TOutputPixelType is the destination type. The input type is inferred
 *  by the templated static function Convert.
 *
 *  This implementaion, does a simple assignment operator, so if you are
 *  going from from a higher bit representation to a lower bit one (int to
 *  char), you may want to specialize and add some sort of transfer function.
 *  \ingroup ITKIOMeshBase
 */
template<typename PixelType>
class MeshConvertPixelTraits
{
public:
  /** Determine the pixel data type. */
  using ComponentType = typename PixelType::ComponentType;

  /** Return the number of components per pixel. */
  static unsigned int GetNumberOfComponents()
  { return PixelType::GetNumberOfComponents();}

  static unsigned int GetNumberOfComponents(const PixelType& )
  { return PixelType::GetNumberOfComponents();}

  /** Return the nth component of the pixel. */
  static ComponentType GetNthComponent(int c, const PixelType& pixel)
  { return pixel.GetNthComponent(c); }

  /** Set the nth component of the pixel. */
  static void SetNthComponent(int c, PixelType& pixel, const ComponentType& v)
  { pixel.SetNthComponent(c, v); }

  /** Return a single scalar value from this pixel. */
  static ComponentType GetScalarValue(const PixelType& pixel)
  { return pixel.GetScalarValue(); }

};

#define ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(type)                     \
template<>                                                                \
  class MeshConvertPixelTraits<type>                                      \
  {                                                                       \
  public:                                                                 \
  using ComponentType = type;                                             \
  static unsigned int GetNumberOfComponents()                             \
  {                                                                       \
  return 1;                                                               \
  }                                                                       \
  static unsigned int GetNumberOfComponents(const type& itkNotUsed(pixel))\
  {                                                                       \
  return 1;                                                               \
  }                                                                       \
  static ComponentType GetNthComponent(int itkNotUsed(c), const type& pixel)\
  {                                                                       \
  return pixel;                                                           \
  }                                                                       \
  static void SetNthComponent(int , type& pixel, const ComponentType& v)  \
  {                                                                       \
  pixel = v;                                                              \
  }                                                                       \
  static type GetScalarValue(const type& pixel)                           \
  {                                                                       \
  return pixel;                                                           \
  }                                                                       \
  };

  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(float)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(double)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(int)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(char)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(short)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(unsigned int)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(signed char)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(unsigned char)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(unsigned short)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(long)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(unsigned long)
  ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL(bool)

#undef ITK_DEFAULTCONVERTTRAITS_NATIVE_SPECIAL

//
//  Default traits for the Offset<> pixel type
//

#define ITK_MESH_DEFAULTCONVERTTRAITS_OFFSET_TYPE(dimension)                       \
  template<>                                                                       \
  class MeshConvertPixelTraits< Offset<dimension> >                                \
  {                                                                                \
  public:                                                                          \
  using TargetType = Offset<dimension>;                                           \
  using ComponentType = TargetType::OffsetValueType;                              \
  static unsigned int GetNumberOfComponents()                                      \
  {                                                                                \
  return dimension;                                                                \
  }                                                                                \
  static unsigned int GetNumberOfComponents(const TargetType& itkNotUsed(pixel))   \
  {                                                                                \
  return dimension;                                                                \
  }                                                                                \
  static ComponentType GetNthComponent(int c, const TargetType& pixel)             \
  {                                                                                \
  return pixel[c];                                                                 \
  }                                                                                \
  static void SetNthComponent(int i, TargetType & pixel, const ComponentType& v)   \
  {                                                                                \
  pixel[i] = v;                                                                    \
  }                                                                                \
  static ComponentType GetScalarValue(const TargetType& pixel)                     \
  {                                                                                \
  return pixel[0];                                                                 \
  }                                                                                \
  };                                                                               \


// Define traits for Offset<> from dimensions 1 to 5
  ITK_MESH_DEFAULTCONVERTTRAITS_OFFSET_TYPE(1)
  ITK_MESH_DEFAULTCONVERTTRAITS_OFFSET_TYPE(2)
  ITK_MESH_DEFAULTCONVERTTRAITS_OFFSET_TYPE(3)
  ITK_MESH_DEFAULTCONVERTTRAITS_OFFSET_TYPE(4)
  ITK_MESH_DEFAULTCONVERTTRAITS_OFFSET_TYPE(5)

//
//  Default traits for the pixel types deriving from FixedArray<>
//

#define ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE(type,componenttype, dimension) \
  template<>                                                                         \
  class MeshConvertPixelTraits< type< componenttype, dimension> >                    \
  {                                                                                  \
  public:                                                                            \
  using TargetType = type< componenttype, dimension >;                              \
  using ComponentType = componenttype;                           \
  static unsigned int GetNumberOfComponents()                                        \
  {                                                                                  \
  return dimension;                                                                  \
  }                                                                                  \
  static unsigned int GetNumberOfComponents(const TargetType& itkNotUsed(pixel))     \
  {                                                                                  \
  return dimension;                                                                  \
  }                                                                                  \
  static ComponentType GetNthComponent(int c, const TargetType& pixel)               \
  {                                                                                  \
  return pixel[c];                                                                   \
  }                                                                                  \
  static void SetNthComponent(int i, TargetType & pixel, const ComponentType& v)     \
  {                                                                                  \
  pixel[i] = v;                                                                      \
  }                                                                                  \
  static ComponentType GetScalarValue(const TargetType& pixel)                       \
  {                                                                                  \
  return pixel[0];                                                                   \
  }                                                                                  \
  };                                                                                 \

//
//
// Define traits for Classed deriving from FixedArray from dimensions 1 to 6
// These classes include: Vector, CovariantVector and Point.
//
//
#define ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, Type)      \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE(ArrayType,Type,1)                     \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE(ArrayType,Type,2)                     \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE(ArrayType,Type,3)                     \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE(ArrayType,Type,4)                     \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE(ArrayType,Type,5)                     \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE(ArrayType,Type,6)

#define ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_TYPES_MACRO(ArrayType)      \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, char);           \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, signed char);    \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, unsigned char);  \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, short);          \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, unsigned short); \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, int);            \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, unsigned int);   \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, long);           \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, unsigned long);  \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, float);          \
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_MACRO(ArrayType, double);

  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_TYPES_MACRO(Vector);
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_TYPES_MACRO(CovariantVector);
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_TYPES_MACRO(Point);
  ITK_MESH_DEFAULTCONVERTTRAITS_FIXEDARRAY_TYPE_ALL_TYPES_MACRO(FixedArray);

//
//  End of Traits for the classes deriving from FixedArray.
//
//


//
//  Default traits for the pixel types deriving from Matrix<>
//

#define ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE(type,componenttype,rows,cols)    \
template<>                                                                         \
  class MeshConvertPixelTraits< type< componenttype, rows, cols > >                \
  {                                                                                \
  public:                                                                          \
  using TargetType = type< componenttype, rows, cols >;                           \
  using ComponentType = componenttype;                         \
  static unsigned int GetNumberOfComponents()                                      \
  {                                                                                \
  return rows * cols;                                                              \
  }                                                                                \
  static unsigned int GetNumberOfComponents(const TargetType& itkNotUsed(pixel))   \
  {                                                                                \
  return rows * cols;                                                              \
  }                                                                                \
  static ComponentType GetNthComponent(int c, const TargetType& pixel)             \
  {                                                                                \
  const unsigned int row = c / cols;                                               \
  const unsigned int col = c % cols;                                               \
  return pixel[row][col];                                                          \
  }                                                                                \
  static void SetNthComponent(int i, TargetType & pixel, const ComponentType& v)   \
  {                                                                                \
  const unsigned int row = i / cols;                                               \
  const unsigned int col = i % cols;                                               \
  pixel[row][col] = v;                                                             \
  }                                                                                \
  static ComponentType GetScalarValue(const TargetType& pixel)                     \
  {                                                                                \
  return pixel[0][0];                                                              \
  }                                                                                \
  };                                                                               \

//
//
// Define traits for Classed deriving from Matrix from dimensions 1 to 6
//
//
#define ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, Type)      \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE(ArrayType,Type,1,1)                   \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE(ArrayType,Type,2,2)                   \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE(ArrayType,Type,3,3)                   \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE(ArrayType,Type,4,4)                   \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE(ArrayType,Type,5,5)                   \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE(ArrayType,Type,6,6)

#define ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_TYPES_MACRO(ArrayType)      \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, char);           \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, signed char);    \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, unsigned char);  \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, short);          \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, unsigned short); \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, int);            \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, unsigned int);   \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, long);           \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, unsigned long);  \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, float);          \
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_MACRO(ArrayType, double);

//
// Add here other classes that derive from Matrix or that have the same API
//
  ITK_MESH_DEFAULTCONVERTTRAITS_MATRIX_TYPE_ALL_TYPES_MACRO(Matrix);

//
//  End of Traits for the classes deriving from Matrix.
//
//


//
//  Default traits for the pixel types deriving from std::complex<>
//

#define ITK_MESH_DEFAULTCONVERTTRAITS_COMPLEX_TYPE( componenttype )                \
template<>                                                                         \
  class MeshConvertPixelTraits< ::std::complex< componenttype > >                  \
  {                                                                                \
  public:                                                                          \
  using TargetType = ::std::complex< componenttype>;                              \
  using ComponentType = componenttype;                         \
  static unsigned int GetNumberOfComponents()                                      \
  {                                                                                \
  return 2;                                                                        \
  }                                                                                \
  static unsigned int GetNumberOfComponents(const TargetType & itkNotUsed(pixel))  \
  {                                                                                \
  return 2;                                                                        \
  }                                                                                \
  static ComponentType GetNthComponent(int i, TargetType & pixel)                  \
  {                                                                                \
  if( i == 0 )                                                                     \
    {                                                                              \
    return pixel.imag();                                                           \
    }                                                                              \
  else                                                                             \
    {                                                                              \
    return pixel.real();                                                           \
    }                                                                              \
  }                                                                                \
  static void SetNthComponent(int i, TargetType & pixel, const ComponentType& v)   \
  {                                                                                \
  if( i == 0 )                                                                     \
    {                                                                              \
    pixel = TargetType( v, pixel.imag() );                                         \
    }                                                                              \
  else                                                                             \
    {                                                                              \
    pixel = TargetType( pixel.real(), v );                                         \
    }                                                                              \
  }                                                                                \
  static ComponentType GetScalarValue(const TargetType& pixel)                     \
  {                                                                                \
  return std::norm(pixel);                                                         \
  }                                                                                \
  };                                                                               \

  ITK_MESH_DEFAULTCONVERTTRAITS_COMPLEX_TYPE(float);
  ITK_MESH_DEFAULTCONVERTTRAITS_COMPLEX_TYPE(double);

#define ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(type,componenttype)               \
template<>                                                                         \
  class MeshConvertPixelTraits< type< componenttype> >                             \
  {                                                                                \
  public:                                                                          \
  using TargetType = type< componenttype >;                            \
  using ComponentType = componenttype;                         \
  static unsigned int GetNumberOfComponents()                                      \
  {                                                                                \
  return 0;                                                                        \
  }                                                                                \
  static unsigned int GetNumberOfComponents(const TargetType& pixel)               \
  {                                                                                \
  return pixel.Size();                                                             \
  }                                                                                \
  static ComponentType GetNthComponent(int c, const TargetType& pixel)             \
  {                                                                                \
  return pixel[c];                                                                 \
  }                                                                                \
  static void SetNthComponent(int i, TargetType & pixel, const ComponentType& v)   \
  {                                                                                \
  pixel[i] = v;                                                                    \
  }                                                                                \
  static ComponentType GetScalarValue(const TargetType& pixel)                     \
  {                                                                                \
  return pixel[0];                                                                 \
  }                                                                                \
  };                                                                               \

#define ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE_ALL_TYPES_MACRO(ArrayType)        \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, char);                       \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, signed char);                \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, unsigned char);              \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, short);                      \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, unsigned short);             \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, int);                        \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, unsigned int);               \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, long);                       \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, unsigned long);              \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, float);                      \
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE(ArrayType, double);

  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE_ALL_TYPES_MACRO(Array);
  ITK_MESH_DEFAULTCONVERTTRAITS_ARRAY_TYPE_ALL_TYPES_MACRO(VariableLengthVector);
//
//  End of Traits for the classes deriving from std::complex.
//
//

} // end namespace itk
#endif
