/*=========================================================================
 *
 *  Copyright UMC Utrecht and contributors
 *
 *  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 __itkOpenCLImageFormat_h
#define __itkOpenCLImageFormat_h

#include "itkOpenCL.h"
#include <ostream>

namespace itk
{
/** \class OpenCLImageFormat
 * \brief The OpenCLImageFormat class represents the format of a OpenCLImage.
 * \ingroup OpenCL
 */

// Forward declaration
class OpenCLContext;

class ITKOpenCL_EXPORT OpenCLImageFormat
{
public:

  /** Standard class typedefs. */
  typedef OpenCLImageFormat Self;

  /** \enum OpenCLImageFormat::ImageType
   * This enum defines the image type for the OpenCL image object.
   * \value BUFFER The OpenCL buffer.
   * \value IMAGE2D The 2D image
   * \value IMAGE3D The 2D image.
   * \value IMAGE2D_ARRAY The 1D image array.
   * \value IMAGE1D The The 1D image.
   * \value IMAGE1D_ARRAY The 1D array.
   * \value IMAGE1D_BUFFER The 1D buffer. */
  enum ImageType {
    BUFFER         = 0x10F0,
    IMAGE2D        = 0x10F1,
    IMAGE3D        = 0x10F2,
    IMAGE2D_ARRAY  = 0x10F3,
    IMAGE1D        = 0x10F4,
    IMAGE1D_ARRAY  = 0x10F5,
    IMAGE1D_BUFFER = 0x10F6
  };

  /** \enum OpenCLImageFormat::ChannelOrder
   * This enum specifies the number of channels and the channel layout i.e.
   * the memory layout in which channels are stored in the image. Valid values
   * are described in the table below:
   *
   * \value R Single red channel.
   * \value A Single alpha channel.
   * \value RG Red then green channel.
   * \value RA Red then alpha channel.
   * \value RGB Red, green, and blue channels.
     \note This format can only be used if channel data type is
           CL_UNORM_SHORT_565, CL_UNORM_SHORT_555 or CL_UNORM_INT101010.
   * \value RGBA Red, green, blue, then alpha channels.
   * \value BGRA Blue, green, red, then alpha channels.
   * \value ARGB Alpha, red, green, then blue channels.
   * \value INTENSITY Single intensity channel.
   * \note This format can only be used if channel data type is
   *       CL_UNORM_INT8, CL_UNORM_INT16, CL_SNORM_INT8, CL_SNORM_INT16,
   *       CL_HALF_FLOAT, or CL_FLOAT.
   * \value LUMINANCE Single LUMINANCE channel.
   * \note  This format can only be used if channel data type is
   *        CL_UNORM_INT8, CL_UNORM_INT16, CL_SNORM_INT8, CL_SNORM_INT16,
   *        CL_HALF_FLOAT, or CL_FLOAT.
   * \value Rx Similar to CL_R except alpha = 0 at edges.
   * \value RGx Similar to CL_RG except alpha = 0 at edges.
   * \value RGBx Similar to CL_RGB except alpha = 0 at edges.
   * \note This format can only be used if channel data type is
   *       CL_UNORM_SHORT_565, CL_UNORM_SHORT_555 or CL_UNORM_INT101010.
   * \value DEPTH Allow a CL image to be created from a GL depth texture.
   * \value DEPTH_STENCIL Allow a CL image to be created from a GL depth-stencil texture. */
  enum ChannelOrder {
    R             = 0x10B0,
    A             = 0x10B1,
    RG            = 0x10B2,
    RA            = 0x10B3,
    RGB           = 0x10B4,
    RGBA          = 0x10B5,
    BGRA          = 0x10B6,
    ARGB          = 0x10B7,
    INTENSITY     = 0x10B8,
    LUMINANCE     = 0x10B9,
    Rx            = 0x10BA,
    RGx           = 0x10BB,
    RGBx          = 0x10BC,
    DEPTH         = 0x10BD,
    DEPTH_STENCIL = 0x10BE
  };

  /** \enum OpenCLImageFormat::ChannelType
   * This enum describes the size of the channel data type.
   * The number of bits per element determined by the image_channel_data_type
   * and image_channel_order must be a power of two. The list of supported
   * values is described in the table below:
   *
   * \value SNORM_INT8 Each channel component is a normalized signed 8-bit integer value.
   * \value SNORM_INT16 Each channel component is a normalized signed 16-bit integer value.
   * \value UNORM_INT8 Each channel component is a normalized unsigned 8-bit integer value.
   * \value UNORM_INT16 Each channel component is a normalized unsigned 16-bit integer value.
   * \value UNORM_SHORT_565 Represents a normalized 5-6-5 3-channel RGB image.
   * \note The channel order must be CL_RGB or CL_RGBx.
   * \value UNORM_SHORT_555 Represents a normalized x-5-5-5 4-channel xRGB image.
   * \note The channel order must be CL_RGB or CL_RGBx.
   * \value UNORM_INT_101010 Represents a normalized x-10-10-10 4-channel xRGB image.
   * \note The channel order must be CL_RGB or CL_RGBx.
   * \value SIGNED_INT8 Each channel component is an unnormalized signed 8-bit integer value.
   * \value SIGNED_INT16 Each channel component is an unnormalized signed 16-bit integer value.
   * \value SIGNED_INT32 Each channel component is an unnormalized signed 32-bit integer value.
   * \value UNSIGNED_INT8 Each channel component is an unnormalized unsigned 8-bit integer value.
   * \value UNSIGNED_INT16 Each channel component is an unnormalized unsigned 16-bit integer value.
   * \value UNSIGNED_INT32 Each channel component is an unnormalized unsigned 32-bit integer value.
   * \value HALF_FLOAT Each channel component is a 16-bit half-float value.
   * \value FLOAT Each channel component is a single precision floating-point value.
   * \value UNORM_INT24 Each channel component is stored as an unsigned normalized 24-bit value. */
  enum ChannelType {
    SNORM_INT8       = 0x10D0,
    SNORM_INT16      = 0x10D1,
    UNORM_INT8       = 0x10D2,
    UNORM_INT16      = 0x10D3,
    UNORM_SHORT_565  = 0x10D4,
    UNORM_SHORT_555  = 0x10D5,
    UNORM_INT_101010 = 0x10D6,
    SIGNED_INT8      = 0x10D7,
    SIGNED_INT16     = 0x10D8,
    SIGNED_INT32     = 0x10D9,
    UNSIGNED_INT8    = 0x10DA,
    UNSIGNED_INT16   = 0x10DB,
    UNSIGNED_INT32   = 0x10DC,
    HALF_FLOAT       = 0x10DD,
    FLOAT            = 0x10DE,
    UNORM_INT24      = 0x10DF
  };

  /** Constructs a null OpenCL image format descriptor.
   * \sa IsNull() */
  OpenCLImageFormat();

  /** Constructs an OpenCL image format descriptor from
   * \a channelOrder and \a channelType. */
  OpenCLImageFormat(
    const OpenCLImageFormat::ChannelOrder channelOrder,
    const OpenCLImageFormat::ChannelType channelType );

  /** Constructs an OpenCL image format descriptor from
   * \a imageType \a channelOrder and \a channelType. */
  OpenCLImageFormat(
    const OpenCLImageFormat::ImageType imageType,
    const OpenCLImageFormat::ChannelOrder channelOrder,
    const OpenCLImageFormat::ChannelType channelType );

  /** Returns true if this OpenCL image format descriptor is null. */
  bool IsNull() const;

  /** Returns the image type in this OpenCL image format.
   *\sa GetChannelOrder(), GetChannelType() */
  OpenCLImageFormat::ImageType GetImageType() const;

  /** Returns the order of channels in this OpenCL image format.
   *\sa GetChannelType(), GetImageType() */
  OpenCLImageFormat::ChannelOrder GetChannelOrder() const;

  /** Returns the representation type for channels in this OpenCL image format.
   * \sa GetChannelOrder(), GetImageType() */
  OpenCLImageFormat::ChannelType GetChannelType() const;

private:

  cl_mem_object_type m_ImageType;
  cl_image_format    m_Format;

  /** friends from OpenCL core */
  friend class OpenCLContext;
  friend class OpenCLImage;
};

/** Operator ==
* Returns true if \a lhs OpenCL image format is the same as \a rhs, false otherwise.
* \sa operator!= */
bool ITKOpenCL_EXPORT operator==( const OpenCLImageFormat & lhs, const OpenCLImageFormat & rhs );

/** Operator !=
* Returns true if \a lhs OpenCL image format is not the same as \a rhs, false otherwise.
* \sa operator== */
bool ITKOpenCL_EXPORT operator!=( const OpenCLImageFormat & lhs, const OpenCLImageFormat & rhs );

/** Stream out operator for OpenCLImageFormat */
template< typename charT, typename traits >
inline
std::basic_ostream< charT, traits > &
operator<<( std::basic_ostream< charT, traits > & strm,
  const OpenCLImageFormat & format )
{
  if( format.IsNull() )
  {
    strm << "OpenCLImageFormat(null)";
    return strm;
  }

  strm << "OpenCLImageFormat(";
  switch( int( format.GetImageType() ) )
  {
    case OpenCLImageFormat::BUFFER:
      strm << "BUFFER, "; break;
    case OpenCLImageFormat::IMAGE2D:
      strm << "IMAGE2D, "; break;
    case OpenCLImageFormat::IMAGE3D:
      strm << "IMAGE3D, "; break;
    case OpenCLImageFormat::IMAGE2D_ARRAY:
      strm << "IMAGE2D_ARRAY, "; break;
    case OpenCLImageFormat::IMAGE1D:
      strm << "IMAGE1D, "; break;
    case OpenCLImageFormat::IMAGE1D_ARRAY:
      strm << "IMAGE1D_ARRAY, "; break;
    case OpenCLImageFormat::IMAGE1D_BUFFER:
      strm << "IMAGE1D_BUFFER, "; break;
    default:
      strm << int( format.GetImageType() ) << ", "; break;
  }
  switch( int( format.GetChannelOrder() ) )
  {
    case OpenCLImageFormat::R:
      strm << "R, "; break;
    case OpenCLImageFormat::A:
      strm << "A, "; break;
    case OpenCLImageFormat::RG:
      strm << "RG, "; break;
    case OpenCLImageFormat::RA:
      strm << "RA, "; break;
    case OpenCLImageFormat::RGB:
      strm << "RGB, "; break;
    case OpenCLImageFormat::RGBA:
      strm << "RGBA, "; break;
    case OpenCLImageFormat::BGRA:
      strm << "BGRA, "; break;
    case OpenCLImageFormat::ARGB:
      strm << "ARGB, "; break;
    case OpenCLImageFormat::INTENSITY:
      strm << "INTENSITY, "; break;
    case OpenCLImageFormat::LUMINANCE:
      strm << "LUMINANCE, "; break;
    case OpenCLImageFormat::Rx:
      strm << "Rx, "; break;
    case OpenCLImageFormat::RGx:
      strm << "RGx, "; break;
    case OpenCLImageFormat::RGBx:
      strm << "RGBx, "; break;
    case OpenCLImageFormat::DEPTH:
      strm << "DEPTH, "; break;
    case OpenCLImageFormat::DEPTH_STENCIL:
      strm << "DEPTH_STENCIL, "; break;
    default:
      strm << int( format.GetChannelOrder() ) << ", "; break;
  }
  switch( int( format.GetChannelType() ) )
  {
    case OpenCLImageFormat::SNORM_INT8:
      strm << "SNORM_INT8"; break;
    case OpenCLImageFormat::SNORM_INT16:
      strm << "SNORM_INT16"; break;
    case OpenCLImageFormat::UNORM_INT8:
      strm << "UNORM_INT8"; break;
    case OpenCLImageFormat::UNORM_INT16:
      strm << "UNORM_INT16"; break;
    case OpenCLImageFormat::UNORM_SHORT_565:
      strm << "UNORM_SHORT_565"; break;
    case OpenCLImageFormat::UNORM_SHORT_555:
      strm << "UNORM_SHORT_555"; break;
    case OpenCLImageFormat::UNORM_INT_101010:
      strm << "UNORM_INT_101010"; break;
    case OpenCLImageFormat::SIGNED_INT8:
      strm << "SIGNED_INT8"; break;
    case OpenCLImageFormat::SIGNED_INT16:
      strm << "SIGNED_INT16"; break;
    case OpenCLImageFormat::SIGNED_INT32:
      strm << "SIGNED_INT32"; break;
    case OpenCLImageFormat::UNSIGNED_INT8:
      strm << "UNSIGNED_INT8"; break;
    case OpenCLImageFormat::UNSIGNED_INT16:
      strm << "UNSIGNED_INT16"; break;
    case OpenCLImageFormat::UNSIGNED_INT32:
      strm << "UNSIGNED_INT32"; break;
    case OpenCLImageFormat::HALF_FLOAT:
      strm << "HALF_FLOAT"; break;
    case OpenCLImageFormat::FLOAT:
      strm << "FLOAT"; break;
    case OpenCLImageFormat::UNORM_INT24:
      strm << "UNORM_INT24"; break;
    default:
      strm << int( format.GetChannelType() ); break;
  }

  strm << ')' << std::endl;
  return strm;
}


} // end namespace itk

#endif /* __itkOpenCLImageFormat_h */
