/*
*  
*  $Id: exporttodicomdircommand.cpp $
*  Ginkgo CADx Project
*
*  Copyright 2008-14 MetaEmotion S.L. All rights reserved.
*  http://ginkgo-cadx.com
*
*  This file is licensed under LGPL v3 license.
*  See License.txt for details
*
*/
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4996)
#endif


#include <dcmtk/dcmdata/dcdicdir.h>
#include <dcmtk/dcmdata/dcdeftag.h>

#include <sstream>

#include <wx/filename.h>
#include <wx/file.h>
#include <wx/dir.h>
#include <main/controllers/configurationcontroller.h>
#include <main/controllers/historycontroller.h>
#include <main/controllers/dicommanager.h>
#include <main/controllers/controladorlog.h>
#include <main/entorno.h>

#include <api/globals.h>
#include <api/internationalization/internationalization.h>
#include <api/dicom/idicommanager.h>

#include <main/entorno.h>
#include <main/controllers/controladoreventos.h>

#include <eventos/mensajes.h>

#include "exporttodicomdircommand.h"


#define IDC_EXPORT_TO_DICOMDIR           118

GADAPI::ExportToDicomDirCommandParams::ExportToDicomDirCommandParams(const TListOfPks& seriesList, const std::string& destinationPath, const GIL::DICOM::DicomDataset& anonymized, bool includeGinkgo)
{
	m_seriesList = seriesList;
	m_destinationPath = destinationPath;
	m_hasError = false;
	m_anonymized = anonymized;
	m_includeGinkgoTags = includeGinkgo;
}

GADAPI::ExportToDicomDirCommandParams::~ExportToDicomDirCommandParams()
{
}

GADAPI::ExportToDicomDirCommand::ExportToDicomDirCommand(ExportToDicomDirCommandParams* pParams): GNC::GCS::IComando(pParams,"Export Dicom Dir")
{
	SetId(IDC_EXPORT_TO_DICOMDIR);
	m_pExportacionParams = pParams;
}

inline std::string GetRightPath(const std::string& uid, const std::string& prefix, std::map<std::string, int>& mapOfUids, int& index)
{
	std::string pathOfUid;
	if (mapOfUids.find(uid) == mapOfUids.end()) {
		mapOfUids[uid] = index;
		std::ostringstream ostr;
		ostr << prefix << index++;
		pathOfUid = ostr.str();
	} else {
		std::ostringstream ostr;
		ostr << prefix << mapOfUids[uid];
		pathOfUid = ostr.str();
	}
	return pathOfUid;
}

inline bool CreateDir(const wxString& path) 
{
	if (!wxDirExists(path)) {
		if (!wxFileName::Mkdir(path)) {
			return false;
		}
	}
	return true;
}

inline bool CreatePathOfSeries(const GNC::GCS::IHistoryController::StudyModel& studyModel, 
	const GNC::GCS::IHistoryController::SeriesModel& seriesModel, 
	std::map<std::string, int>& mapOfPathPatients, 
	std::map<std::string, int>& mapOfPathStudies, 
	std::map<std::string, int>& mapOfPathSeries,
	int& patientIndex,
	int& studyIndex,
	int& seriesIndex,
	const std::string& destinationPath,
	std::string& pathOfPatient,
	std::string& pathOfStudy,
	std::string& pathOfSeries, 
	wxString& fullPath)
{
	pathOfPatient = GetRightPath(studyModel.pat_id, "PT", mapOfPathPatients, patientIndex);
	pathOfStudy = GetRightPath(studyModel.study_iuid, "ST", mapOfPathStudies, studyIndex);
	pathOfSeries = GetRightPath(seriesModel.series_iuid, "SE", mapOfPathSeries, seriesIndex);

	fullPath = FROMPATH(destinationPath) + wxFileName::GetPathSeparator() + wxT("DICOM");
	if (!CreateDir(fullPath)) {
		return false;
	}
	fullPath += wxFileName::GetPathSeparator() + wxString::FromUTF8(pathOfPatient.c_str());
	if (!CreateDir(fullPath)) {
		return false;
	}
	fullPath +=	wxFileName::GetPathSeparator() + wxString::FromUTF8(pathOfStudy.c_str());
	if (!CreateDir(fullPath)) {
		return false;
	}
	fullPath += wxFileName::GetPathSeparator() + wxString::FromUTF8(pathOfSeries.c_str());
	if (!CreateDir(fullPath)) {
		return false;
	}
	return true;
}

inline void InsertTagRecord(const DcmTagKey& key, const std::string& value, DcmDirectoryRecord* record) 
{
	DcmTag tag(key);
	DcmElement* e = newDicomElement(tag);
	e->putString(value.c_str());	
	record->insert(e, true, false);
}

inline DcmDirectoryRecord* GetSeriesRecord(const GNC::GCS::IHistoryController::StudyModel& studyModel, 
	const GNC::GCS::IHistoryController::SeriesModel& seriesModel, 
	std::map<std::string, DcmDirectoryRecord*>& dcmMapOfPatients, 
	std::map<std::string, DcmDirectoryRecord*>& dcmMapOfStudies, 
	std::map<std::string, DcmDirectoryRecord*>& dcmMapOfSeries,
	DcmDirectoryRecord * root)
{
	DcmDirectoryRecord* PatientRecord;
	if (dcmMapOfPatients.find(studyModel.pat_id) == dcmMapOfPatients.end()) {
		PatientRecord = new DcmDirectoryRecord();
		dcmMapOfPatients[studyModel.pat_id] = PatientRecord;
		root->insertSub(PatientRecord);
		InsertTagRecord(DCM_DirectoryRecordType, "PATIENT", PatientRecord);
		InsertTagRecord(DCM_SpecificCharacterSet, "ISO_IR 192", PatientRecord);
		InsertTagRecord(DCM_PatientID, studyModel.pat_id, PatientRecord);
		InsertTagRecord(DCM_PatientName, studyModel.pat_name, PatientRecord);
		{
			std::ostringstream ostr;
			ostr << studyModel.pat_sex;
			InsertTagRecord(DCM_PatientSex, ostr.str(), PatientRecord);
		}
		InsertTagRecord(DCM_PatientBirthDate, studyModel.pat_bithdate, PatientRecord);
	} else {
		PatientRecord = dcmMapOfPatients[studyModel.pat_id];
	}

	DcmDirectoryRecord* StudyRecord;
	if (dcmMapOfStudies.find(studyModel.study_iuid) == dcmMapOfStudies.end()) {
		StudyRecord = new DcmDirectoryRecord();
		dcmMapOfStudies[studyModel.study_iuid] = StudyRecord;
		PatientRecord->insertSub(StudyRecord);
		InsertTagRecord(DCM_DirectoryRecordType, "STUDY", StudyRecord);
		InsertTagRecord(DCM_SpecificCharacterSet, "ISO_IR 192", StudyRecord);
		InsertTagRecord(DCM_StudyInstanceUID, studyModel.study_iuid, StudyRecord);
		InsertTagRecord(DCM_StudyDescription, studyModel.study_desc, StudyRecord);
		wxDateTime date;
		date.ParseFormat(wxString::FromUTF8(studyModel.study_datetime.c_str()), wxT("%Y-%m-%dT%H:%M:%S"));
		if (date.IsValid()) {
			std::string tmp(date.Format(wxT("%Y%m%d")).ToUTF8());
			InsertTagRecord(DCM_StudyDate, tmp, StudyRecord);
			tmp = date.Format(wxT("%H%M%S.%l")).ToUTF8();
			InsertTagRecord(DCM_StudyTime, tmp, StudyRecord);
		}
		InsertTagRecord(DCM_AccessionNumber, studyModel.study_acc_no, StudyRecord);
		InsertTagRecord(DCM_StudyID, studyModel.study_id, StudyRecord);
	} else {
		StudyRecord = dcmMapOfStudies[studyModel.study_iuid];
	}

	DcmDirectoryRecord* SeriesRecord;
	if (dcmMapOfSeries.find(studyModel.study_iuid) == dcmMapOfSeries.end()) {
		SeriesRecord = new DcmDirectoryRecord();
		dcmMapOfSeries[seriesModel.series_iuid] = SeriesRecord;
		StudyRecord->insertSub(SeriesRecord);
		InsertTagRecord(DCM_DirectoryRecordType, "SERIES", SeriesRecord);
		InsertTagRecord(DCM_SpecificCharacterSet, "ISO_IR 192", SeriesRecord);
		InsertTagRecord(DCM_SeriesInstanceUID, seriesModel.series_iuid, SeriesRecord);
		InsertTagRecord(DCM_SeriesDescription, seriesModel.series_desc, SeriesRecord);
		wxDateTime date;
		date.ParseFormat(wxString::FromUTF8(seriesModel.series_datetime.c_str()), wxT("%Y-%m-%dT%H:%M:%S"));
		if (date.IsValid()) {
			std::string tmp(date.Format(wxT("%Y%m%d")).ToUTF8());
			InsertTagRecord(DCM_SeriesDate, tmp, SeriesRecord);
			tmp = date.Format(wxT("%H%M%S.%l")).ToUTF8();
			InsertTagRecord(DCM_SeriesTime,  tmp, SeriesRecord);
		}
		InsertTagRecord(DCM_Modality, seriesModel.series_modality, SeriesRecord);
		InsertTagRecord(DCM_SeriesNumber, seriesModel.series_no, SeriesRecord);
	} else {
		SeriesRecord = dcmMapOfSeries[seriesModel.series_iuid];
	}

	return SeriesRecord;
}

void GADAPI::ExportToDicomDirCommand::Execute()
{
	if (!NotificarProgreso(0.0, _Std("Exporting files...")) )
		return;

	std::string pathOfDicomDir;
	{
		std::ostringstream ostr;
		ostr << m_pExportacionParams->m_destinationPath;
		ostr << (char)wxFileName::GetPathSeparator();
		ostr << "DICOMDIR";
		pathOfDicomDir = ostr.str();
	}

	//clean dicomdir file and dicomdir directory if present...
	{
		std::ostringstream ostr;
		ostr << m_pExportacionParams->m_destinationPath;
		ostr << (char) wxFileName::GetPathSeparator();
		ostr << "DICOM";
		GNC::Entorno::Instance()->RemoveDirRecursive(ostr.str());
		wxRemoveFile(FROMPATH(pathOfDicomDir));
	}
	//

	std::map<std::string, int> mapOfPathPatients;
	std::map<std::string, int> mapOfPathStudies;
	std::map<std::string, int> mapOfPathSeries;
	std::map<std::string, int> mapOfPathImages;

	std::map<std::string, DcmDirectoryRecord*> dcmMapOfPatients;
	std::map<std::string, DcmDirectoryRecord*> dcmMapOfStudies;
	std::map<std::string, DcmDirectoryRecord*> dcmMapOfSeries;

	std::string pathOfPatient, pathOfStudy, pathOfSeries;

	int patientIndex=0, studyIndex=0, seriesIndex=0;

	int numberOfSeries = m_pExportacionParams->m_seriesList.size();
	int actualSerie = 1;

	DcmDicomDir dicomDir(pathOfDicomDir.c_str(), "GINKGOCADXCD");
	DcmDirectoryRecord *   root = &(dicomDir.getRootRecord());
	
	//FIRST OF ALL EXPORT WITHOUT ANONYMIZE
	if (m_pExportacionParams->m_seriesList.size() > 0 && m_pExportacionParams->m_anonymized.tags.empty() && m_pExportacionParams->m_includeGinkgoTags) 
	{
		for (ExportToDicomDirCommandParams::TListOfPks::const_iterator itUids = m_pExportacionParams->m_seriesList.begin(); itUids != m_pExportacionParams->m_seriesList.end(); ++itUids)
		{
			wxString cadena = wxString::Format(_("Exporting series %d of %d"), (int)(actualSerie++), (int)(numberOfSeries));
			if (!NotificarProgreso((float)actualSerie/numberOfSeries, std::string(cadena.ToUTF8())))
				return;
			//find source paths and series model
			GNC::GCS::IHistoryController::SeriesModel seriesModel = GNC::GCS::HistoryController::Instance()->GetSeriesModel((*itUids));
			GNC::GCS::IHistoryController::StudyModel studyModel = GNC::GCS::HistoryController::Instance()->GetStudyModel(seriesModel.study_fk);
			GNC::GCS::IHistoryController::FileModelList fileModels;
			GNC::GCS::HistoryController::Instance()->GetSeriesSortedFileModels((*itUids), fileModels);

			if (fileModels.empty()) {
				m_pExportacionParams->m_Error = _Std("Some of selected series has been deleted");
				m_pExportacionParams->m_hasError = true;
				return;
			}

			//get path of series
			wxString fullPathWx;
			if (!CreatePathOfSeries(studyModel, seriesModel, mapOfPathPatients, mapOfPathStudies, mapOfPathSeries, patientIndex, studyIndex, seriesIndex, m_pExportacionParams->m_destinationPath, pathOfPatient, pathOfStudy, pathOfSeries, fullPathWx) ) {
				m_pExportacionParams->m_Error = _Std("There was an error creating directory");
				m_pExportacionParams->m_hasError = true;
				return;
			}
				
			//dicomdir structure
			DcmDirectoryRecord* SeriesRecord = GetSeriesRecord(studyModel, seriesModel, dcmMapOfPatients, dcmMapOfStudies, dcmMapOfSeries, root);

			//path is created, now we are going to copy images...	
			mapOfPathImages[seriesModel.series_iuid] = 0;
			for (GNC::GCS::IHistoryController::FileModelList::const_iterator itDCMModels = fileModels.begin(); itDCMModels != fileModels.end(); ++itDCMModels)
			{
				std::string pathOfImage;
				{
					std::ostringstream ostr;
					ostr << "IM";
					ostr << mapOfPathImages[seriesModel.series_iuid]++;
					pathOfImage = ostr.str();
				}

				wxString pathOfImageWx = fullPathWx + wxFileName::GetPathSeparator() + wxString::FromUTF8(pathOfImage.c_str());
				std::string absolutepathFile = (*itDCMModels).real_path;
				if (!wxCopyFile(FROMPATH(absolutepathFile),  pathOfImageWx)) {
					LOG_ERROR("ExportToDicomDirCommand", "Error copying file " << absolutepathFile << " TO " << pathOfImageWx.ToUTF8());
					m_pExportacionParams->m_Error = _Std("There was an error writing file");
					m_pExportacionParams->m_hasError = true;
					return;
				}
				DcmDirectoryRecord* ImageRecord = new DcmDirectoryRecord();
				SeriesRecord->insertSub(ImageRecord);
				std::string fileId;
				{
					std::ostringstream ostr;
					ostr << "DICOM" << "\\" << pathOfPatient << "\\" << pathOfStudy << "\\" << pathOfSeries << "\\" << pathOfImage;
					fileId = ostr.str();
				}
				InsertTagRecord(DCM_DirectoryRecordType, "IMAGE", ImageRecord);
				InsertTagRecord(DCM_ReferencedSOPInstanceUIDInFile, (*itDCMModels).sopiuid, ImageRecord);
				InsertTagRecord(DCM_ReferencedTransferSyntaxUIDInFile, (*itDCMModels).tsuid, ImageRecord);
				InsertTagRecord(DCM_ReferencedSOPClassUIDInFile, (*itDCMModels).sopcuid, ImageRecord);
				InsertTagRecord(DCM_SpecificCharacterSet, "ISO_IR 192", ImageRecord);
				InsertTagRecord(DCM_ImageComments, (*itDCMModels).file_desc, ImageRecord);
				std::string instanceNumber;
				{
					std::ostringstream ostr;
					ostr << (*itDCMModels).instance_number;
					instanceNumber = ostr.str();
				}
				InsertTagRecord(DCM_InstanceNumber, instanceNumber, ImageRecord);
				InsertTagRecord(DCM_ReferencedFileID, fileId, ImageRecord);
			}
		}
	}///END EXPORT WITHOUT ANONYMIZE
	std::string m_TmpDir = GNC::Entorno::Instance()->CrearDirectorioTemporal();
	//export series anonymizing...
	if (m_pExportacionParams->m_seriesList.size() > 0 && (!m_pExportacionParams->m_anonymized.tags.empty() || !m_pExportacionParams->m_includeGinkgoTags)) 
	{
		for (ExportToDicomDirCommandParams::TListOfPks::const_iterator itUids = m_pExportacionParams->m_seriesList.begin(); itUids != m_pExportacionParams->m_seriesList.end(); ++itUids)
		{
			wxString cadena = wxString::Format(_("Exporting series %d of %d"), (int)(actualSerie++), (int)(numberOfSeries));
			if (!NotificarProgreso((float)actualSerie/numberOfSeries, std::string(cadena.ToUTF8())))
				return;
			//find source paths and series model
			GNC::GCS::IHistoryController::FileModelList fileModels;
			GNC::GCS::HistoryController::Instance()->GetSeriesSortedFileModels((*itUids), fileModels);

			for (GNC::GCS::IHistoryController::FileModelList::const_iterator itFileModel = fileModels.begin(); itFileModel != fileModels.end(); ++itFileModel) {
				GIL::DICOM::DICOMManager manager;
				manager.CargarFichero((*itFileModel).real_path);
				manager.ActualizarJerarquia(m_pExportacionParams->m_anonymized);
				if (!m_pExportacionParams->m_includeGinkgoTags) {
					manager.AnonimizarTagsPrivados();
				}
				wxString targetFile = FROMPATH(m_TmpDir) + wxFileName::GetPathSeparator() + wxString::Format(wxT("%d"), (int)(rand()));
				while (wxFileExists(targetFile)) {
					targetFile = FROMPATH(m_TmpDir) + wxFileName::GetPathSeparator() + wxString::Format(wxT("%d"), (int)(rand()));
				}
				std::string targetFileStd(TOPATH(targetFile));
				if (!manager.AlmacenarFichero(targetFileStd)) {
					m_pExportacionParams->m_Error = _Std("There was an error anonymizing files");
					m_pExportacionParams->m_hasError = true;
					return;
				}
			}				
		}
	}
	//read tmp directory and insert into dcmdir...
	{
		wxString tmpDirWx = FROMPATH(m_TmpDir);
		wxDir dir;
		if (dir.Open(tmpDirWx)) {
			wxString fileName;
			bool cont = dir.GetFirst(&fileName);
			while (cont) {
				fileName=dir.GetName()+ wxFileName::GetPathSeparator(wxPATH_NATIVE) +fileName;
				const std::string fileNameStd(TOPATH(fileName));
				GNC::GCS::IHistoryController::DICOMFileModel dicomFile;
				GNC::GCS::IHistoryController::TAddErrorList foo;
				if (!GNC::GCS::HistoryController::Instance()->ReadFile(dicomFile, fileNameStd, foo)) {
					LOG_ERROR("ExportDICOMDir", "error reading " << fileNameStd);
					continue;
				}

				//create path
				wxString fullPathWx;
				if (!CreatePathOfSeries(dicomFile.study, dicomFile.series, mapOfPathPatients, mapOfPathStudies, mapOfPathSeries, patientIndex, studyIndex, seriesIndex, m_pExportacionParams->m_destinationPath, pathOfPatient, pathOfStudy, pathOfSeries, fullPathWx) ) {
					m_pExportacionParams->m_Error = _Std("There was an error creating directory");
					m_pExportacionParams->m_hasError = true;
					return;
				}
					
				//dicomdir structure
				DcmDirectoryRecord* SeriesRecord = GetSeriesRecord(dicomFile.study, dicomFile.series, dcmMapOfPatients, dcmMapOfStudies, dcmMapOfSeries, root);

				//path is created, now we are going to copy images...	
				if (mapOfPathImages.find(dicomFile.series.series_iuid) == mapOfPathImages.end()) {
					mapOfPathImages[dicomFile.series.series_iuid] = 0;
				}
				std::string pathOfImage;
				{
					std::ostringstream ostr;
					ostr << "IM";
					ostr << mapOfPathImages[dicomFile.series.series_iuid]++;
					pathOfImage = ostr.str();
				}

				wxString pathOfImageWx = fullPathWx + wxFileName::GetPathSeparator() + wxString::FromUTF8(pathOfImage.c_str());
				//moving tmp files...
				if (!wxRenameFile(fileName, pathOfImageWx)) {
					m_pExportacionParams->m_Error = _Std("There was an error writing file");
					m_pExportacionParams->m_hasError = true;
					return;
				}

				DcmDirectoryRecord* ImageRecord = new DcmDirectoryRecord();
				SeriesRecord->insertSub(ImageRecord);
				std::string fileId;
				{
					std::ostringstream ostr;
					ostr << "DICOM" << "\\" << pathOfPatient << "\\" << pathOfStudy << "\\" << pathOfSeries << "\\" << pathOfImage;
					fileId = ostr.str();
				}
				InsertTagRecord(DCM_ReferencedFileID, fileId, ImageRecord);
				InsertTagRecord(DCM_DirectoryRecordType, "IMAGE", ImageRecord);
				InsertTagRecord(DCM_ReferencedSOPInstanceUIDInFile, dicomFile.file.sopiuid, ImageRecord);
				InsertTagRecord(DCM_ReferencedTransferSyntaxUIDInFile, dicomFile.file.tsuid, ImageRecord);
				InsertTagRecord(DCM_ReferencedSOPClassUIDInFile, dicomFile.file.sopcuid, ImageRecord);
				{
					std::ostringstream ostr;
					ostr << dicomFile.file.instance_number;
					InsertTagRecord(DCM_InstanceNumber, ostr.str(), ImageRecord);
				}
				InsertTagRecord(DCM_ImageComments, dicomFile.file.file_desc, ImageRecord);
				InsertTagRecord(DCM_SpecificCharacterSet, "ISO_IR 192", ImageRecord);
				cont = dir.GetNext(&fileName);
			}
		}

	}
	OFCondition cond = dicomDir.write();
	std::cout << cond.text() << std::endl;
}



void GADAPI::ExportToDicomDirCommand::Update()
{
	if (m_pExportacionParams->m_hasError)
	{
		GNC::GCS::ControladorEventos::Instance()->ProcesarEvento(new GNC::GCS::Events::EventoMensajes(NULL,m_pExportacionParams->m_Error, GNC::GCS::Events::EventoMensajes::StatusMessage,true, GNC::GCS::Events::EventoMensajes::Error));
	} else {
		GNC::GCS::ControladorEventos::Instance()->ProcesarEvento(new GNC::GCS::Events::EventoMensajes(NULL, _Std("Export has been finished successfully"), GNC::GCS::Events::EventoMensajes::StatusMessage,true, GNC::GCS::Events::EventoMensajes::Informacion));
	}
}

//endregion


#ifdef _MSC_VER
#pragma warning(pop)
#endif

