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

  Program: GDCM (Grassroots DICOM). A DICOM library

  Copyright (c) 2006-2011 Mathieu Malaterre
  All rights reserved.
  See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "gdcmLookupTable.h"
#include "gdcmSwapper.h"
#include <vector>
#include <set>
#include <iomanip>
#include <algorithm>

#include <cstring>

namespace gdcm
{

class LookupTableInternal
{
public:
  LookupTableInternal():RGB()
  {
    Length[0] = Length[1] = Length[2] = 0;
    Subscript[0] = Subscript[1] = Subscript[2] = 0;
    BitSize[0] = BitSize[1] = BitSize[2] = 0;
  }
  unsigned int Length[3]; // In DICOM the length is specified on a short
  // but 65536 is expressed as 0 ...
  unsigned short Subscript[3];
  unsigned short BitSize[3];
  std::vector<unsigned char> RGB;
};

LookupTable::LookupTable()
{
  Internal = new LookupTableInternal;
  BitSample = 0;
  IncompleteLUT = false;
}

LookupTable::~LookupTable()
{
  delete Internal;
}

bool LookupTable::Initialized() const
{
  bool b1 = BitSample != 0;
  bool b2 =
    Internal->BitSize[0] != 0 &&
    Internal->BitSize[1] != 0 &&
    Internal->BitSize[2] != 0;
  return b1 && b2;
}

void LookupTable::Clear()
{
  BitSample = 0;
  IncompleteLUT = false;
  delete Internal;
  Internal = new LookupTableInternal;
}

void LookupTable::Allocate( unsigned short bitsample )
{
  if( bitsample == 8 )
    {
    Internal->RGB.resize( 256 * 3 );
    }
  else if ( bitsample == 16 )
    {
    Internal->RGB.resize( 65536 * 2 * 3 );
    }
  else
    {
    gdcmAssertAlwaysMacro(0);
    }
  BitSample = bitsample;
}

void LookupTable::InitializeLUT(LookupTableType type, unsigned short length,
  unsigned short subscript, unsigned short bitsize)
{
  if( bitsize != 8 && bitsize != 16 )
    {
    return;
    }
  gdcm_assert( type >= RED && type <= BLUE );
  gdcm_assert( subscript == 0 );
  gdcm_assert( bitsize == 8 || bitsize == 16 );
  if( length == 0 )
    {
    Internal->Length[type] = 65536;
    }
  else
    {
    if( length != 256 )
      {
      IncompleteLUT = true;
      }
    Internal->Length[type] = length;
    }
  Internal->Subscript[type] = subscript;
  Internal->BitSize[type] = bitsize;
}

unsigned int LookupTable::GetLUTLength(LookupTableType type) const
{
  return Internal->Length[type];
}

void LookupTable::SetLUT(LookupTableType type, const unsigned char *array,
  unsigned int length)
{
  (void)length;
  //if( !Initialized() ) return;
  if( !Internal->Length[type] )
    {
    gdcmDebugMacro( "Need to set length first" );
    return;
    }

  if( !IncompleteLUT )
    {
    if( Internal->RGB.size() != 3*Internal->Length[type]*(BitSample/8) ) {
      gdcmErrorMacro( "Invalid length for LUT data" );
      return;
    }
    }
  // Too funny: 05115014-mr-siemens-avanto-syngo-with-palette-icone.dcm
  // There is pseudo PALETTE_COLOR LUT in the Icon, if one look carefully the LUT values
  // goes like this: 0, 1, 2, 3, 4, 5, 6, 7 ...
  if( BitSample == 8 )
    {
      const unsigned int mult = Internal->BitSize[type]/8;
      const unsigned int mult2 = length / Internal->Length[type];
      gdcm_assert( Internal->Length[type] * mult2 == length );
      if( Internal->Length[type]*mult == length
          || Internal->Length[type]*mult + 1 == length )
      {
        gdcm_assert( mult2 == 1 || mult2 == 2 );
        unsigned int offset = 0;
        if( mult == 2 )
        {
          offset = 1;
        }
        for( unsigned int i = 0; i < Internal->Length[type]; ++i)
        {
          gdcm_assert( i*mult+offset < length );
          gdcm_assert( 3*i+type < Internal->RGB.size() );
          Internal->RGB[3*i+type] = array[i*mult+offset];
        }
      }
      else
      {
        unsigned int offset = 0;
        gdcm_assert( mult2 == 2 );
        for( unsigned int i = 0; i < Internal->Length[type]; ++i)
        {
          gdcm_assert( i*mult2+offset < length );
          gdcm_assert( 3*i+type < Internal->RGB.size() );
          Internal->RGB[3*i+type] = array[i*mult2+offset];
        }
      }
    }
  else if( BitSample == 16 )
    {
    gdcm_assert( Internal->Length[type]*(BitSample/8) == length );
    uint16_t *uchar16 = (uint16_t*)(void*)Internal->RGB.data();
    const uint16_t *array16 = (const uint16_t*)(const void*)array;
    for( unsigned int i = 0; i < Internal->Length[type]; ++i)
      {
      gdcm_assert( 2*i < length );
      gdcm_assert( 2*(3*i+type) < Internal->RGB.size() );
      uchar16[3*i+type] = array16[i];
      }
    }
}

void LookupTable::GetLUT(LookupTableType type, unsigned char *array, unsigned int &length) const
{
  if( BitSample == 8 )
    {
    const unsigned int mult = Internal->BitSize[type]/8;
    length = Internal->Length[type]*mult;
    unsigned int offset = 0;
    if( mult == 2 )
      {
      offset = 1;
      }
    for( unsigned int i = 0; i < Internal->Length[type]; ++i)
      {
      gdcm_assert( i*mult+offset < length );
      gdcm_assert( 3*i+type < Internal->RGB.size() );
      array[i*mult+offset] = Internal->RGB[3*i+type];
      }
    }
  else if( BitSample == 16 )
    {
    length = Internal->Length[type]*(BitSample/8);
    uint16_t *uchar16 = (uint16_t*)(void*)Internal->RGB.data();
    uint16_t *array16 = (uint16_t*)(void*)array;
    for( unsigned int i = 0; i < Internal->Length[type]; ++i)
      {
      gdcm_assert( 2*i < length );
      gdcm_assert( 2*(3*i+type) < Internal->RGB.size() );
      array16[i] = uchar16[3*i+type];
      }
    }
}

void LookupTable::GetLUTDescriptor(LookupTableType type, unsigned short &length,
  unsigned short &subscript, unsigned short &bitsize) const
{
  gdcm_assert( type >= RED && type <= BLUE );
  if( Internal->Length[type] == 65536 )
    {
    length = 0;
    }
  else
    {
    length = (unsigned short)Internal->Length[type];
    }
  subscript = Internal->Subscript[type];
  bitsize = Internal->BitSize[type];

  // postcondition
  gdcm_assert( subscript == 0 );
  gdcm_assert( bitsize == 8 || bitsize == 16 );
}

void LookupTable::InitializeRedLUT(unsigned short length,
unsigned short subscript,
unsigned short bitsize)
  {
  InitializeLUT(RED, length, subscript, bitsize);
  }
void LookupTable::InitializeGreenLUT(unsigned short length,
  unsigned short subscript,
  unsigned short bitsize)
{
  InitializeLUT(GREEN, length, subscript, bitsize);
}
void LookupTable::InitializeBlueLUT(unsigned short length,
  unsigned short subscript,
  unsigned short bitsize)
{
  InitializeLUT(BLUE, length, subscript, bitsize);
}

void LookupTable::SetRedLUT(const unsigned char *red, unsigned int length)
{
  SetLUT(RED, red, length);
}

void LookupTable::SetGreenLUT(const unsigned char *green, unsigned int length)
{
  SetLUT(GREEN, green, length);
}

void LookupTable::SetBlueLUT(const unsigned char *blue, unsigned int length)
{
  SetLUT(BLUE, blue, length);
}

namespace {
  using U8 = union {
    uint8_t rgb[4]; // 3rd value = 0
    uint32_t I;
  };

  using U16 = union {
    uint16_t rgb[4]; // 3rd value = 0
    uint64_t I;
  };

#if 0
  // FIXME: see comments below
  struct ltstr8
    {
    bool operator()(U8 u1, U8 u2) const
      {
      return u1.I < u2.I;
      }
    };
  struct ltstr16
    {
    bool operator()(U16 u1, U16 u2) const
      {
      return u1.I < u2.I;
      }
    };
#endif
} // end namespace

inline void printrgb( const unsigned char *rgb )
{
  std::cout << int(rgb[0]) << "," << int(rgb[1]) << "," << int(rgb[2]);
}

void LookupTable::Encode(std::istream &is, std::ostream &os)
{
  if ( BitSample == 8 )
    {
#if 0
    // FIXME:
    // There is a very subtle issue here. We are trying to compress a 8bits RGB image
    // into an 8bits allocated indexed Pixel Data with 8bits LUT... this is just not
    // possible in the general case
    typedef std::set< U8, ltstr8 > RGBColorIndexer;
    RGBColorIndexer s;

    int count = 0;
    while( !is.eof() )
      {
      U8 u;
      u.rgb[3] = 0;
      is.read( (char*)u.rgb, 3);
      gdcm_assert( u.rgb[3] == 0 );
      //gdcm_assert( u.rgb[0] == u.rgb[1] && u.rgb[1] == u.rgb[2] );
      std::pair<RGBColorIndexer::iterator,bool> it = s.insert( u );
      if( it.second ) ++count;
      int d = std::distance(s.begin(), it.first);
      //std::cout << count << " Index: " << d << " -> "; printrgb( u.rgb ); std::cout << "\n";
      //gdcm_assert( s.size() < 256 );
      gdcm_assert( d < s.size() );
      //gdcm_assert( d < 256 && d >= 0 );
      }

    // now generate output image
    // this has two been done in two passes as std::set always re-balance
    is.clear();
    is.seekg( 0 );
    while( !is.eof() )
      {
      U8 u;
      u.rgb[3] = 0;
      is.read( (char*)u.rgb, 3);
      gdcm_assert( u.rgb[3] == 0 );
      //gdcm_assert( u.rgb[0] == u.rgb[1] && u.rgb[1] == u.rgb[2] );
      std::pair<RGBColorIndexer::iterator,bool> it = s.insert( u );
      int d = std::distance(s.begin(), it.first);
      //std::cout << "Index: " << d << " -> "; printrgb( u.rgb ); std::cout << "\n";
      gdcm_assert( d < s.size() );
      //gdcm_assert( d < 256 && d >= 0 );
      os.put( d );
      }

    // now setup the LUT itself:
    unsigned short ncolor = s.size();
    // FIXME: shop'off any RGB that is not at the beginning.
    if( ncolor > 256 ) ncolor = 256;
    InitializeRedLUT(ncolor, 0, 8);
    InitializeGreenLUT(ncolor, 0, 8);
    InitializeBlueLUT(ncolor, 0, 8);
    //int i = Internal->RGB.size();
    //gdcm_assert( Internal->RGB.size() == 5 );
    int idx = 0;
    for( RGBColorIndexer::const_iterator it = s.begin(); it != s.end() && idx < 256; ++it, ++idx )
      {
      gdcm_assert( idx == std::distance( s.begin(), it ) );
      //std::cout << "Index: " << idx << " -> "; printrgb( it->rgb ); std::cout << "\n";
      Internal->RGB[3*idx+RED]   = it->rgb[RED];
      Internal->RGB[3*idx+GREEN] = it->rgb[GREEN];
      Internal->RGB[3*idx+BLUE]  = it->rgb[BLUE];
      }
#else
    while( !is.eof() )
      {
      U8 u;
      u.rgb[3] = 0;
      is.read( (char*)u.rgb, 3);
      gdcm_assert( u.rgb[3] == 0 );
      int d = 0;
      gdcm_assert( d < 256 && d >= 0 );
      os.put( (char)d );
      }
#endif
    }
  else if ( BitSample == 16 )
    {
#if 0
    typedef std::set< U16, ltstr16 > RGBColorIndexer;
    RGBColorIndexer s;

    while( !is.eof() )
      {
      U16 u;
      u.rgb[3] = 0;
      is.read( (char*)u.rgb, 3*2);
      gdcm_assert( u.rgb[3] == 0 );
      //gdcm_assert( u.rgb[0] == u.rgb[1] && u.rgb[1] == u.rgb[2] );
      std::pair<RGBColorIndexer::iterator,bool> it = s.insert( u );
      int d = std::distance(s.begin(), it.first);
      //std::cout << "Index: " << d << " -> "; printrgb( u.rgb ); std::cout << "\n";
      gdcm_assert( d < s.size() );
      gdcm_assert( d < 65536 && d >= 0 );
      }

    // now generate output image
    // this has two been done in two passes as std::set always re-balance
    is.clear();
    is.seekg( 0 );
    while( !is.eof() )
      {
      U16 u;
      u.rgb[3] = 0;
      is.read( (char*)u.rgb, 3*2);
      gdcm_assert( u.rgb[3] == 0 );
      //gdcm_assert( u.rgb[0] == u.rgb[1] && u.rgb[1] == u.rgb[2] );
      std::pair<RGBColorIndexer::iterator,bool> it = s.insert( u );
      unsigned short d = std::distance(s.begin(), it.first);
      //std::cout << "Index: " << d << " -> "; printrgb( u.rgb ); std::cout << "\n";
      gdcm_assert( d < s.size() );
      gdcm_assert( d < 65536 && d >= 0 );
      os.write( (const char*)&d , 2 );
      }

    // now setup the LUT itself:
    unsigned short ncolor = s.size();
    InitializeRedLUT(ncolor, 0, 16);
    InitializeGreenLUT(ncolor, 0, 16);
    InitializeBlueLUT(ncolor, 0, 16);
    //int i = Internal->RGB.size();
    //gdcm_assert( Internal->RGB.size() == 5 );
    int idx = 0;
    uint16_t *rgb16 = (uint16_t*)&Internal->RGB[0];
    for( RGBColorIndexer::const_iterator it = s.begin(); it != s.end(); ++it, ++idx )
      {
      gdcm_assert( idx == std::distance( s.begin(), it ) );
      //std::cout << "Index: " << idx << " -> "; printrgb( it->rgb ); std::cout << "\n";
      rgb16[3*idx+RED]   = it->rgb[RED];
      rgb16[3*idx+GREEN] = it->rgb[GREEN];
      rgb16[3*idx+BLUE]  = it->rgb[BLUE];
      }
#else
    while( !is.eof() )
      {
      U16 u;
      u.rgb[3] = 0;
      is.read( (char*)u.rgb, 3*2);
      gdcm_assert( u.rgb[3] == 0 );
      int d = 0;
      gdcm_assert( d < 65536 && d >= 0 );
      os.write( (const char*)&d , 2 );
      }
#endif
  }
}

void LookupTable::Decode(std::istream &is, std::ostream &os) const
{
  gdcm_assert( Initialized() );
  if ( BitSample == 8 )
    {
    unsigned char idx;
    unsigned char rgb[3];
    while( !is.eof() )
      {
      is.read( (char*)(&idx), 1);
      if( is.eof() || !is.good() ) break;
      if( IncompleteLUT )
        {
        gdcm_assert( idx < Internal->Length[RED] );
        gdcm_assert( idx < Internal->Length[GREEN] );
        gdcm_assert( idx < Internal->Length[BLUE] );
        }
      rgb[RED]   = Internal->RGB[3*idx+RED];
      rgb[GREEN] = Internal->RGB[3*idx+GREEN];
      rgb[BLUE]  = Internal->RGB[3*idx+BLUE];
      os.write((char*)rgb, 3 );
      }
    }
  else if ( BitSample == 16 )
    {
    // gdcmData/NM-PAL-16-PixRep1.dcm
    const uint16_t *rgb16 = (uint16_t*)(void*)Internal->RGB.data();
    while( !is.eof() )
      {
      unsigned short idx;
      unsigned short rgb[3];
      is.read( (char*)(&idx), 2);
      if( is.eof() || !is.good() ) break;
      if( IncompleteLUT )
        {
        gdcm_assert( idx < Internal->Length[RED] );
        gdcm_assert( idx < Internal->Length[GREEN] );
        gdcm_assert( idx < Internal->Length[BLUE] );
        }
      rgb[RED]   = rgb16[3*idx+RED];
      rgb[GREEN] = rgb16[3*idx+GREEN];
      rgb[BLUE]  = rgb16[3*idx+BLUE];
      os.write((char*)rgb, 3*2);
      }
    }
}

bool LookupTable::Decode(char *output, size_t outlen, const char *input, size_t inlen ) const
{
  bool success = false;
  if( outlen < 3 * inlen )
    {
    gdcmDebugMacro( "Out buffer too small" );
    return false;
    }
  if( !Initialized() )
    {
    gdcmDebugMacro( "Not Initialized" );
    return false;
    }
  if ( BitSample == 8 )
    {
    const unsigned char * end = (const unsigned char*)input + inlen;
    unsigned char * rgb = (unsigned char*)output;
    for( const unsigned char * idx = (const unsigned char*)input; idx != end; ++idx )
      {
      if( IncompleteLUT )
        {
        gdcm_assert( *idx < Internal->Length[RED] );
        gdcm_assert( *idx < Internal->Length[GREEN] );
        gdcm_assert( *idx < Internal->Length[BLUE] );
        }
      rgb[RED]   = Internal->RGB[3 * *idx+RED];
      rgb[GREEN] = Internal->RGB[3 * *idx+GREEN];
      rgb[BLUE]  = Internal->RGB[3 * *idx+BLUE];
      rgb += 3;
      }
    success = true;
    }
  else if ( BitSample == 16 )
    {
    const uint16_t *rgb16 = (const uint16_t*)(void*)Internal->RGB.data();
    gdcm_assert( inlen % 2 == 0 );
    const uint16_t * end = (const uint16_t*)(const void*)(input + inlen);
    uint16_t * rgb = (uint16_t*)(void*)output;
    for( const uint16_t * idx = (const uint16_t*)(const void*)input; idx != end; ++idx )
      {
      if( IncompleteLUT )
        {
        gdcm_assert( *idx < Internal->Length[RED] );
        gdcm_assert( *idx < Internal->Length[GREEN] );
        gdcm_assert( *idx < Internal->Length[BLUE] );
        }
      rgb[RED]   = rgb16[3 * *idx+RED];
      rgb[GREEN] = rgb16[3 * *idx+GREEN];
      rgb[BLUE]  = rgb16[3 * *idx+BLUE];
      rgb += 3;
      }
    success = true;
    }
  return success;
}

bool LookupTable::Decode8(char *output, size_t outlen, const char *input, size_t inlen ) const
{
  bool success = false;
  if( outlen < 3 * inlen )
    {
    gdcmDebugMacro( "Out buffer too small" );
    return false;
    }
  if( !Initialized() )
    {
    gdcmDebugMacro( "Not Initialized" );
    return false;
    }
  if ( BitSample == 8 )
    {
    const unsigned char * end = (const unsigned char*)input + inlen;
    unsigned char * rgb = (unsigned char*)output;
    for( const unsigned char * idx = (const unsigned char*)input; idx != end; ++idx )
      {
      if( IncompleteLUT )
        {
        gdcm_assert( *idx < Internal->Length[RED] );
        gdcm_assert( *idx < Internal->Length[GREEN] );
        gdcm_assert( *idx < Internal->Length[BLUE] );
        }
      rgb[RED]   = Internal->RGB[3 * *idx+RED];
      rgb[GREEN] = Internal->RGB[3 * *idx+GREEN];
      rgb[BLUE]  = Internal->RGB[3 * *idx+BLUE];
      rgb += 3;
      }
    success = true;
    }
  else if ( BitSample == 16 )
    {
    const uint16_t *rgb16 = (const uint16_t*)(void*)Internal->RGB.data();
    gdcm_assert( inlen % 2 == 0 );
    const uint16_t * end = (const uint16_t*)(const void*)(input + inlen);
    uint8_t * rgb = (uint8_t*)output;
    for( const uint16_t * idx = (const uint16_t*)(const void*)input; idx != end; ++idx )
      {
      if( IncompleteLUT )
        {
        gdcm_assert( *idx < Internal->Length[RED] );
        gdcm_assert( *idx < Internal->Length[GREEN] );
        gdcm_assert( *idx < Internal->Length[BLUE] );
        }
      rgb[RED]   = rgb16[3 * *idx+RED] >> 8;
      rgb[GREEN] = rgb16[3 * *idx+GREEN] >> 8;
      rgb[BLUE]  = rgb16[3 * *idx+BLUE] >> 8;
      rgb += 3;
      }
    success = true;
    }
  return success;
}

const unsigned char *LookupTable::GetPointer() const
{
  if ( BitSample == 8 )
    {
    return Internal->RGB.data();
    }
  return nullptr;
}

bool LookupTable::GetBufferAsRGBA(unsigned char *rgba) const
{
  bool ret = false;
  if ( BitSample == 8 )
    {
    std::vector<unsigned char>::const_iterator it = Internal->RGB.begin();
    for(; it != Internal->RGB.end() ;)
      {
      // RED
      *rgba++ = *it++;
      // GREEN
      *rgba++ = *it++;
      // BLUE
      *rgba++ = *it++;
      // ALPHA
      *rgba++ = 255;
      }
    ret = true;
    }
  else if ( BitSample == 16 )
    {
/*
    gdcm_assert( Internal->Length[type]*(BitSample/8) == length );
    uint16_t *uchar16 = (uint16_t*)&Internal->RGB[0];
    const uint16_t *array16 = (uint16_t*)array;
    for( unsigned int i = 0; i < Internal->Length[type]; ++i)
      {
      gdcm_assert( 2*i < length );
      gdcm_assert( 2*(3*i+type) < Internal->RGB.size() );
      uchar16[3*i+type] = array16[i];
      std::cout << i << " -> " << array16[i] << "\n";
      }

    ret = true;
*/
    //std::vector<unsigned char>::const_iterator it = Internal->RGB.begin();
    uint16_t *uchar16 = (uint16_t*)(void*)Internal->RGB.data();
    uint16_t *rgba16 = (uint16_t*)(void*)rgba;
    size_t s = Internal->RGB.size();
    s /= 2;
    s /= 3;
    memset(rgba,0,Internal->RGB.size() * 4 / 3); // FIXME
    for(size_t i = 0; i < s; ++i)
      {
      // RED
      *rgba16++ = *uchar16++;
      // GREEN
      *rgba16++ = *uchar16++;
      // BLUE
      *rgba16++ = *uchar16++;
      // ALPHA
      *rgba16++ = 255*255;
      }

    ret = true;
    }
  return ret;
}

bool LookupTable::WriteBufferAsRGBA(const unsigned char *rgba)
{
  bool ret = false;
  if ( BitSample == 8 )
    {
    std::vector<unsigned char>::iterator it = Internal->RGB.begin();
    for(; it != Internal->RGB.end() ;)
      {
      // RED
      *it++ = *rgba++;
      // GREEN
      *it++ = *rgba++;
      // BLUE
      *it++ = *rgba++;
      // ALPHA
      rgba++; // = 255;
      }
    ret = true;
    }
  else if ( BitSample == 16 )
    {
    //gdcm_assert( Internal->Length[type]*(BitSample/8) == length );
    uint16_t *uchar16 = (uint16_t*)(void*)Internal->RGB.data();
    const uint16_t *rgba16 = (const uint16_t*)(const void*)rgba;
    size_t s = Internal->RGB.size();
    s /= 2;
    s /= 3;
    gdcm_assert( s == 65536 );

    for( unsigned int i = 0; i < s /*i < Internal->Length[type]*/; ++i)
      {
      //gdcm_assert( 2*i < length );
      //gdcm_assert( 2*(3*i+type) < Internal->RGB.size() );
      //uchar16[3*i+type] = array16[i];
      //std::cout << i << " -> " << array16[i] << "\n";
      // RED
      *uchar16++ = *rgba16++;
      // GREEN
      *uchar16++ = *rgba16++;
      // BLUE
      *uchar16++ = *rgba16++;
      //
      rgba16++; // = *rgba16++;
      }

    ret = true;
    //ret = false;
    }
  return ret;
}

void LookupTable::Print(std::ostream &os) const
{
  if( BitSample == 16 )
    {
    uint16_t maxlut[3] = { 0 , 0 , 0};
    uint16_t minlut[3] = { 0xffff, 0xffff, 0xffff };
    uint16_t *uchar16 = (uint16_t*)(void*)Internal->RGB.data();
    if( Internal->Length[BLUE] != Internal->Length[RED]
    || Internal->Length[RED] != Internal->Length[GREEN] ) return;
    for( unsigned int i = 0; i < Internal->Length[BLUE]; ++i)
      {
      os << std::dec <<std::setw( 5 ) << std::setfill( '0' ) << i << " : ";
      for(int type = RED; type <= BLUE; ++type )
        {
        gdcm_assert( 2*(3*i+type) < Internal->RGB.size() );
        const uint16_t val = SwapperDoOp::Swap(uchar16[3*i+type]);
        minlut[type] = std::min( minlut[type], val );
        maxlut[type] = std::max( maxlut[type], val );
        os << std::hex << std::setw( 4 ) << std::setfill( '0' ) << val << ",";
        }
      os << std::endl;
      }
    os << "MINLUT: ";
    for(int type = RED; type <= BLUE; ++type )
      os << std::hex << std::setw( 4 ) << std::setfill( '0' ) << minlut[type] << ",";
    os << std::endl;
    os << "MAXLUT: ";
    for(int type = RED; type <= BLUE; ++type )
      os << std::hex << std::setw( 4 ) << std::setfill( '0' ) << maxlut[type] << ",";
    os << std::endl;
    }
}

bool LookupTable::IsRGB8() const
{
  if( BitSample == 16 )
    {
    uint16_t maxlut[3] = { 0 , 0 , 0};
    uint16_t minlut[3] = { 0xffff, 0xffff, 0xffff };
    uint16_t *uchar16 = (uint16_t*)(void*)Internal->RGB.data();
    if( Internal->Length[BLUE] != Internal->Length[RED]
    || Internal->Length[RED] != Internal->Length[GREEN] ) return false;
    for( unsigned int i = 0; i < Internal->Length[BLUE]; ++i)
      {
      for(int type = RED; type <= BLUE; ++type )
        {
        gdcm_assert( 2*(3*i+type) < Internal->RGB.size() );
        const uint16_t val = SwapperDoOp::Swap(uchar16[3*i+type]);
        minlut[type] = std::min( minlut[type], val );
        maxlut[type] = std::max( maxlut[type], val );
        if( maxlut[type] > 0xff ) return false;
        }
      }
    }
  return true;
}

} // end namespace gdcm
