/*
*  
*  $Id: pacscontroller.cpp $
*  Ginkgo CADx Project
*
*  Copyright 2008-12 MetaEmotion S.L. All rights reserved.
*  http://ginkgo-cadx.com
*
*  This file is licensed under LGPL v3 license.
*  See License.txt for details
*
*
*/
#include <string>
#include <openssl/md5.h>

#include "pacscontroller.h"
#include "dcmtk/dicomimg2dcm.h"
#include "dcmtk/dicomservers.h"
#include "dcmtk/dicomservice.h"
#include "dcmtk/dicomnetclient.h"
#include "dcmtk/dicomfindassociation.h"
#include "dcmtk/dicommoveassociation.h"
#include "dcmtk/dicomgetassociation.h"
#include "dcmtk/dicomstoreassociation.h"
#include "dcmtk/dicomprintassociation.h"
#include "dcmtk/dicomcustomassociation.h"
#include <dcmtk/dcmdata/dcrledrg.h>
#include <dcmtk/dcmjpeg/djdecode.h>
#include <dcmtk/dcmdata/dcdicdir.h>
#include <api/ientorno.h>
#include <api/dicom/imodelodicom.h>
#include <main/entorno.h>
#include <wx/filename.h>
#include <wx/dir.h>
#include <wx/file.h>
#include <wx/tokenzr.h>
#include <main/controllers/configurationcontroller.h>
#include <main/controllers/historycontroller.h>
#include <main/controllers/dicommanager.h>

#ifdef verify
#define MACRO_QUE_ESTORBA verify
#undef verify
#endif

#include <dcmtk/dcmdata/dcdeftag.h>
#include <dcmtk/dcmdata/dcdict.h>
#include <dcmtk/dcmdata/dcdicent.h>

#ifdef MACRO_QUE_ESTORBA
#define verify MACRO_QUE_ESTORBA
#endif

namespace GIL {
	namespace DICOM {
		wxCSConv GetConv(const std::string& Charset)
		{
			wxCSConv wxConv(wxFONTENCODING_ISO8859_1);
			if (Charset == "ISO_IR 192" || Charset == "ISO_IR 6" || Charset == "ISO_IR 138") {
				wxConv = wxCSConv(wxFONTENCODING_UTF8);
			}
			else if (Charset == "ISO_IR 100") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_1);
			}
			else if (Charset == "ISO_IR 101") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_2);
			}
			else if (Charset == "ISO_IR 109") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_3);
			}
			else if (Charset == "ISO_IR 110") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_4);
			}
			else if (Charset == "ISO_IR 148") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_9);
			}
			else if (Charset == "ISO_IR 144") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_5);
			}
			else if (Charset == "ISO_IR 127") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_6);
			}
			else if (Charset == "ISO_IR 126") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_7);
			}
			else if (Charset == "ISO_IR 138") {
				wxConv = wxCSConv(wxFONTENCODING_ISO8859_8);
			}
			return wxConv;
		}

		PACSController* PACSController::m_pInstance = NULL;

		PACSController::PACSController()
		{
			m_pServiceInstance = NULL;
		}

		PACSController::~PACSController()
		{
			//std::cout << "PACSController destruyendose" << std::endl;
			DicomServerList::FreeInstance();
			//std::cout << "PACSController destruido" << std::endl;
		}


		PACSController* PACSController::Instance()
		{
			if (m_pInstance == NULL) {
				m_pInstance = new PACSController();
			}
			return m_pInstance;
		}

		void PACSController::FreeInstance()
		{
			if (m_pInstance != NULL) {
				m_pInstance->StopServer();
				delete m_pInstance;
				m_pInstance = NULL;
			}
			//se desregistran los codecs dcmtk!
			DJDecoderRegistration::cleanup();
			DcmRLEDecoderRegistration::cleanup();
			//purgar temp
			PurgarDirectorioTemporal();
		}

		void PACSController::buildDicomDir()
		{

		}

		void PACSController::Store(const std::string& fileName, DcmDataset* dset)
		{

			GTRACE("PACSController::Store() ");
			// TODO: Try/catch


			OFString OFPacienteUId;
			OFString OFEstudioUId;
			OFString OFSerieUId;
			OFString OFImagenUId;

			std::string PacienteUId;
			std::string EstudioUId;
			std::string SerieUId;
			std::string ImagenUId;

			if (dset->findAndGetOFString(DCM_PatientID, OFPacienteUId).good()) {
				PacienteUId.assign(OFPacienteUId.c_str());
			}
			if (dset->findAndGetOFString(DCM_StudyInstanceUID, OFEstudioUId).good()) {
				EstudioUId.assign(OFEstudioUId.c_str());
			}
			if (dset->findAndGetOFString(DCM_SeriesInstanceUID, OFSerieUId).good()) {
				SerieUId.assign(OFSerieUId.c_str());
			}
			if (dset->findAndGetOFString(DCM_SOPInstanceUID, OFImagenUId).good()) {
				ImagenUId.assign(OFImagenUId.c_str());
			}
			//std::cout << "PacienteUId = " << PacienteUId << std::endl;
			//std::cout << "EstudioUId = " << EstudioUId << std::endl;
			//std::cout << "SerieUId = " << SerieUId << std::endl;
			//std::cout << "ImagenUId = " << ImagenUId << std::endl;

			std::string rutaStd;
			GetRutaImagenTemp(PacienteUId,EstudioUId,SerieUId,ImagenUId,rutaStd);

			if (!wxRenameFile(FROMPATH(fileName), FROMPATH(rutaStd))) {
				LOG_ERROR("PACSCONTROLLER-STORE", "Error writing file to temp dir");
			}
		}

		IModeloDicom* PACSController::BuscarPaciente(
			void* connectionKey,
			const std::string& serverId,
			const std::string& idPaciente,
			const std::string& nombrePaciente,
			const std::string& /*fechaNacimiento*/,
			IModeloDicom* pModelo,
			GNC::IProxyNotificadorProgreso* pNotificador
			)
		{
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);

			wxCSConv conv = GetConv(server->GetDefaultCharset());

			DcmDataset query;
			DcmElement* e = NULL;

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);

			e = newDicomElement(DCM_QueryRetrieveLevel);
			e->putString(wxString( "PATIENT", wxConvUTF8).mb_str(conv));
			query.insert(e);

			e = newDicomElement(DCM_PatientID);
			e->putString(wxString( idPaciente.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_PatientName);
			e->putString(wxString( nombrePaciente.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_PatientBirthDate);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_PatientSex);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_ReferringPhysicianName);
			if (query.insert(e).bad()) {
				delete e;
			}

			NetClient<FindAssociation> a(GIL::DICOM::DCMTK::Network::Instance(connectionKey), "C-FIND", pNotificador);

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			if (server->useTLS) {
				a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
			}
			if (server->GetPACSUser() != "") {
				a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
			}

			a.QueryServer(&query, server, pModelo, localAET, CT_FindPacientes);

			return pModelo;
		}

		IModeloDicom* PACSController::BuscarEstudio(
			void* connectionKey,
			const std::string& serverId,
			const std::string& idPaciente,
			const std::string& nombrePaciente,
			const std::string& idEstudio,
			const std::string& AccNumber,
			const std::string& modality,
			const std::string& fechaDesde,
			const std::string& fechaHasta,
			const std::string& timeFrom,
			const std::string& timeTo,
			const std::string& descripcionEstudio,
			const std::string& WXUNUSED(nombreEstacion),
			IModeloDicom* pModelo,
			GNC::IProxyNotificadorProgreso* pNotificador
			)
		{
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);

			wxCSConv conv = GetConv(server->GetDefaultCharset());

			std::string description;
			if (!descripcionEstudio.empty()) {
				description = descripcionEstudio;
			}

			// create date querystring
			std::string date;
			if (fechaDesde.empty() && fechaHasta.empty()) {
				date = "";
			} else {
				date = fechaDesde + "-" + fechaHasta;
			}

			if (fechaDesde == fechaHasta) {
				date = fechaDesde;
			}

			std::string time;
			if (timeFrom.empty() && timeTo.empty()) {
				time = "";
			} else {
				time = timeFrom + "-" + timeTo;
			}

			if (timeFrom == timeTo) {
				time = timeFrom;
			}

			std::string station;
			DcmDataset query;
			DcmElement* e = NULL;

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);

			e = newDicomElement(DCM_QueryRetrieveLevel);
			e->putString(wxString( "STUDY", wxConvUTF8).mb_str(conv));
			query.insert(e);

			e = newDicomElement(DCM_PatientName);
			e->putString(wxString( nombrePaciente.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_PatientID);
			e->putString(wxString( idPaciente.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_ModalitiesInStudy);
			if (modality != "*" && modality != "") {
				e->putString(wxString( modality.c_str(), wxConvUTF8).mb_str(conv));
			}
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_PatientBirthDate);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_PatientSex);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyDate);
			e->putString(wxString( date.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyTime);
			e->putString(time.c_str());
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyID);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyInstanceUID);
			e->putString(wxString( idEstudio.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyDescription);
			e->putString(wxString( description.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_AccessionNumber);
			e->putString(wxString( AccNumber.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StationName);
			e->putString(wxString( station.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_ReferringPhysicianName);
			if (query.insert(e).bad()) {
				delete e;
			}

			NetClient<FindAssociation> a(connectionKey, "C-FIND", pNotificador);

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			
			if (server->useTLS) {
				a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
			}
			if (server->GetPACSUser() != "") {
				a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
			}
			a.QueryServer(&query, server, pModelo, localAET, CT_FindEstudios);

			return pModelo;
		}

		IModeloDicom* PACSController::BuscarSeries(
			void* connectionKey,
			const std::string& serverId,
			const std::string& idEstudio,
			const std::string& idSerie,
			IModeloDicom* pModelo,
			GNC::IProxyNotificadorProgreso* pNotificador
			)
		{
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);

			wxCSConv conv = GetConv(server->GetDefaultCharset());

			std::string estudioId;
			std::string serieId;

			if (!idEstudio.empty()) {
				estudioId.assign(idEstudio.c_str());
			}

			if (!idSerie.empty()) {
				serieId.assign(idSerie.c_str());
			}

			DcmDataset query;
			DcmElement* e = NULL;

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);

			e = newDicomElement(DCM_QueryRetrieveLevel);
			e->putString(wxString( "SERIES", wxConvUTF8).mb_str(conv));
			query.insert(e);

			e = newDicomElement(DCM_StudyInstanceUID);
			e->putString(wxString( estudioId.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_SeriesInstanceUID);
			e->putString(wxString( serieId.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_Modality);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_SeriesDescription);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_SeriesDate);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_SeriesTime);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_NumberOfSeriesRelatedInstances);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_ReferringPhysicianName);
			if (query.insert(e).bad()) {
				delete e;
			}

			NetClient<FindAssociation> a(connectionKey, "C-FIND", pNotificador);

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			if (server->useTLS) {
				a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
			}
			if (server->GetPACSUser() != "") {
				a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
			}
			a.QueryServer(&query, server, pModelo, localAET, CT_FindSeries);

			return pModelo;
		}

		IModeloDicom* PACSController::BuscarImagenes(
			void* connectionKey,
			const std::string& serverId,
			const std::string& idSerie,
			const std::string& /*idImagen*/,
			IModeloDicom* pModelo,
			GNC::IProxyNotificadorProgreso* pNotificador
			)
		{
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);

			wxCSConv conv = GetConv(server->GetDefaultCharset());

			std::string serieId;

			if (!idSerie.empty()) {
				serieId = idSerie;
			}

			DcmDataset query;
			DcmElement* e = NULL;

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);

			e = newDicomElement(DCM_QueryRetrieveLevel);
			e->putString(wxString( "IMAGE", wxConvUTF8).mb_str(conv));
			query.insert(e);

			e = newDicomElement(DCM_SeriesInstanceUID);
			e->putString(wxString( serieId.c_str(), wxConvUTF8).mb_str(conv));
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_SOPInstanceUID);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_ImageID);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_ImageIndex);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_ReferringPhysicianName);
			if (query.insert(e).bad()) {
				delete e;
			}

			NetClient<FindAssociation> a(connectionKey, "C-FIND", pNotificador);

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			if (server->useTLS) {
				a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
			}
			if (server->GetPACSUser() != "") {
				a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
			}
			a.QueryServer(&query, server, pModelo, localAET, CT_FindImagenes);

			return pModelo;
		}

		bool PACSController::LeerDicomDir(const std::string& pathDicomDir, IModeloDicom* pModelo)
		{
			if(pModelo == NULL || !DICOMManager::EsDicom(pathDicomDir))
			{
				return false;
			}

			DcmDicomDir            dicomdir(pathDicomDir.c_str());
			DcmDirectoryRecord *   root = &(dicomdir.getRootRecord());
			DcmDirectoryRecord *   PatientRecord = NULL;
			DcmDirectoryRecord *   StudyRecord = NULL;
			DcmDirectoryRecord *   SeriesRecord = NULL;
			DcmDirectoryRecord *   FileRecord = NULL;
			OFString            tmpString;

			wxFileName fileNameDicomDir(FROMPATH(pathDicomDir));
			std::string basePath;
			basePath = TOPATH(fileNameDicomDir.GetPath());

			if(root != NULL)
			{
				while (((PatientRecord = root->nextSub(PatientRecord)) != NULL))
				{
					OFString uidPaciente, nombrePaciente, fechaNacimiento, sexo;
					PatientRecord->findAndGetOFString(DCM_PatientID,uidPaciente);
					PatientRecord->findAndGetOFString(DCM_PatientName,nombrePaciente);
					PatientRecord->findAndGetOFString(DCM_PatientBirthDate, fechaNacimiento);
					PatientRecord->findAndGetOFString(DCM_PatientSex, sexo);

					pModelo->InsertarPaciente(uidPaciente.c_str(),nombrePaciente.c_str(),fechaNacimiento.c_str(),sexo.c_str());
					while (((StudyRecord = PatientRecord->nextSub(StudyRecord)) != NULL))
					{
						OFString uidEstudio, descripcionEstudio, fechaEstudio, horaEstudio, doctor, accesionNumber;
						StudyRecord->findAndGetOFString(DCM_StudyInstanceUID, uidEstudio);
						StudyRecord->findAndGetOFString(DCM_StudyDescription, descripcionEstudio);
						StudyRecord->findAndGetOFString(DCM_StudyDate, fechaEstudio);
						StudyRecord->findAndGetOFString(DCM_StudyTime, horaEstudio);
						StudyRecord->findAndGetOFString(DCM_ReferringPhysicianName, doctor);
						StudyRecord->findAndGetOFString(DCM_AccessionNumber, accesionNumber);

						pModelo->InsertarEstudio(uidPaciente.c_str(),uidEstudio.c_str(),accesionNumber.c_str(), descripcionEstudio.c_str(),"",fechaEstudio.c_str(),horaEstudio.c_str(),doctor.c_str());
						while (((SeriesRecord = StudyRecord->nextSub(SeriesRecord)) != NULL))
						{
							OFString uidSerie,modalidadSerie,fechaSerie,horaSerie,descripcionSerie,numeroSerie,doctorSerie;
							SeriesRecord->findAndGetOFString(DCM_SeriesInstanceUID, uidSerie);
							SeriesRecord->findAndGetOFString(DCM_Modality, modalidadSerie);
							SeriesRecord->findAndGetOFString(DCM_SeriesDate, fechaSerie);
							SeriesRecord->findAndGetOFString(DCM_SeriesTime, horaSerie);
							SeriesRecord->findAndGetOFString(DCM_SeriesDescription, descripcionSerie);
							SeriesRecord->findAndGetOFString(DCM_SeriesNumber, numeroSerie);
							SeriesRecord->findAndGetOFString(DCM_ReferringPhysicianName, doctorSerie);

							pModelo->InsertarSerie(uidEstudio.c_str(),uidSerie.c_str(),modalidadSerie.c_str(),fechaSerie.c_str(),horaSerie.c_str(),descripcionSerie.c_str(),numeroSerie.c_str(),doctorSerie.c_str());
							while(((FileRecord = SeriesRecord->nextSub(FileRecord)) != NULL))
							{
								if(FileRecord->findAndGetOFStringArray(DCM_DirectoryRecordType,tmpString).good() && (tmpString == "IMAGE" || tmpString == "ENCAP DOC"))
								{
									if(FileRecord->findAndGetOFStringArray(DCM_ReferencedFileID,tmpString).good())
									{
										OFString uidImagen;
										FileRecord->findAndGetOFString(DCM_ReferencedSOPInstanceUIDInFile, uidImagen);

										wxString currentPath = FROMPATH(basePath);

										wxString wxStr = FROMPATH(tmpString);
										wxString separador = wxFileName::GetPathSeparator();
										wxString barra = wxT('\\');
										wxStr.Replace(barra,separador);

										if (wxFileExists(currentPath + separador + wxStr)) 
										{
											currentPath+= separador + wxStr;
										} 
										else
										{										
											// recase: Get case insensitive path (if needed)
											wxStringTokenizer tokenizer(wxStr, separador);
											bool validPath = true; // When false, "recase" will do nothing more

											while (validPath && tokenizer.HasMoreTokens() )
											{
												wxString relPathPart = tokenizer.GetNextToken();

												if (tokenizer.HasMoreTokens()) { // Scanning subdirectories
													wxString entry;
													if (validPath && !wxDir::Exists(currentPath + separador + relPathPart)) {
														wxDir dir;
														bool cont = dir.Open(currentPath) && dir.GetFirst(&entry, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN );
														bool found = false;
														while ( cont )
														{
															if (entry.CmpNoCase(relPathPart) == 0) {
																found = true;
																cont = false;
															}
															if (cont) {
																cont = dir.GetNext(&entry);
															}
														}
														if (!found) {
															currentPath += separador + relPathPart; // Assign case sentitive subdir to current path. (Will be non-existing)
															validPath = false;
														}
														else {
															currentPath += separador + entry; // Assign case sentitive subdir to current path
														}
													}
													else {
														currentPath += separador + relPathPart; // Assign case sentitive subdir to current path (Could be non-existing if validPath == false)
													}

												}
												else { // Scanning end file

													wxString entry;

													if (validPath && !wxFile::Access(currentPath + separador + relPathPart, wxFile::read)) {
														wxDir dir;
														bool cont = dir.Open(currentPath) && dir.GetFirst(&entry, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN );
														bool found = false;
														while ( cont )
														{
															if (entry.CmpNoCase(relPathPart) == 0) {
																cont = false;
																found = true;
															}
															if (cont) {
																cont = dir.GetNext(&entry);
															}
														}
														if (!found) {
															currentPath += separador + relPathPart; // Assign case sentitive filename to current path. (Will be non-existing)
															validPath = false;
														}
														else {
															currentPath += separador + entry; // Assign case sentitive filename to current path
														}
													}
													else {
														currentPath += separador + relPathPart; // Assign case sentitive filename to current path (Could be non-existing if validPath == false)
													}
												}

											}
										}//end recase

										pModelo->InsertarImagen(uidSerie.c_str(), uidImagen.c_str(), std::string(TOPATH(currentPath)));
									}
								}
							}
						}
					}
				}
				return true;
			} else {
				return false;
			}
		}

		bool PACSController::findAndGetTagFromFile(const std::string& ruta, unsigned short group, unsigned short element, std::string& value)
		{

			DcmFileFormat dcm;
			OFCondition   cond;
			OFString      ofval;
			bool          found = false;
			cond = dcm.loadFile(ruta.c_str());
			if (cond.good()) {
				cond = dcm.getDataset()->findAndGetOFString(DcmTagKey(group, element), ofval);
			}
			if (cond.good()) {
				value = ofval.c_str();
				found = true;
			}
			return found;
		}

		bool PACSController::EsDICOM(const std::string& path, bool accept_dicomdir, bool accept_dicomfile) const
		{
			bool valido = false;
			char magic[5] = { 'X', 'X', 'X', 'X', 0 };
			std::string emptystr;
			std::fstream dcmfile;

			dcmfile.open (path.c_str(), std::ios::in | std::ios::binary);

			if (!dcmfile.eof() && dcmfile.good()) {
				dcmfile.seekp (128, std::ios::beg);
			}
			if (!dcmfile.eof() && dcmfile.good() ) {
				dcmfile.read (magic, 4);
			}
			if (!dcmfile.eof() && dcmfile.good()) {
				if ( magic[0] == 'D' && magic[1] == 'I' && magic[2] == 'C' && magic[3] == 'M' ) {
					if (!accept_dicomdir || !accept_dicomfile) {
						// Leemos el tag 0004 0x1130 => Si existe es DICOMDIR
						bool has_dcmdirtag = false;
						GIL::DICOM::DicomDataset base;

						if (GIL::DICOM::PACSController::Instance()->findAndGetTagFromFile(path, 0x0004, 0x1200, emptystr))
						{
							has_dcmdirtag = true;
						}

						if (has_dcmdirtag) {
							if (accept_dicomdir) {
								valido = true;
							}
						}
						else {
							if (accept_dicomfile) {
								valido = true;
							}
						}
					}
					else {
						valido = true;
					}
				}
			}

			dcmfile.close();
			return valido;
		}

		bool PACSController::EsPDF(const std::string& path) const
		{
			bool valido = false;
			char magic[4] = { 'X', 'X', 'X', 'X' }; // %PDF
			std::fstream pdffile;

			pdffile.open (path.c_str(), std::ios::in | std::ios::binary);

			if (!pdffile.eof() && pdffile.good() ) {
				pdffile.read (magic, 4);
			}
			if (!pdffile.eof() && pdffile.good()) {
				if ( magic[0] == '%' && magic[1] == 'P' && magic[2] == 'D' && magic[3] == 'F' ) {
					valido = true;
				}
			}
			pdffile.close();

			return valido;
		}

		void PACSController::FillInQuery(const GIL::DICOM::DicomDataset& base, DcmDataset* query, DicomServer* server)
		{			
			wxCSConv conv = GetConv(server->GetDefaultCharset());
			OFCondition cond;

			typedef GIL::DICOM::DicomDataset TJerarquia;

			for (ListaTags::const_iterator it = base.tags.begin(); it != base.tags.end(); ++it) {
				DcmElement* e = GIL::DICOM::DICOMManager::CrearElementoConValor((*it).first.c_str());

				if (e != NULL) {
					const std::string& val = (*it).second;
					if (val.size() > 0) {
						e->putString( wxString( (*it).second.c_str(), wxConvUTF8).mb_str(conv) );
					}					
					cond = query->insert(e, true, false);

					if (cond.bad()) {
						LOG_ERROR("PACSCONTROLLER", "No se pudo insertar el elemento: (" << e->getTag().toString().c_str() << "): " << cond.text());
					}
				}

				for (TJerarquia::DatasetList::const_iterator it2 = base.secuencias.begin(); it2 != base.secuencias.end(); ++it2) {
					const TJerarquia& seq = (*it2);
					DcmElement* es = GIL::DICOM::DICOMManager::CrearElementoConValor(seq.tagName.c_str());			

					for (TJerarquia::DatasetList::const_iterator it3 = seq.items.begin(); it3 != seq.items.end(); ++it3) {
						const TJerarquia& item = (*it3);
						DcmItem* di = new DcmItem();

						for (ListaTags::const_iterator it4 = item.tags.begin(); it4 != item.tags.end(); ++it4) {
							DcmElement* ei = GIL::DICOM::DICOMManager::CrearElementoConValor((*it4).first.c_str());
							if (ei != NULL) {
								const std::string& val = (*it4).second;
								if (val.size() > 0) {
									ei->putString( wxString( (*it4).second.c_str(), wxConvUTF8).mb_str(conv) );
								}					
								cond = di->insert(ei, true, false);

								if (cond.bad()) {
									LOG_ERROR("PACSCONTROLLER", "No se pudo insertar el elemento: (" << ei->getTag().toString().c_str() << "): " << cond.text());
								}
							}

						}

						query->insertSequenceItem(es->getTag(), di);
					}					
				}
			}
		}

		bool PACSController::Print(void* connectionKey, const std::string& serverId, const GIL::DICOM::DicomDataset& film, const GIL::DICOM::DicomDataset& layout, const GIL::DICOM::DicomDataset& job, const std::list<std::string> files, GNC::IProxyNotificadorProgreso* pNotificador)
		{
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);
			if (server == NULL) {
				LOG_ERROR("C-MOVE/C-GET", "Invalid server");
				return false;
			}
			if (!server->GetReuseConnection()) {
				ReleaseConnection(connectionKey);
				GetConnection(connectionKey);
			}
			try {
				PrintAssociation f(connectionKey, "C-PRINT", pNotificador);
				CONDITION cond = f.Print(server, GNC::Entorno::Instance()->GetDicomLocalAET(), film, layout, job, files);
				if (cond.bad()) {
					throw GIL::DICOM::PACSException(cond.text(), "GIL/DICOM/Print");
				}
			}
			catch(std::exception& ex) {
				std::ostringstream os;
				os << "Internal error: " << ex.what();
				throw GIL::DICOM::PACSException(os.str(), "GIL/DICOM/Print");
			}

			return true;
		}


		bool PACSController::ObtenerEstudio(void* connectionKey, const std::string& serverId, const GIL::DICOM::DicomDataset& base, IModeloDicom* pModelo, GNC::IProxyNotificadorProgreso* pNotificador)
		{
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);
			if (server == NULL) {
				LOG_ERROR("C-MOVE/C-GET", "Invalid server");
				return false;
			}
			
			wxCSConv conv = GetConv(server->GetDefaultCharset());

			if (!server->GetReuseConnection()) {
				ReleaseConnection(connectionKey);
				GetConnection(connectionKey);
			}

			if (pModelo == NULL) {
				return false;
			}

			DcmDataset query;
			DcmElement* e = NULL;

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);

			e = newDicomElement(DCM_PatientID);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_PatientName);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyDate);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyTime);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_StudyID);
			if (query.insert(e).bad()) {
				delete e;
			}

			FillInQuery(base, &query, server);

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			if (server->GetRetrieveSeries()) {
				//buscar + descargar series...
				//we have to know the series uids and obtain series by series
				e = newDicomElement(DCM_QueryRetrieveLevel);
				e->putString(wxString( "SERIES", wxConvUTF8).mb_str(conv));
				query.insert(e, true);

				e = newDicomElement(DCM_SeriesInstanceUID);
				if (query.insert(e).bad()) {
					delete e;
				}

				e = newDicomElement(DCM_Modality);
				if (query.insert(e).bad()) {
					delete e;
				}

				std::list<std::string> listOfUIDS;
				std::list<std::string> listOfModalities;
				{
					NetClient<FindAssociation> f(connectionKey, "C-GET/FIND", pNotificador);
					f.SetMaxResults(-1);

					std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

					if (server->useTLS) {
						f.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
					}
					if (server->GetPACSUser() != "") {
						f.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
					}

					f.QueryServer(&query, server, pModelo, localAET, CT_None);

					if (f.Stopped()){
						return false;
					}
					DcmStack* stack = f.GetResultStack();

					unsigned int numResults = 0;

					numResults = stack->card();


					for (unsigned int i = 0; i < numResults; i++) {

						if (stack->elem(i)->ident() == EVR_dataset) {
							DcmDataset* dset = dynamic_cast<DcmDataset*>(stack->elem(i));
							if (dset) {
								OFString OFSSeriesInstanceUID;
								OFString OFSeriesModality;
								if ( dset->findAndGetOFString(DCM_SeriesInstanceUID, OFSSeriesInstanceUID).good() && dset->findAndGetOFString(DCM_Modality, OFSeriesModality).good() )
								{
									std::string seriesInstanceUID(OFSSeriesInstanceUID.c_str());
									std::string seriesModality(OFSeriesModality.c_str());

									listOfUIDS.push_back(seriesInstanceUID);
									listOfModalities.push_back(seriesModality);
								}
							}

						}
					}
				}

				for (std::list<std::string>::iterator itUIDS = listOfUIDS.begin(), itModalities = listOfModalities.begin();
					itUIDS != listOfUIDS.end(); ++itUIDS, ++itModalities) 
				{
					//it's mandatory to release connection here to start one connection for each series	
					ReleaseConnection(connectionKey);
					GetConnection(connectionKey);

					GIL::DICOM::DicomDataset baseAux = base;
					baseAux.tags["0020|000e"] = (*itUIDS);
					baseAux.tags["0008|0060"] = (*itModalities);
					ObtenerSerie(connectionKey, serverId, baseAux, pModelo, pNotificador);
				}

				query.clear();	
			} else if (server->GetRetrieveWithMove()) {
				e = newDicomElement(DCM_QueryRetrieveLevel);
				e->putString(wxString( "STUDY", wxConvUTF8).mb_str(conv));
				query.insert(e, true);

				if (server) {
					LOG_INFO("C-MOVE", "Obteniendo estudio del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << "Method=C-MOVE");
				}

				NetClient<MoveAssociation> a(connectionKey, "C-MOVE", pNotificador);

				a.SetRole(Association::RT_Requestor);
				a.SetModelo(pModelo);
				a.SetCallbackHandler(this);

				if (server->useTLS) {
					a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
				}
				if (server->GetPACSUser() != "") {
					a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
				}
				a.QueryServer(&query, server, pModelo, localAET, CT_MoveSerie);

			}
			else {

				if (server) {
					LOG_INFO("C-GET", "Obteniendo estudio del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << ", Method=C-GET");
				}

				//we have to know the series uids and obtain series by series
				e = newDicomElement(DCM_QueryRetrieveLevel);
				e->putString(wxString( "SERIES", wxConvUTF8).mb_str(conv));
				query.insert(e, true);

				e = newDicomElement(DCM_SeriesInstanceUID);
				if (query.insert(e).bad()) {
					delete e;
				}

				e = newDicomElement(DCM_Modality);
				if (query.insert(e).bad()) {
					delete e;
				}

				NetClient<FindAssociation> f(connectionKey, "C-GET/FIND", pNotificador);
				f.SetMaxResults(-1);

				std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

				if (server->useTLS) {
					f.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
				}
				if (!server->GetPACSUser().empty()) {
					f.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
				}

				f.QueryServer(&query, server, pModelo, localAET, CT_None);

				if (f.Stopped()){
					return false;
				}
				DcmStack* stack = f.GetResultStack();

				unsigned int numResults = 0;

				numResults = stack->card();

				for (unsigned int i = 0; i < numResults; i++) {

					if (stack->elem(i)->ident() == EVR_dataset) {
						DcmDataset* dset = dynamic_cast<DcmDataset*>(stack->elem(i));
						if (dset) {
							OFString OFSStudyInstanceUID;
							OFString OFSSeriesInstanceUID;
							OFString OFSeriesModality;
							if ( dset->findAndGetOFString(DCM_SeriesInstanceUID, OFSSeriesInstanceUID).good() && dset->findAndGetOFString(DCM_Modality, OFSeriesModality).good() )
							{
								GIL::DICOM::DicomDataset baseAux = base;

								if (dset->findAndGetOFString(DCM_StudyInstanceUID, OFSStudyInstanceUID).good()) {
									baseAux.tags["0020|000d"] = OFSStudyInstanceUID.c_str();
								}

								baseAux.tags["0020|000e"] = OFSSeriesInstanceUID.c_str();
								baseAux.tags["0008|0060"] = OFSeriesModality.c_str();

								ObtenerSerie(connectionKey, serverId, baseAux, pModelo, pNotificador);
							}
						}

					}
				}

				query.clear();

			}

			return true;

		}

		bool PACSController::ObtenerSerie(void* connectionKey, const std::string& serverId, const GIL::DICOM::DicomDataset& base, IModeloDicom* pModelo, GNC::IProxyNotificadorProgreso* pNotificador)
		{
			if (pModelo == NULL) {
				return false;
			}
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);

			if (server == NULL) {
				LOG_ERROR("C-MOVE/C-GET", "Invalid server");
				return false;
			}

			wxCSConv conv = GetConv(server->GetDefaultCharset());

			std::string modality;
			if (base.tags.find("0008|0060") != base.tags.end()) {
				modality = base.tags.find("0008|0060")->second;
			}

			DcmElement* e = NULL;
			DcmDataset query;

			unsigned int numResults = 0;

			if (!server->GetRetrieveWithMove() && modality.empty()) { // We have to find series modality
				FillInQuery(base, &query, server);

				e = newDicomElement(DCM_SpecificCharacterSet);
				e->putString(server->GetDefaultCharset().c_str());
				query.insert(e);

				e = newDicomElement(DCM_QueryRetrieveLevel);
				e->putString(wxString( "SERIES", wxConvUTF8).mb_str(conv));
				query.insert(e, true);

				e = newDicomElement(DCM_StudyInstanceUID);
				if (query.insert(e).bad()) {
					delete e;
				}

				e = newDicomElement(DCM_SeriesInstanceUID);
				if (query.insert(e).bad()) {
					delete e;
				}

				e = newDicomElement(DCM_Modality);
				if (query.insert(e).bad()) {
					delete e;
				}

				NetClient<FindAssociation> f(connectionKey, "C-GET/FIND", pNotificador);
				f.SetMaxResults(-1);

				std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

				if (server->useTLS) {
					f.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
				}
				if (server->GetPACSUser() != "") {
					f.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
				}

				if (!f.QueryServer(&query, server, pModelo, localAET, CT_None)) 
				{
					return false;
				}

				if (f.Stopped()){
					return false;
				}
				DcmStack* stack = f.GetResultStack();

				OFString OFSeriesModality;

				numResults = stack->card();
				if (numResults >1) {
					LOG_WARN("C-GET", "Obteniendo serie del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << ", there have a Series with more than one modality, we are going to process only first modality");
				}

				for (unsigned int i = 0; i < numResults; i++) {

					if (stack->elem(i)->ident() == EVR_dataset) {
						DcmDataset* dset = dynamic_cast<DcmDataset*>(stack->elem(i));
						if (dset) {

							if ( dset->findAndGetOFString(DCM_Modality, OFSeriesModality).good() && OFSeriesModality.size() > 0 )
							{
								modality = OFSeriesModality.c_str();
								break;
							}
						}

					}
				}
				query.clear();
			}// end query modality

			FillInQuery(base, &query, server);

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);			

			e = newDicomElement(DCM_QueryRetrieveLevel);
			e->putString(wxString( "SERIES", wxConvUTF8).mb_str(conv));
			query.insert(e, true);

			e = newDicomElement(DCM_SeriesInstanceUID);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_Modality);
			if (query.insert(e).bad()) {
				delete e;
			}

			e = newDicomElement(DCM_SeriesNumber);
			if (query.insert(e).bad()) {
				delete e;
			}

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			if (server->GetRetrieveWithMove()) {

				if (server) {
					LOG_INFO("C-MOVE", "Obteniendo serie del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << ", Method=C-MOVE");
				}

				if (server) {
					LOG_INFO("C-MOVE", "Obteniendo estudio del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << ", Method=C-MOVE");
				}

				NetClient<MoveAssociation> a(connectionKey, "C-MOVE", pNotificador);
				a.SetRole(Association::RT_Requestor);
				a.SetModelo(pModelo);

				a.SetCallbackHandler(this);

				if (server->useTLS) {
					a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
				}
				if (server->GetPACSUser() != "") {
					a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
				}
				a.QueryServer(&query, server, pModelo, localAET, CT_MoveSerie);
			}
			else {
				if (server) {
					LOG_INFO("C-GET", "Obteniendo serie del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << ", Method=C-GET");
				}

				NetClient<GetAssociation> a(connectionKey, "C-GET", pNotificador);
				a.SetMaxResults(-1);
				a.SetWellKnownNumResults(numResults);
				a.SetStorageSOPClasses(GIL::DICOM::Conformance::GetModalities().GetSupportedSOPClassUIDs(modality));
				a.SetModelo(pModelo);

				a.SetCallbackHandler(this);

				if (server->useTLS) {
					a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
				}
				if (server->GetPACSUser() != "") {
					a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
				}
				if (!a.QueryServer(&query, server, pModelo, localAET, CT_MoveSerie)) {
					return false;
				}
			}
			query.clear();
			return true;

		}

		bool PACSController::ObtenerImagen(void* connectionKey, const std::string& serverId, const GIL::DICOM::DicomDataset& base, IModeloDicom* pModelo,GNC::IProxyNotificadorProgreso* pNotificador)
		{
			if (pModelo == NULL) {
				return false;
			}
			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);

			if (server == NULL) {
				LOG_ERROR("C-MOVE/C-GET", "Invalid server");
				return false;
			}

			wxCSConv conv = GetConv(server->GetDefaultCharset());

			DcmDataset query;
			DcmElement* e = NULL;

			FillInQuery(base, &query, server);

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);

			e = newDicomElement(DCM_QueryRetrieveLevel);
			e->putString(wxString( "IMAGE", wxConvUTF8).mb_str(conv));
			query.insert(e, true);

			e = newDicomElement(DCM_InstanceNumber);
			if (query.insert(e).bad()) {
				delete e;
			}

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			if (server->GetRetrieveWithMove()) {

				if (server) {
					LOG_INFO("C-MOVE", "Obteniendo imagen del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << ", Method=C-MOVE");
				}

				NetClient<MoveAssociation> a(connectionKey, "C-MOVE", pNotificador);
				a.SetModelo(pModelo);

				a.SetCallbackHandler(this);

				if (server->useTLS) {
					a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
				}
				if (server->GetPACSUser() != "") {
					a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
				}
				a.QueryServer(&query, server, pModelo, localAET, CT_MoveImagen);

			}
			else {

				if (server) {
					LOG_INFO("C-GET", "Obteniendo imagen del PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser << ", Method=C-GET");
				}

				NetClient<GetAssociation> a(connectionKey, "C-GET", pNotificador);
				a.SetModelo(pModelo);

				a.SetCallbackHandler(this);

				if (server->useTLS) {
					a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
				}
				if (server->GetPACSUser() != "") {
					a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
				}
				a.QueryServer(&query, server, pModelo, localAET, CT_MoveImagen);
			}
			return true;
		}

		/** Gets the current server ID list */
		void PACSController::GetServerList(std::list<std::string>& /*list*/)
		{
			//TODO
		}

		/** Perform query */
		bool PACSController::Query(
			void* connectionKey,
			const std::string& abstractSyntax,
			const std::list<std::string>& /*transferSyntaxes*/,
			const GIL::DICOM::DicomDataset& queryWrapper,
			int maxResultTagLen,
			std::list< GNC::GCS::Ptr<GIL::DICOM::DicomDataset> >& resultsWrapper,				
			const std::string& serverId,
			GNC::IProxyNotificadorProgreso* pNotificador
			)
		{

			resultsWrapper.clear();

			DicomServerList* listaServidores = DicomServerList::Instance();
			DicomServer* server = listaServidores->GetServer(serverId);

			DcmDataset query;
			DcmElement* e = NULL;

			e = newDicomElement(DCM_SpecificCharacterSet);
			e->putString(server->GetDefaultCharset().c_str());
			query.insert(e);

			DICOMManager* dm = new DICOMManager(&query, server->GetDefaultCharset());
			dm->InsertarJerarquia(queryWrapper, NULL, NULL);
			delete dm;

			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();

			NetClient<FindAssociation> a(connectionKey, "C-FIND", pNotificador);

			a.SetAbstractSyntax(abstractSyntax);

			if (server->useTLS) {
				a.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
			}
			if (server->GetPACSUser() != "") {
				a.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
			}
			if (a.QueryServer(&query, server, NULL, localAET, CT_None)) {
				DcmStack* stack = a.GetResultStack();
				if (stack != NULL)
				{

					for (unsigned int i = 0; i < stack->card(); i++) {

						if (stack->elem(i)->ident() == EVR_dataset) {
							GIL::DICOM::DICOMManager mgr( static_cast<DcmDataset*>(stack->elem(i)), server->GetDefaultCharset() );
							GNC::GCS::Ptr<GIL::DICOM::DicomDataset> base = new GIL::DICOM::DicomDataset();							
							mgr.CargarJerarquia((*base), maxResultTagLen);
							resultsWrapper.push_back(base);
						}
					}
				}
				return true;
			}
			else {
				return false;
			}
		}


		bool PACSController::QueryRetrieve(
			void* connectionKey,
			const std::string& serverId,
			IModeloDicom* pModelo,
			const GIL::DICOM::DicomDataset& base,
			GNC::IProxyNotificadorProgreso* pNotificador)
		{
			std::string strTmp;
			if(base.getTag("0008|0052", strTmp)) {
				if (strTmp == "STUDY") {
					std::string uidEstudio, accNumber;
					base.getTag("0020|000d", uidEstudio);
					base.getTag("0008|0050", accNumber);
					if (accNumber == "" && uidEstudio == "") {
						throw GIL::DICOM::PACSException(_Std("You have to specify at least uid study or accession number"));
					}

					ObtenerEstudio(connectionKey, serverId, base, pModelo, pNotificador);
				} else if (strTmp == "SERIES") {
					std::string uidSeries;
					if (!base.getTag("0020|000e",uidSeries)) {
						throw GIL::DICOM::PACSException(_Std("You have to specify at least uid series"));
					}
					ObtenerSerie(connectionKey, serverId, base, pModelo, pNotificador);
				} else if (strTmp == "IMAGE")  {
					std::string uidImage;
					if (!base.getTag("0008|0018",uidImage)) {
						throw GIL::DICOM::PACSException(_Std("You have to specify uid series"));
					}
					ObtenerImagen(connectionKey, serverId, base, pModelo, pNotificador);
				} else  {
					throw GIL::DICOM::PACSException(_Std("Unknown query/retrieve level"));
				}
			} else {
				throw GIL::DICOM::PACSException(_Std("Query retrieve level not specified"));
			}

			return true;
		}

		GIL::DICOM::ICustomAssociation* PACSController::CreateCustomAssociation(void* connectionKey, const std::string& ambitolog, GNC::IProxyNotificadorProgreso* pNotificador)
		{
			return new CustomAssociation(connectionKey, ambitolog, pNotificador);
		}
		
		void PACSController::DestroyCustomAssociation(GIL::DICOM::ICustomAssociation* assoc)
		{
			if (assoc != NULL) {
				delete assoc;
			}
		}

		void PACSController::StartServer()
		{
			StopServer();

			int localPort = 0;
			std::string localAET;
			{
				GNC::GCS::ConfigurationController::Instance()->readIntGeneral("/GinkgoCore/PACS/Local", "Puerto", localPort, 11112);
				GNC::GCS::ConfigurationController::Instance()->readStringGeneral("/GinkgoCore/PACS/Local", "AET", localAET, "GINKGO_%IP4");
			}

			this->m_pServiceInstance = new GIL::DICOM::Service(_Std("PACS-Service"));
			this->m_pServiceInstance->SetRole(GIL::DICOM::Service::RT_Acceptor);
			this->m_pServiceInstance->SetAcceptorPort(localPort);
			
			this->m_pServiceInstance->SetLocalAET(localAET);
			this->m_pServiceInstance->Start();

		}

		void PACSController::StopServer()
		{
			if (this->m_pServiceInstance != NULL) {
				unsigned long tid = this->m_pServiceInstance->GetTid();
				GNC::GCS::ThreadController::Stop(tid);
				delete this->m_pServiceInstance;
				this->m_pServiceInstance = NULL;
			}
		}


		void PACSController::GetConnection(void* connectionKey)
		{
			GIL::DICOM::DCMTK::Network::Instance(connectionKey);
		}

		void PACSController::ReleaseConnection(void* connectionKey)
		{
			GIL::DICOM::DCMTK::Network::FreeInstance(connectionKey);
		}

		void PACSController::SubirArchivos(void* /*connectionKey*/, const std::string& serverId, const std::list<std::string> &pathsSubida, GNC::IProxyNotificadorProgreso* pNotificador, TipoTransferSyntaxEnvio transferSyntax)
		{

			GTRACE("PACSController::SubirArchivos( " << serverId.c_str() << ", [...]");
			DicomServerList* listaServidores = DicomServerList::Instance();
			std::string localAET = GNC::Entorno::Instance()->GetDicomLocalAET();
			GIL::DICOM::DicomStoreAssociation asociacion;
			DicomServer* server = listaServidores->GetServer(serverId);
			if (server) {
				LOG_INFO("PACS-STORE", "Enviando al PACS " << serverId << ": " << server->AET << "@" << server->HostName << ":" << server->Port << " PDU=" << server->PDU << ", TLS=" << server->useTLS << ",  User = " << server->pacsUser);
			}
			if (server->useTLS) {				
				asociacion.SetTLS(server->GetCertificate(), server->GetPrivateKey(), server->GetverifyCredentials());
			}
			if (server->GetPACSUser() != "") {
				asociacion.SetUserPass(server->GetPACSUser(), server->GetPACSPass());
			}
			asociacion.Store(pathsSubida,server, localAET, pNotificador, transferSyntax);

		}

		std::string GetMD5(const std::string& cadena)
		{
			unsigned char salida[MD5_DIGEST_LENGTH];
			MD5((const unsigned char*)cadena.c_str(), cadena.size(),salida);

			std::ostringstream cadenaMD5;
			cadenaMD5.setf ( std::ios::hex, std::ios::basefield );
			for(int i = 0; i<MD5_DIGEST_LENGTH; i++)
			{
				cadenaMD5 << (int)salida[i];
			}
			return cadenaMD5.str();
		}

		bool PACSController::GetRutaSerie(const std::string& uidPaciente, const std::string& uidEstudio, const std::string& uidSerie, std::string& ruta, bool crearDirectorios,bool temporal)
		{
			bool correcto = true;
			std::string dicomDirPath = GNC::GCS::HistoryController::Instance()->GetGinkgoDicomDir();
			//md5 de uidpaciente, estudio y serie
			std::stringstream concat;
			if(uidPaciente != "")
				concat << uidPaciente;
			else
				concat << _("Unknown");
			if(uidEstudio != "")
				concat << uidEstudio;
			else
				concat << _("Unknown");
			if(uidSerie != "")
				concat << uidSerie;
			else
				concat << _("Unknown");
			std::string cadena = concat.str();
			std::string resumen = GetMD5(cadena);

			wxString dirRaiz, dirSerie;
			dirRaiz = FROMPATH(dicomDirPath);
			if (!wxDir::Exists(dirRaiz) && crearDirectorios) {
				correcto = correcto && wxFileName::Mkdir(dirRaiz, 0700);
			}

			if(temporal) {
				dirRaiz = dirRaiz + wxFileName::GetPathSeparator(wxPATH_NATIVE) + wxT("TEMP");
				if (!wxDir::Exists(dirRaiz)) {
					correcto = correcto && wxFileName::Mkdir(dirRaiz, 0700);
				}
			}

			dirSerie= dirRaiz + wxFileName::GetPathSeparator(wxPATH_NATIVE) + FROMPATH(resumen);
			if (!wxDir::Exists(dirSerie) && crearDirectorios) {
				correcto = correcto && wxFileName::Mkdir(dirSerie, 0700);
			}

			ruta = TOPATH(dirSerie);
			return correcto;
		}

		bool PACSController::GetRutaImagen(const std::string& uidPaciente, const std::string& uidEstudio, const std::string& uidSerie, const std::string& uidImagen, std::string& ruta, bool crearDirectorios)
		{
			bool correcto = GetRutaSerie(uidPaciente,uidEstudio,uidSerie, ruta, crearDirectorios, false);
			std::ostringstream ostr;
			ostr << ruta << (char)wxFileName::GetPathSeparator(wxPATH_NATIVE)<<GetMD5(uidImagen)<<".dcm";
			ruta = ostr.str();
			return correcto;
		}

		bool PACSController::GetRutaImagenTemp(const std::string& uidPaciente, const std::string& uidEstudio, const std::string& uidSerie, const std::string& uidImagen, std::string& ruta, bool crearDirectorios)
		{
			bool correcto = GetRutaSerie(uidPaciente,uidEstudio,uidSerie, ruta, crearDirectorios, true);
			std::ostringstream ostr;
			ostr << ruta << (char)wxFileName::GetPathSeparator(wxPATH_NATIVE)<<GetMD5(uidImagen)<<".dcm";
			ruta = ostr.str();
			return correcto;
		}

		void PACSController::DumpFileElements(const std::string& rutaFichero, IInspectCallBack* callback)
		{
			DcmFileFormat fileformat;
			if (rutaFichero.empty() || callback == NULL) {
				return;
			}
			OFCondition status = fileformat.loadFile(rutaFichero.c_str());
			if (status.good()) {

				DcmDataset* ds = fileformat.getDataset();

				DcmDataDictionary& globalDataDict = dcmDataDict.wrlock();
				DcmHashDictIterator iter(globalDataDict.normalBegin());
				DcmHashDictIterator end(globalDataDict.normalEnd());

				std::string keyStr;
				std::string descStr;
				std::string valStr;
				for (; iter != end; ++iter) {
					DcmTagKey tagkey = (*iter)->getKey();
					keyStr = tagkey.toString().c_str();
					descStr = (*iter)->getTagName();
					OFString val;
					OFCondition c = ds->findAndGetOFString(tagkey, val);
					if (c.good()) {
						valStr = val.c_str();
						callback->Inspect(keyStr, descStr, valStr);
					}
				}
				dcmDataDict.unlock();
			}
		}


		//region "Métodos de Dicomización"
		/*
		void PACSController::CrearInstanciaDeImportacion(void* ref) {
		m_ListaInstanciasDicomImg2DCM[ref] = new IDICOMImg2DCM();
		}

		void PACSController::LiberarInstanciaDeImportacion(void* ref) {
		ListaInstanciasImg2DCMType::iterator it = m_ListaInstanciasDicomImg2DCM.find(ref);
		if (it != m_ListaInstanciasDicomImg2DCM.end()) {
		delete (*it).second;
		m_ListaInstanciasDicomImg2DCM.erase(it);
		}
		}
		*/

		IDICOMImg2DCM* PACSController::CrearInstanciaDeImportacion() {
			return new DICOMImg2DCM();
		}

		void PACSController::LiberarInstanciaDeImportacion(IDICOMImg2DCM* instancia) {
			DICOMImg2DCM* i = dynamic_cast<DICOMImg2DCM*>(instancia);
			if (i != NULL) {
				delete i;
			}
		}
		//endregion

		//region "Métodos de carga/almacenamiento de DICOMS"
		IDICOMManager* PACSController::CrearInstanciaDeDICOMManager() {
			return new DICOMManager();
		}

		void PACSController::LiberarInstanciaDeDICOMManager(IDICOMManager* instancia) {
			DICOMManager* i = dynamic_cast<DICOMManager*>(instancia);
			if (i != NULL) {
				delete i;
			}
		}

		void  BorrarDirTemp(wxString dirPath)
		{
			//borra en cascada
			if(!wxRmdir(dirPath)){
				//vaciar
				wxDir dir;
				if (dir.Open(dirPath)) {
					wxString fileName;
					bool cont = dir.GetFirst(&fileName);
					while (cont) {
						fileName=dir.GetName()+ wxFileName::GetPathSeparator(wxPATH_NATIVE) +fileName;
						if(wxDir::Exists(fileName)){
							BorrarDirTemp(fileName);
						}else{
							wxRemoveFile(fileName);
						}
						cont = dir.GetNext(&fileName);
					}
				}
			}
			if(wxDir::Exists(dirPath)){
				wxRmdir(dirPath);
			}
		}

		void PACSController::PurgarDirectorioTemporal()
		{
			wxString dirTemp;
			dirTemp = FROMPATH(GNC::GCS::HistoryController::Instance()->GetGinkgoDicomDir());
			if (!wxDir::Exists(dirTemp)) {
				return;
			}

			dirTemp = dirTemp + wxFileName::GetPathSeparator(wxPATH_NATIVE) + wxT("TEMP");
			if (!wxDir::Exists(dirTemp)) {
				return;
			}

			//borrar en profundidad
			BorrarDirTemp(dirTemp);
		}
		//endregion
	};
};