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

  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 "gdcmKAKADUCodec.h"
#include "gdcmTransferSyntax.h"
#include "gdcmDataElement.h"
#include "gdcmFilename.h"
#include "gdcmSystem.h"
#include "gdcmSequenceOfFragments.h"
#include "gdcmPNMCodec.h"
#include "gdcmByteSwap.txx"

namespace gdcm
{
/*
*/

KAKADUCodec::KAKADUCodec()
{
  //NeedByteSwap = true;
}

KAKADUCodec::~KAKADUCodec()
= default;

bool KAKADUCodec::CanDecode(TransferSyntax const &ts) const
{
#ifndef GDCM_USE_KAKADU
  (void)ts;
  return false;
#else
  return ts == TransferSyntax::JPEG2000Lossless
      || ts == TransferSyntax::JPEG2000;
#endif
}

bool KAKADUCodec::CanCode(TransferSyntax const &) const
{
  return false;
}

/* KAKADU command line is a bit tricky to use:
 *
 * kdu_expand
 */
bool KAKADUCodec::Decode(DataElement const &in, DataElement &out)
{
#ifndef GDCM_USE_KAKADU
  (void)in;
  (void)out;
  return false;
#else
  // First thing creates a j2k file from the fragment:
  const SequenceOfFragments *sf = in.GetSequenceOfFragments();
  if(!sf) return false;

  if( NumberOfDimensions == 2 )
    {
    // http://msdn.microsoft.com/en-us/library/hs3e7355.aspx
    // -> check if tempnam needs the 'free'
    char *tempinput  = tempnam(0, "gdcminkduexp");
    char *tempoutput = tempnam(0, "gdcmoutkduexp");
    if( !tempinput || !tempoutput )
      {
      //free(input);
      //free(output);
      return false;
      }
    std::string input = tempinput;
    input += ".j2k";
    std::string output = tempoutput;
    output += ".rawl";

    std::ofstream outfile(input.c_str(), std::ios::binary);
    sf->WriteBuffer(outfile);
    outfile.close(); // flush !

    //Filename fn( System::GetCurrentProcessFileName() );
    //std::string executable_path = fn.GetPath();
#ifdef GDCM_USE_SYSTEM_KAKADU
    std::string kakadu_command = GDCM_KAKADU_EXPAND_EXECUTABLE;
    kakadu_command += " -quiet";
#else
#error not implemented
#endif
    // ./bin/kakadujpeg -d -s jpeg.jpg -ci 0 out.raw
    kakadu_command += " -i ";
    kakadu_command += input;
    kakadu_command += " -o ";
    kakadu_command += output;

    //std::cerr << kakadu_command << std::endl;
    gdcmDebugMacro( kakadu_command );
    int ret = system(kakadu_command.c_str());
    //std::cerr << "system: " << ret << std::endl;

    size_t len = System::FileSize(output.c_str());
    if(!len) return false;

    std::ifstream is(output.c_str(), std::ios::binary);
    char * buf = new char[len];
    is.read(buf, len);
    out.SetTag( Tag(0x7fe0,0x0010) );
    out.SetByteValue( buf, len );
    delete[] buf;

    if( !System::RemoveFile(input.c_str()) )
      {
      gdcmErrorMacro( "Could not delete input: " << input );
      }

    if( !System::RemoveFile(output.c_str()) )
      {
      gdcmErrorMacro( "Could not delete output: " << output );
      }

    free(tempinput);
    free(tempoutput);
    }
  else if ( NumberOfDimensions == 3 )
    {
    std::stringstream os;
    if( sf->GetNumberOfFragments() != Dimensions[2] )
      {
      gdcmErrorMacro( "Not handled" );
      return false;
      }

    for(unsigned int i = 0; i < sf->GetNumberOfFragments(); ++i)
      {
      // http://msdn.microsoft.com/en-us/library/hs3e7355.aspx
      // -> check if tempnam needs the 'free'
      char *tempinput  = tempnam(0, "gdcminkduexp");
      char *tempoutput = tempnam(0, "gdcmoutkduexp");
      if( !tempinput || !tempoutput )
        {
        //free(input);
        //free(output);
        return false;
        }
      std::string input = tempinput;
      input += ".j2k";
      std::string output = tempoutput;
      output += ".rawl";

      std::ofstream outfile(input.c_str(), std::ios::binary);
      const Fragment &frag = sf->GetFragment(i);
      gdcm_assert( !frag.IsEmpty() );
      const ByteValue *bv = frag.GetByteValue();
      gdcm_assert( bv );
      //sf->WriteBuffer(outfile);
      bv->WriteBuffer( outfile );
      outfile.close(); // flush !

      //Filename fn( System::GetCurrentProcessFileName() );
      //std::string executable_path = fn.GetPath();
#ifdef GDCM_USE_SYSTEM_KAKADU
      std::string kakadu_command = GDCM_KAKADU_EXPAND_EXECUTABLE;
      kakadu_command += " -quiet";
#else
#error not implemented
#endif
      // ./bin/kakadujpeg -d -s jpeg.jpg -ci 0 out.raw
      kakadu_command += " -i ";
      kakadu_command += input;
      kakadu_command += " -o ";
      kakadu_command += output;

      //std::cerr << kakadu_command << std::endl;
      gdcmDebugMacro( kakadu_command );
      int ret = system(kakadu_command.c_str());
      //std::cerr << "system: " << ret << std::endl;

      size_t len = System::FileSize(output.c_str());
      if(!len) return false;

      std::ifstream is(output.c_str(), std::ios::binary);
      char * buf = new char[len];
      is.read(buf, len);
      os.write(buf, len);
      //out.SetByteValue( buf, len );
      delete[] buf;

      if( !System::RemoveFile(input.c_str()) )
        {
        gdcmErrorMacro( "Could not delete input: " << input );
        }
      if( !System::RemoveFile(output.c_str()) )
        {
        gdcmErrorMacro( "Could not delete output: " << output );
        }
      free(tempinput);
      free(tempoutput);
      }
    std::string str = os.str();
    gdcm_assert( str.size() );
    out.SetTag( Tag(0x7fe0,0x0010) );
    out.SetByteValue( &str[0], str.size() );
    }
  else
    {
    gdcmErrorMacro( "Not handled" );
    return false;
    }

  // FIXME:
  //LossyFlag = true;

  //return ImageCodec::Decode(in,out);
  return true;
#endif
}

// Compress into JPEG
bool KAKADUCodec::Code(DataElement const &in, DataElement &out)
{
#ifndef GDCM_USE_KAKADU
  (void)in;
  (void)out;
  return false;
#else
  // That would be neat, please contribute :)
  return false;
#endif
}

ImageCodec * KAKADUCodec::Clone() const
{
  return nullptr;
}

} // end namespace gdcm
