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

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.htm 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 "vtkVVDicomTagPrinter.h"

#include "vtkObjectFactory.h"

#include "gdcmDocument.h"
#include "gdcmSeqEntry.h"
#include "gdcmSQItem.h"
#include "gdcmBinEntry.h"
#include "gdcmValEntry.h" //internal of gdcm

#include "gdcmFileHelper.h"
#include "gdcmDebug.h"
#include "gdcmDirList.h"
#include "gdcmGlobal.h"
#include "gdcmDictSet.h"
#include "gdcmArgMgr.h"
#include "gdcmOrientation.h"
#include <vtksys/SystemTools.hxx>
#include <vtksys/Base64.h>

#include <iostream>

//----------------------------------------------------------------------------
vtkStandardNewMacro( vtkVVDicomTagPrinter );
vtkCxxRevisionMacro(vtkVVDicomTagPrinter, "$Revision: 1.10 $");

//----------------------------------------------------------------------------
vtkVVDicomTagPrinter::vtkVVDicomTagPrinter()
{
  this->FileName = 0;
  this->header = new gdcm::File();
}

//----------------------------------------------------------------------------
vtkVVDicomTagPrinter::~vtkVVDicomTagPrinter()
{
  this->SetFileName(0);
  delete header;
}

//----------------------------------------------------------------------------
void vtkVVDicomTagPrinter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "FileName: " 
    << (this->FileName?this->FileName:"(none)") << endl;
}

int vtkVVDicomTagPrinter::IsFileDicom()
{
  this->header->SetFileName( this->FileName );

  if ( this->header->Load() )
    {
    return this->header->IsReadable() ? 1 : 0 ;
    }
  else
    {
    return 0;
    }
}
 
//----------------------------------------------------------------------------
void vtkVVDicomTagPrinter::PrintTags()
{
 
  this->header->SetFileName( this->FileName );

  this->header->SetMaxSizeLoadEntry( 4096 );

  bool res = this->header->Load();

  if ( !res )
    {
    // Failed to get tags from file
    std::cout << "<DartMeasurement name=\"DicomTags\" type=\"text/string\">Failed to load tags. Please check integrity of DICOM file.</DartMeasurement>\n";
    return;
    }

  gdcm::DocEntry* d = header->GetFirstEntry();

  // Copy of the header content
  while(d)
  {

    bool keyFound = false;
    std::string key, val;
    
    // Because BinEntry is a ValEntry...
    if ( gdcm::BinEntry* b = dynamic_cast<gdcm::BinEntry*>(d) )
      {
      if (b->GetName() != "Pixel Data" && b->GetName() != "unkn")
        {
        if (b->GetValue() == gdcm::GDCM_BINLOADED )
          {
          // base64 streams have to be a multiple of 4 bytes long
          int encodedLengthEstimate = 2 * b->GetLength();
          encodedLengthEstimate = ((encodedLengthEstimate / 4) + 1) * 4;
            
          char *bin = new char[encodedLengthEstimate];
          int encodedLengthActual = vtksysBase64_Encode(
            (const unsigned char *) b->GetBinArea(),
            b->GetLength(),
            (unsigned char *) bin,
            0);
          std::string encodedValue(bin, encodedLengthActual);

          keyFound = true;
          key = b->GetKey();
          val = encodedValue;
          delete []bin;
          }
        }
      }
    else if ( gdcm::ValEntry* v = dynamic_cast<gdcm::ValEntry*>(d) )
      {   
      // Only copying field from the public DICOM dictionary
      if( v->GetName() != "unkn")
        {       
        keyFound = true;
        key = v->GetKey();
        val = v->GetValue();
        }
      }

    // Remove padding characters from the string
    char paddingChar = ' ';
    char *value = const_cast< char * >(val.c_str());
    if (value!=NULL)
      {
      unsigned int realLength = val.length();
      // This code removes extra padding chars at the end of the string. A bug
      // in DART results in strings with \0 to screw up the submissions
      if (realLength > 0)
        {
        size_t i = 0;
        for(i = (size_t)realLength; (i > 0) && (value[i - 1] == paddingChar); i--)
          {
          value[i - 1] = '\0';
          realLength = (unsigned int)i;
          }
        }
      }

    if (key.find("0008|1030") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Study Description\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0008|0015") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Body Part\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0008|0060") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Modality\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0008|0070") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Manufacturer\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0008|0080") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Institution\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0008|1090") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Model\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0018|0022") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Scan options\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0028|0120") != std::string::npos )
      {
      std::cout << "<DartMeasurement name=\"Pixel Padding Value\" type=\"text/string\">" << value << "</DartMeasurement>\n";
      }
    else if (key.find("0018|1120") != std::string::npos )
      {
      vtksys_stl::string dcm_gantry_tilt = header->GetEntryValue(0x0018, 0x1120);
      if (!(dcm_gantry_tilt == gdcm::GDCM_UNFOUND || dcm_gantry_tilt == ""))
        {
        std::cout << "<DartMeasurement name=\"Gantry tilt\" type=\"text/string\">" << value << "</DartMeasurement>\n";
        }
      }

    d = header->GetNextEntry();
  }

  this->PrintPixelType();
  this->PrintImageDimensions();
  this->PrintPatientOrientation();
  this->PrintRescaleSI();
  std::cout << "<DartMeasurement name=\"File Readeable by GDCM\" type=\"numeric/boolean\">" << header->IsReadable() << "</DartMeasurement>\n";

  gdcm::FileHelper *fh = new gdcm::FileHelper(this->header);
  fh->SetPrintLevel( 2 );
  fh->Print();

  delete fh;
  std::cout<<std::flush;
}

//----------------------------------------------------------------------------
void vtkVVDicomTagPrinter::PrintPixelType()
{
  int numComp = this->header->GetNumberOfScalarComponents();
  if (numComp == 1)
    {
    std::cout << "<DartMeasurement name=\"Pixel Type\" type=\"text/string\">Scalar ";
    }
  else
    {
    std::cout << "<DartMeasurement name=\"Pixel Type\" type=\"text/string\">RGB ";
    }
  
  std::string type = this->header->GetPixelType();
  if( type == "8U")
    {
    std::cout << "Unsigned char</DartMeasurement>\n";
    }
  else if( type == "8S")
    {
    std::cout << "Signed Char</DartMeasurement>\n";
    }
  else if( type == "16U")
    {
    std::cout << "Unsigned short</DartMeasurement>\n";
    }
  else if( type == "16S")
    {
    std::cout << "Signed short</DartMeasurement>\n";
    }
  else if( type == "32U")
    {
    std::cout << "Unsigned int</DartMeasurement>\n";
    }
  else if( type == "32S")
    {
    std::cout << "Signed int</DartMeasurement>\n";
    }
  else if ( type == "FD" )
    {
    //64 bits Double image
    std::cout << "Double</DartMeasurement>\n";
    }
  else
    {
    std::cout << "Unknown</DartMeasurement>\n";
    }
}

//----------------------------------------------------------------------------
void vtkVVDicomTagPrinter::PrintImageDimensions()
{
  unsigned int dimensions[3]; 
  double spacing[3];
  dimensions[0] = header->GetXSize();
  dimensions[1] = header->GetYSize();
  dimensions[2] = header->GetZSize();
  spacing[0] = header->GetXSpacing();
  spacing[1] = header->GetYSpacing();
  spacing[2] = header->GetZSpacing();
  ostrstream s;
  s << dimensions[0] << " " << dimensions[1] << " " << dimensions[2] << ends;
  std::cout << "<DartMeasurement name=\"Dimensions\" type=\"text/string\">" 
             << s.str() << "</DartMeasurement>\n";
}

//----------------------------------------------------------------------------
void vtkVVDicomTagPrinter::PrintPatientOrientation()
{
  float imageOrientation[6];
  header->GetImageOrientationPatient(imageOrientation);
  ostrstream s;
  for (int i=0; i< 6; i++)
    {
    s << imageOrientation[i] << " ";
    }
  s << ends;
  std::cout << "<DartMeasurement name=\"Patient Orientation\" type=\"text/string\">" 
             << s.str() << "</DartMeasurement>\n";
}
  
//----------------------------------------------------------------------------
void vtkVVDicomTagPrinter::PrintRescaleSI()
{
  double rs, ri;
  rs = header->GetRescaleSlope();
  ri = header->GetRescaleIntercept();
  cout << "<DartMeasurement name=\"Rescale Slope\" type=\"numeric/double\">" << rs << "</DartMeasurement>\n";
  cout << "<DartMeasurement name=\"Rescale Intercept\" type=\"numeric/double\">" << ri << "</DartMeasurement>\n";
}
