/// Converter.cpp
/**
*/

#include <wx/filename.h>

#include <string>
#include <sstream>
#include <iostream>
#include <algorithm>

#include "StringConvert.h"
#include "DicomFile.h"
#include "SeriesHandler.h"
#include "HandlerFactory.h"
#include "OutputFactory.h"
#include "Converter.h"
#include "OutputterBase.h"

using namespace jcs;

///
/**
*/
template <class T>
struct GetFirst : std::unary_function<T, typename T::first_type> {
  typename T::first_type operator()(const T& t) const {
    return t.first;
  }
};


///
/**
*/
static void
DeleteHandler (std::pair<std::string, SeriesHandler*> mPair)
{
  delete mPair.second;
}


///
/**
*/
Converter::Converter(int type)
{
  mpOutputter = OutputFactory::CreateNewOutputter(type);
}


///
/**
*/
Converter::~Converter()
{
  HandlerMap::iterator it = mHandlerMap.begin();
  HandlerMap::iterator it_end = mHandlerMap.end();
  for (;it != it_end; ++it) {
    delete it->second;
  }
  delete mpOutputter;
}


///
/**
*/
void
Converter::SetOutput(int type)
{
  delete mpOutputter;
  mpOutputter = OutputFactory::CreateNewOutputter(type);
  UpdateAllOutput();
}


///
/** Adds a file to the list of files to be processed.
    \param path Path to file to add.
    \param match String to match within a file's SeriesDescription.
    \return 1.
*/
int
Converter::AddFile(const wxString& path, const wxString& match)
{
  Message fileMessage;
  wxString message;

  if (!mAddFile(path, match, message)) {
    fileMessage.push_back(path);
    fileMessage.push_back(_T("Not added"));
    fileMessage.push_back(message);
  }

  if (fileMessage.size() > 0) {
    messages.push_back(fileMessage);
  }
  return 1;
}


///
/** Adds a file to the set to be processed. Creates a handler for each series.
    \param path Path to file.
    \param match String to match within a file's SeriesDescription.
    \param message Holds messages about this process.
    \return True if file successfully added, false otherwise.
*/
bool
Converter::mAddFile(const wxString& path, const wxString& match, wxString& message)
{
  wxString name;
  wxString ext;
  wxFileName::SplitPath(path, NULL, &name, &ext);
  if (name == _T("DICOMDIR")) {
    message = _T("We don't convert DICOMDIR files.");
    return false;
  }

//  std::cout << "Converter::mAddFile, path: " << path << std::endl;

  DicomFile input(path);
  if (!input.IsOk()) {
    message = wxString(input.ErrorMessage(), wxConvLocal);
    return false;
  }

  if (!match.empty()) {
    std::string series_description;
    input.Find("SeriesDescription", series_description);
    if (series_description.find(match.mb_str(wxConvLocal)) == std::string::npos) {
      message = _T("Series description doesn't match ");
      message.append(match);
      return false;
    }
  }

  std::string series_uid;
  //input.Find("SeriesInstanceUid", series_uid);
  input.Find("SeriesInstanceUID", series_uid);

  // Fix problems with termination
  std::string temp(series_uid.c_str());
  series_uid.clear();
  series_uid = temp;

  mSeriesToUpdate.insert(series_uid);
  
  if (!mHandlerMap.count(series_uid)) {
    //mHandlerMap[series_uid] = HandlerFactory::CreateHandler(path.mb_str(wxConvLocal));
    mHandlerMap[series_uid] = HandlerFactory::CreateHandler(path);
  }

  //if (!mHandlerMap[series_uid]->AddFile(path.mb_str(wxConvLocal))) {
  if (!mHandlerMap[series_uid]->AddFile(path)) {
    message = _T("File already in list.");
    return false;
  }

  return true;
}


///
/**
*/
void
Converter::UpdateOutput()
{
  std::set<std::string>::iterator it = mSeriesToUpdate.begin();
  std::set<std::string>::iterator it_end = mSeriesToUpdate.end();
  for (; it != it_end; ++it) {
    mpOutputter->UpdateOutputForSeries(mHandlerMap[*it]);
  }
  mSeriesToUpdate.clear();
}


///
/**
*/
void
Converter::UpdateAllOutput()
{
  HandlerMap::iterator it = mHandlerMap.begin();
  HandlerMap::iterator it_end = mHandlerMap.end();
  for (; it != it_end; ++it) {
    mpOutputter->UpdateOutputForSeries(it->second);
  }
}


///
/**
*/
void
Converter::UpdateOutput(const std::string& seriesUid)
{
  mpOutputter->UpdateOutputForSeries(mHandlerMap[seriesUid]); 
}


///
/**
*/
void 
Converter::RemoveSeries(const std::string& seriesUid)
{
  delete mHandlerMap[seriesUid];
  mHandlerMap.erase(seriesUid);
  mpOutputter->RemoveSeries(seriesUid);
}


///
/**
*/
const std::vector<std::string> 
Converter::GetStringInfo(const std::string& s) const
{
  return (*mHandlerMap.find(s)).second->GetStringInfo();
}


///
/** Returns list of series names (SeriesUID)
    \return An immutable vector of strings.
*/
const std::vector<std::string>
Converter::GetSeriesList()
{
  HandlerMap::iterator it = mHandlerMap.begin();
  HandlerMap::iterator it_end = mHandlerMap.end();
  std::vector<std::string> v;
  for (; it != it_end; ++it) {
    v.push_back(it->first);
  }
  return v;
}


///
/**
*/
void
Converter::ConvertAll()
{
  mCancel = false;

  HandlerMap::iterator it = mHandlerMap.begin();
  HandlerMap::iterator it_end = mHandlerMap.end();
  for (; it != it_end; ++it) {
    if (mCancel) {
      break;
    }
    mpOutputter->ConvertSeries(it->second);
  }
}
