/// NewSpmOutputter.cpp
/**
*/

#include <string>
#include <vector>

#include "SeriesHandler.h"
#include "NewSpmVolume.h"
#include "NewSpmOutputter.h"

using namespace jcs;


///
/**
*/
NewSpmOutputter::NewSpmOutputter() : 
Basic3DOutputter(CreateOptions())
{

}


///
/**
*/
Options
NewSpmOutputter::CreateOptions()
{
  Options options = Get3DOptions();
  options.pathname = "SpmAnalyze";
  return options;
}


///
/**
*/
BasicVolumeFormat* 
NewSpmOutputter::GetOutputVolume(const char* file)
{
  return new NewSpmVolume(file, headerExtension.mb_str(wxConvLocal), rawExtension.mb_str(wxConvLocal));
}


///
/**
*/
int
NewSpmOutputter::ConvertSeries(SeriesHandler* handler)
{
  
  int bits_allocated, pixel_rep = 0;
  handler->Find("BitsAllocated", bits_allocated);
  handler->Find("PixelRepresentation", pixel_rep);

  switch (bits_allocated + pixel_rep) {

  case 8 : {
      SpmConversion<wxUint8> conversion(this, handler);
      conversion.Convert();
      break;
    }
  case 9 : {
      SpmConversion<wxInt8> conversion(this, handler);
      conversion.Convert();
      break;
    }
  case 16 : {
      SpmConversion<wxUint16> conversion(this, handler);
      conversion.Convert();
      break;
    }
  case 17 : {
      SpmConversion<wxInt16> conversion(this, handler);
      conversion.Convert();
      break;
    }
  case 32 : {
      SpmConversion<wxUint32> conversion(this, handler);
      conversion.Convert();
      break;
    }
  case 33 : {
      SpmConversion<wxInt32> conversion(this, handler);
      conversion.Convert();
      break;
    }
  default : {
      wxLogMessage(_("BitsAllocated and PixelRepresentation values (%d, %d) not supported."), bits_allocated, pixel_rep);
    }
  }

  return 1;
}


///
/**
*/
template <class T>
SpmConversion<T>::SpmConversion(Basic3DOutputter* outputter, SeriesHandler* handler)
: Basic3DConversion<T>(outputter, handler)
{
  mHeader = new NewSpmHeader();
}


///
/**
*/
template <class T>
SpmConversion<T>::~SpmConversion()
{
  delete mHeader;
}


///
/**
*/
template <class T> void
SpmConversion<T>::ProcessSlice(std::vector<T>& slice)
{
  std::reverse(slice.begin(), slice.end());
}


///
/**
*/
template <class T> void
SpmConversion<T>::GetHeaderForSeries()
{
  mHeader->InitHeader();
  std::string s;

  this->mHandler->Find("SeriesDate", s);
  s.copy(mHeader->hist.exp_date, sizeof(mHeader->hist.exp_date));
  
  this->mHandler->Find("SeriesTime", s);
  s.copy(mHeader->hist.exp_time, sizeof(mHeader->hist.exp_time));
  
  this->mHandler->Find("SeriesDescription", s);
  s.copy(mHeader->hist.descrip, sizeof(mHeader->hist.descrip));

  this->mHandler->Find("PatientName", s);
  s.copy(mHeader->hist.patient_id, sizeof(mHeader->hist.patient_id));

  float fvar;
  this->mHandler->Find("RepetitionTime", fvar);
  mHeader->dime.pixdim[4] = fvar;

  mHeader->dime.dim[1] = this->mHandler->GetColumns();
  mHeader->dime.dim[2] = this->mHandler->GetRows();
  mHeader->dime.dim[3] = this->mHandler->GetNumberOfSlices();
  // dim[3] recalculated in mConvert

  std::vector<double>voxel_size = this->mHandler->GetVoxelSize();
  mHeader->dime.pixdim[1] = voxel_size[0];
  mHeader->dime.pixdim[2] = voxel_size[1];
  mHeader->dime.pixdim[3] = voxel_size[2];
  // pixdim[3] recalculated in mConvert

  this->mHandler->Find("PhotometricInterpretation", s);
  if (s == "RGB") {
    mHeader->dime.datatype = 128; // DT_RGB
  }
  else {

    this->mHandler->Find("BitsAllocated", mHeader->dime.bitpix);

    // Standard Analyze data types don't include
    // either unsigned 16 bit or signed 8 bit
    switch (mHeader->dime.bitpix) {
    case 16 : 
      mHeader->dime.datatype = 4;	// DT_SIGNED_SHORT 
      break;

    case 8 :
      mHeader->dime.datatype = 8;  // DT_UNSIGNED_CHAR
      break;

      default :
      mHeader->dime.datatype = 0;  // DT_UNKNOWN
    }
  }

  // Write/calculate remaining fields
  mHeader->hk.sizeof_hdr = 348;
  mHeader->dime.dim[0] = 4;
  mHeader->dime.dim[4] = 1; // one volume per file by default

  int dimensionality = this->mOutputter->GetDimensionality(this->mHandler->GetSeriesUid());
  if (dimensionality == 4) {
    mHeader->dime.dim[4] = this->GetNumberOfVolumes();
  }

  mHeader->hk.regular = 'r'; // all volumes same size
  mHeader->dime.vox_offset = 0;
  mHeader->hist.origin[0] = mHeader->dime.dim[1]/2;
  mHeader->hist.origin[1] = mHeader->dime.dim[2]/2;
  mHeader->hist.origin[2] = mHeader->dime.dim[3]/2;
}


///
/**
    \param volPair A volume identifier and a volume.
*/
template <class T> void
SpmConversion<T>::CompleteHeaderForVolume(std::pair<VolId, Volume<T> > volPair)
{
  // Calculate number of slices for this volume
  int n_slices = volPair.second.size();
  mHeader->SetNumberOfSlices(n_slices);
  
  // Calculate voxel size in Z from slice spacing for multi-slice files
  if (n_slices > 1) {
    mHeader->SetSliceSpacing(volPair.second.GetSpacing());
  }

  double slope, intercept;
  if (this->mOutputter->rescale == false &&
      this->mHandler->GetRescaleSlopeAndIntercept(volPair.first, slope, intercept)) {
    mHeader->dime.scale = slope;
    mHeader->dime.intercept = intercept;
  }
  else {
    mHeader->dime.scale = 1;
    mHeader->dime.intercept = 0;
  }
}
