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

  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 "gdcmTransferSyntax.h"
#include "gdcmTrace.h"

#include <cassert>
#include <cstring>

#include <string>
#include <iostream>

namespace gdcm
{

//#include "gdcmUIDs.cxx"

static const char *TSStrings[] = {
    // Implicit VR Little Endian
  "1.2.840.10008.1.2",
  // Implicit VR Big Endian DLX (G.E Private)
  "1.2.840.113619.5.2",
  // Explicit VR Little Endian
  "1.2.840.10008.1.2.1",
  // Deflated Explicit VR Little Endian
  "1.2.840.10008.1.2.1.99",
  // Explicit VR Big Endian
  "1.2.840.10008.1.2.2",
  // JPEG Baseline (Process 1)
  "1.2.840.10008.1.2.4.50",
  // JPEG Extended (Process 2 & 4)
  "1.2.840.10008.1.2.4.51",
  // JPEG Extended (Process 3 & 5)
  "1.2.840.10008.1.2.4.52",
  // JPEG Spectral Selection, Non-Hierarchical (Process 6 & 8)
  "1.2.840.10008.1.2.4.53",
  // JPEG Full Progression, Non-Hierarchical (Process 10 & 12)
  "1.2.840.10008.1.2.4.55",
  // JPEG Lossless, Non-Hierarchical (Process 14)
  "1.2.840.10008.1.2.4.57",
  // JPEG Lossless, Non-Hierarchical, First-Order Prediction (Process 14,
  //                                                       [Selection Value 1])
  "1.2.840.10008.1.2.4.70",
  // JPEG-LS Lossless Image Compression
  "1.2.840.10008.1.2.4.80",
  // JPEG-LS Lossy (Near-Lossless) Image Compression
  "1.2.840.10008.1.2.4.81",
  // JPEG 2000 Lossless
  "1.2.840.10008.1.2.4.90",
  // JPEG 2000
  "1.2.840.10008.1.2.4.91",
  // JPEG 2000 Part 2 Lossless
  "1.2.840.10008.1.2.4.92",
  // JPEG 2000 Part 2
  "1.2.840.10008.1.2.4.93",
  // RLE Lossless
  "1.2.840.10008.1.2.5",
  // MPEG2 Main Profile @ Main Level
  "1.2.840.10008.1.2.4.100",
  // Old ACR NEMA, fake a TS
  "ImplicitVRBigEndianACRNEMA",
  // Weird Papyrus
  "1.2.840.10008.1.20",
  // CT_private_ELE,
  "1.3.46.670589.33.1.4.1",
  // JPIP Referenced
  "1.2.840.10008.1.2.4.94",
  // MPEG2 Main Profile @ High Level
  "1.2.840.10008.1.2.4.101",
  // MPEG-4 AVC/H.264 High Profile / Level 4.1
  "1.2.840.10008.1.2.4.102",
  // MPEG-4 AVC/H.264 BD-compatible High Profile / Level 4.1
  "1.2.840.10008.1.2.4.103",
  // Unknown
  "Unknown Transfer Syntax", // Pretty sure we never use this case...
  nullptr // Compilers have no obligation to finish by NULL, do it ourself
};

TransferSyntax::TSType TransferSyntax::GetTSType(const char *cstr)
{
  // trim trailing whitespace
  std::string str = cstr;
  std::string::size_type notspace = str.find_last_not_of(' ') + 1;
  if( notspace != str.size() )
    {
    gdcmDebugMacro( "BUGGY HEADER: TS contains " <<
      str.size()-notspace << " whitespace character(s)" );
    str.erase(notspace);
    }

  int i = 0;
  while(TSStrings[i] != nullptr)
  //while(TransferSyntaxStrings[i] != 0)
    {
    if( str == TSStrings[i] )
    //if( str == TransferSyntaxStrings[i] )
      return (TSType)i;
    ++i;
    }
  return TS_END;
}

const char* TransferSyntax::GetTSString(TSType ts)
{
  assert( ts <= TS_END );
  return TSStrings[(int)ts];
  //return TransferSyntaxStrings[(int)ts];
}

bool TransferSyntax::IsImplicit(TSType ts) const
{
  assert( ts != TS_END );
  return ts == ImplicitVRLittleEndian
    || ts == ImplicitVRBigEndianACRNEMA
    || ts == ImplicitVRBigEndianPrivateGE
#ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION
    || ts == WeirdPapryus
#endif
    ;
}

bool TransferSyntax::IsImplicit() const
{
  if ( TSField == TS_END ) return false;
  return TSField == ImplicitVRLittleEndian
    || TSField == ImplicitVRBigEndianACRNEMA
    || TSField == ImplicitVRBigEndianPrivateGE
#ifdef GDCM_SUPPORT_BROKEN_IMPLEMENTATION
    || TSField == WeirdPapryus
#endif
    ;
}

bool TransferSyntax::IsExplicit() const
{
  if ( TSField == TS_END ) return false; // important !
  return !IsImplicit();
}

bool TransferSyntax::IsLossy() const
{
  if (
    TSField == JPEGBaselineProcess1 ||
    TSField == JPEGExtendedProcess2_4 ||
    TSField == JPEGExtendedProcess3_5 ||
    TSField == JPEGSpectralSelectionProcess6_8 ||
    TSField == JPEGFullProgressionProcess10_12 ||
    TSField == JPEGLSNearLossless ||
    TSField == JPEG2000 ||
    TSField == JPEG2000Part2 ||
    TSField == JPIPReferenced ||
    TSField == MPEG2MainProfile ||
    TSField == MPEG2MainProfileHighLevel ||
    TSField == MPEG4AVCH264HighProfileLevel4_1 ||
    TSField == MPEG4AVCH264BDcompatibleHighProfileLevel4_1
  )
    {
    return true;
    }
  return false;

}

// This function really test the kind of compression algorithm and the matching
// transfer syntax.  If you use the JPEG compression algorithm (ITU-T T.81,
// ISO/IEC IS 10918-1), You will not be able to declare a lossy compress pixel
// data using JPEGLosslessProcess14_1 For the same reason using J2K (ITU-T
// T.800, ISO/IEC IS 15444-1), you should not be allowed to stored an
// irreversible wavelet compressed pixel data in a file declared with transfer
// syntax JPEG2000Lossless.
// Same goes for JPEG-LS (ITU-T T.87, ISO/IEC IS 14495-1), and to some extent
// RLE which does not even allow lossy compression...
bool TransferSyntax::CanStoreLossy() const
{
  if (
    TSField == JPEGLosslessProcess14 ||
    TSField == JPEGLosslessProcess14_1 ||
    TSField == JPEGLSLossless ||
    TSField == JPEG2000Lossless ||
    TSField == JPEG2000Part2Lossless ||
    TSField == RLELossless
  )
    {
    return false;
    }
  return true;
}

bool TransferSyntax::IsLossless() const
{
  if (
    TSField == JPEGBaselineProcess1 ||
    TSField == JPEGExtendedProcess2_4 ||
    TSField == JPEGExtendedProcess3_5 ||
    TSField == JPEGSpectralSelectionProcess6_8 ||
    TSField == JPEGFullProgressionProcess10_12 ||
    // TSField == JPEGLSNearLossless || -> can be lossy & lossless
    // TSField == JPEG2000 || -> can be lossy & lossless
    // TSField == JPEG2000Part2 || -> can be lossy & lossless
    // TSField == JPIPReferenced || -> can be lossy & lossless
    TSField == MPEG2MainProfile ||
    TSField == MPEG2MainProfileHighLevel ||
    TSField == MPEG4AVCH264HighProfileLevel4_1 ||
    TSField == MPEG4AVCH264BDcompatibleHighProfileLevel4_1
  )
    {
    return false;
    }
  return true;
}

// By implementation those two functions form a partition
bool TransferSyntax::IsExplicit(TSType ts) const
{
  assert( ts != TS_END );
  return !IsImplicit(ts);
}

TransferSyntax::NegociatedType TransferSyntax::GetNegociatedType() const
{
  if( TSField == TS_END )
    {
    return TransferSyntax::Unknown;
    }
  else if( IsImplicit(TSField) )
    {
    return TransferSyntax::Implicit;
    }
  return TransferSyntax::Explicit;
}

bool TransferSyntax::IsLittleEndian(TSType ts) const
{
  assert( ts != TS_END );
  return !IsBigEndian(ts);
}

bool TransferSyntax::IsBigEndian(TSType ts) const
{
  assert( ts != TS_END );
  return ts == ExplicitVRBigEndian
//    || ts == ImplicitVRBigEndianPrivateGE // Indeed this is LittleEndian
    || ts == ImplicitVRBigEndianACRNEMA;
}

SwapCode TransferSyntax::GetSwapCode() const
{
  assert( TSField != TS_END );
  if( IsBigEndian( TSField ) )
    {
    return SwapCode::BigEndian;
    }
  assert( IsLittleEndian( TSField ) );
  return SwapCode::LittleEndian;
}

bool TransferSyntax::IsEncoded() const
{
  return TSField == DeflatedExplicitVRLittleEndian;
}

bool TransferSyntax::IsEncapsulated() const
{
  bool ret = false;
  switch( TSField )
    {
  //case ImplicitVRLittleEndian:
  //case ImplicitVRBigEndianPrivateGE:
  //case ExplicitVRLittleEndian:
  //case DeflatedExplicitVRLittleEndian:
  //case ExplicitVRBigEndian:
  case JPEGBaselineProcess1:
  case JPEGExtendedProcess2_4:
  case JPEGExtendedProcess3_5:
  case JPEGSpectralSelectionProcess6_8:
  case JPEGFullProgressionProcess10_12:
  case JPEGLosslessProcess14:
  case JPEGLosslessProcess14_1:
  case JPEGLSLossless:
  case JPEGLSNearLossless:
  case JPEG2000Lossless:
  case JPEG2000:
  case JPEG2000Part2Lossless:
  case JPEG2000Part2:
  case JPIPReferenced:
  case RLELossless:
  case MPEG2MainProfile:
  case MPEG2MainProfileHighLevel:
  case MPEG4AVCH264HighProfileLevel4_1:
  case MPEG4AVCH264BDcompatibleHighProfileLevel4_1:
  //case ImplicitVRBigEndianACRNEMA:
  //case WeirdPapryus:
    ret = true;
    break;
  default:
    ;
    }
  return ret;
}

} // end namespace gdcm
