/// NewBvOutputter.cpp
/**
*/

#include <wx/filename.h>

#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <math.h>

#include "StringConvert.h"
#include "DicomFile.h"
#include "SeriesHandler.h"
#include "Converter.h"
#include "NewBvOutputter.h"

using namespace jcs;


///
/**
 */
NewBvOutputter::NewBvOutputter():
OutputterBase(CreateOptions())
{
  mSaveV16 = mOptions.boolOptions["savev16"];
  mSkip = mOptions.intOptions["skip"];
}


///
/**
 */
NewBvOutputter::~NewBvOutputter()
{
  mOptions.boolOptions["savev16"] = mSaveV16;
  mOptions.intOptions["skip"] = mSkip;
}


///
/**
 */
Options
NewBvOutputter::CreateOptions()
{
  Options options = GetBaseOptions();
  options.pathname = "BrainVoyager";
  options.boolOptions["savev16"] = false;
  options.intOptions["skip"] = 0;
  return options;
}


///
/**
 */
void
NewBvOutputter::RemoveSeries(const std::string& seriesUid)
{
  std::set<OutputList::ListType::key_type> keys;

  OutputList::ListType::iterator it = mOutputList.fileNameList.begin();
  OutputList::ListType::iterator it_end = mOutputList.fileNameList.end();
  for (;it != it_end; ++it) {
    if (it->second.seriesUid == seriesUid) {
      keys.insert(it->first);
    }
  }

  std::set<OutputList::ListType::key_type>::iterator key_it = keys.begin();
  std::set<OutputList::ListType::key_type>::iterator key_it_end = keys.end();
  for (;key_it != key_it_end; ++key_it) {
    mOutputList.fileNameList.erase(*key_it);
  }
}


///
/**
    \param handler A pointer to a SeriesHandler.
    \return 1
*/
int
NewBvOutputter::ConvertSeries(SeriesHandler* handler)
{
  int bits_allocated, pixel_rep = 0;
  handler->Find("BitsAllocated", bits_allocated);
  handler->Find("PixelRepresentation", pixel_rep);

  // Note that mosaic == functional is assumed
  if (handler->IsMosaic()) {
    int nVolumes = handler->GetNumberOfVolumes();

    mConvertFmr(handler);

    switch (bits_allocated + pixel_rep) {
    case 8 : mConvertStc<wxUint8>(handler);
      break;
    case 9 : mConvertStc<wxInt8>(handler);
      break;
    case 16 : mConvertStc<wxUint16>(handler);
      break;
    case 17 : mConvertStc<wxInt16>(handler);
      break;
    case 32 : mConvertStc<wxUint32>(handler);
      break;
    case 33 : mConvertStc<wxInt32>(handler);
      break;
      default : mConvertStc<wxUint16>(handler);
    }
  }
  else {

    switch (bits_allocated + pixel_rep) {
    case 8 : mConvertAnat<wxUint8>(handler);
      break;
    case 9 : mConvertAnat<wxInt8>(handler);
      break;
    case 16 : mConvertAnat<wxUint16>(handler);
      break;
    case 17 : mConvertAnat<wxInt16>(handler);
      break;
    case 32 : mConvertAnat<wxUint32>(handler);
      break;
    case 33 : mConvertAnat<wxInt32>(handler);
      break;
      default : mConvertAnat<wxUint16>(handler);
    }
  }

  return 1;
}



///
/**
 */
void
NewBvOutputter::mConvertFmr(SeriesHandler* handler)
{
  wxFileName file = GetFileName(handler->GetSeriesUid());
  wxFileName::Mkdir(file.GetPath(wxPATH_GET_VOLUME), 0777, wxPATH_MKDIR_FULL);
  file.SetExt(_T(""));
  std::string fname(static_cast<const char*>(file.GetFullPath()));  //.mb_str(wxConvLocal)));

  FmrHeader header;
  header.volumes = handler->GetNumberOfVolumes() - mSkip;
  header.slices = handler->GetNumberOfSlices();
  header.skipVolumes = mSkip;
  header.prefix = fname + "_";
  handler->Find("RepetitionTime", header.tr);

  header.interSliceT = header.tr/header.slices;

  header.columns = handler->GetColumns();
  header.rows = handler->GetRows();
  std::vector<double> res = handler->GetVoxelSize();
  header.IpResX = static_cast<float>(res[0]);
  header.IpResY = static_cast<float>(res[1]);

  int layout = static_cast<int>(sqrtf(header.slices));
  if ((layout * layout) < header.slices) {
    ++layout;
  }
  header.layoutColumns = layout;
  header.layoutRows = layout;

  handler->Find("SliceThickness", header.sliceThickness);
  header.sliceGap = res[2] - header.sliceThickness;
  if (header.sliceGap < 0) {
    header.sliceGap = 0.0;
  }

  BvFmr fmr_file(fname);
  fmr_file.WriteFmr(header);

}


///
/**
 */
void
NewBvOutputter::UpdateOutputForSeries(SeriesHandler* handler)
{
  std::string series_uid(handler->GetSeriesUid());
  RemoveSeries(series_uid);
  
  ImageFileName name;
  name.seriesUid = series_uid;
  name.SetPrefix(GenerateDefaultPrefix(handler));

  // fill in default dirs
  FillInDefaultDirs(name, handler);

  std::string output_file_uid = series_uid;

  //Currently we assume that mosaics == BOLD images....
  if (handler->IsMosaic()) {

    name.SetExt("fmr");
    mOutputList.fileNameList.insert(make_pair(output_file_uid, name));
    name.SetExt("stc");
    int n_slices = handler->GetNumberOfSlices();
    int ndigits = itos(n_slices).size();
    for (int i = 1; i <= n_slices; ++i) {
      std::string postfix = "_";
      postfix.append(itos(i, ndigits));
      name.SetPostfix((postfix));
      mOutputList.fileNameList.insert(make_pair(output_file_uid + postfix, name));
    }
  }
  else {
    name.SetExt("vmr");
    mOutputList.fileNameList.insert(make_pair(output_file_uid, name));
    if (mSaveV16) {
      name.SetExt("v16");
      mOutputList.fileNameList.insert(make_pair(output_file_uid, name));
    }
  }
}


///
/**
 */
void
NewBvOutputter::SetOption(const std::string& name, int value)
{
  OutputterBase::SetOption(name, value);
  if (name.find("skip") != std::string::npos) {
    mSkip = value;
  }
}


///
/**
 */
void
NewBvOutputter::SetOption(const std::string& name, bool value)
{
  OutputterBase::SetOption(name, value);
  if (name.find("v16") != std::string::npos) {
    mSaveV16 = value;
  }
}
