/*
 *
 *  $Id: dicomstoreassociation.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
 *
 *  Code adapted from OsiriX
 *
 */
#define LOGGER "C-STORE"
#include <api/controllers/icontroladorlog.h>
#include <main/controllers/controladorlog.h>
#include <main/controllers/dcmtk/ginkgouid.h>
#include "helpers.h"

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

#include "dicomstoreassociation.h"
#include <api/dicom/imodelodicom.h>

#include <api/iproxynotificadorprogreso.h>
#include <main/controllers/controladorpermisos.h>
#include <main/entorno.h>
#include <iostream>

#include <main/controllers/dcmtk/dicomservers.h>

#include <dcmtk/ofstd/ofstring.h>
#include <dcmtk/dcmnet/dimse.h>
#include <dcmtk/dcmnet/diutil.h>
#include <dcmtk/dcmdata/dcdatset.h>
#include <dcmtk/dcmdata/dcmetinf.h>
#include <dcmtk/dcmdata/dcfilefo.h>
//#include <dcmtk/dcmdata/dcdebug.h>
#include <dcmtk/dcmdata/dcuid.h>
#include <dcmtk/dcmdata/dcdict.h>
#include <dcmtk/dcmdata/dcdeftag.h>
//#include "cmdlnarg.h"
#include <dcmtk/ofstd/ofconapp.h>
#include <dcmtk/dcmdata/dcuid.h>     /* for dcmtk version name */
#include <dcmtk/dcmnet/dicom.h>     /* for DICOM_APPLICATION_REQUESTOR */
#include <dcmtk/dcmdata/dcostrmz.h>  /* for dcmZlibCompressionLevel */
#include <dcmtk/dcmnet/dcasccfg.h>  /* for class DcmAssociationConfiguration */
#include <dcmtk/dcmnet/dcasccff.h>  /* for class DcmAssociationConfigurationFile */

#include <dcmtk/dcmjpeg/djencode.h>  /* for dcmjpeg encoders */
#include <dcmtk/dcmjpeg/djdecode.h>  /* for dcmjpeg encoders */
#include <dcmtk/dcmdata/dcrledrg.h>  /* for DcmRLEDecoderRegistration */
#include <dcmtk/dcmdata/dcrleerg.h>  /* for DcmRLEEncoderRegistration */
#include <dcmtk/dcmjpeg/djrploss.h>
#include <dcmtk/dcmjpeg/djrplol.h>
#include <dcmtk/dcmdata/dcpixel.h>
#include <dcmtk/dcmdata/dcrlerp.h>

#include "tls/tls.h"
#include "tls/gtlslayer.h"
#include <dcmtk/dcmtls/tlstrans.h>

#include "helpercompresion.h"

#ifdef MACRO_QUE_ESTORBA
#define verify MACRO_QUE_ESTORBA
#endif

#include <wx/filename.h>
#include <sstream>

/////////////////////-------------------------DEFINES--------------------/////////////////

namespace GIL {
	namespace DICOM {

		/* default application titles */
#define APPLICATIONTITLE        "GINKGO"
#define PEERAPPLICATIONTITLE    "DCM4CHEE"

		static OFBool opt_verbose = OFTrue;
		static OFBool opt_showPresentationContexts = OFTrue;
		static OFBool opt_debug = OFTrue;
		static OFBool opt_abortAssociation = OFFalse;
		static OFCmdUnsignedInt opt_maxReceivePDULength = ASC_DEFAULTMAXPDU;
		//static OFCmdUnsignedInt opt_maxSendPDULength = 0;
		static E_TransferSyntax opt_networkTransferSyntax = EXS_LittleEndianExplicit;

		//static OFBool opt_haltOnUnsuccessfulStore = OFTrue;
		static OFBool opt_haltOnUnsuccessfulStore = OFFalse;
		static OFBool unsuccessfulStoreEncountered = OFFalse;
		static std::string errorEncontrado;
		int lastStatusCode = STATUS_Success;

		static OFBool opt_proposeOnlyRequiredPresentationContexts = OFFalse;
		static OFBool opt_combineProposedTransferSyntaxes = OFFalse;

		static OFCmdUnsignedInt opt_repeatCount = 1;
		static OFCmdUnsignedInt opt_inventPatientCount = 25;
		static OFCmdUnsignedInt opt_inventStudyCount = 50;
		static OFCmdUnsignedInt opt_inventSeriesCount = 100;
		static OFBool opt_inventSOPInstanceInformation = OFFalse;
		static OFBool opt_correctUIDPadding = OFFalse;
		static OFString patientNamePrefix("OFFIS^TEST_PN_");   // PatientName is PN (maximum 16 chars)
		static OFString patientIDPrefix("PID_"); // PatientID is LO (maximum 64 chars)
		static OFString studyIDPrefix("SID_");   // StudyID is SH (maximum 16 chars)
		static OFString accessionNumberPrefix;  // AccessionNumber is SH (maximum 16 chars)
		//static OFBool opt_secureConnection = OFFalse; /* default: no secure connection */
		//static const char *opt_configFile = NULL;
		//static const char *opt_profileName = NULL;
		T_DIMSE_BlockingMode opt_blockMode = DIMSE_NONBLOCKING;
		int opt_dimse_timeout = 0;
		int opt_acse_timeout = 30;
		int opt_Quality = 100;

		//cancelacion

		typedef struct _StoreCallbackInfo {
			T_ASC_Association* assoc;
		} GetCallbackInfo;

		//progreso
		static GNC::IProxyNotificadorProgreso* m_pNotificadorProgreso = NULL;
		static std::string m_mensaje("");
		typedef struct TMedida{
			time_t m_instante;
			int bytesDescargados;
			float oldTasa;
			TMedida() {
				m_instante = time(NULL);
				bytesDescargados = 0;
				oldTasa = 0.0f;
			}
		} TMedida;
		static TMedida m_medida;

		float TasaTransferencia(int bytesDescargados)
		{
			time_t nuevoInstante = time(NULL);
			const double tiempo = difftime(nuevoInstante,m_medida.m_instante);
			if(tiempo>0.15f){
				if(bytesDescargados > m_medida.bytesDescargados) {
					const double bytesRecibidosPeriodo = bytesDescargados - m_medida.bytesDescargados;
					m_medida.oldTasa = ( ((float)bytesRecibidosPeriodo/1024.0f)/tiempo );
					m_medida.bytesDescargados = bytesDescargados;
					m_medida.m_instante = nuevoInstante;
				}
			}
			return m_medida.oldTasa;
		}

		void ResetearMedida(bool clearTasa = false) {
			m_medida.bytesDescargados =-1;
			m_medida.m_instante = time(NULL);
			if(clearTasa) {
				m_medida.oldTasa=0.0f;
			}
		}

		static void errmsg(const std::string& mensaje, bool parar)
		{
			LOG_ERROR(LOGGER,mensaje);
			if(parar) {
				throw GIL::DICOM::PACSException(mensaje);
			}
		}

		/*
		 static void
		 errmsg(const OFString& message, bool parar)
		 {
		 std::string mensaje(message.c_str());
		 errmsg(mensaje,parar);
		 }
		 */

		static OFBool isaListMember(OFList<OFString>& lst, OFString& s)
		{
			OFListIterator(OFString) cur = lst.begin();
			OFListIterator(OFString) end = lst.end();

			OFBool found = OFFalse;

			while (cur != end && !found) {

				found = (s == *cur);

				++cur;
			}

			return found;
		}

		static OFCondition addPresentationContext(T_ASC_Parameters *params,
							   int presentationContextId, const OFString& abstractSyntax,
							   const OFString& transferSyntax,
							   T_ASC_SC_ROLE proposedRole = ASC_SC_ROLE_DEFAULT)
		{
			const char* c_p = transferSyntax.c_str();
			OFCondition cond = ASC_addPresentationContext(params, presentationContextId,
														  abstractSyntax.c_str(), &c_p, 1, proposedRole);
			return cond;
		}

		static OFCondition addPresentationContext(T_ASC_Parameters *params,
							   int presentationContextId, const OFString& abstractSyntax,
							   const OFList<OFString>& transferSyntaxList,
							   T_ASC_SC_ROLE proposedRole = ASC_SC_ROLE_DEFAULT)
		{
			// create an array of supported/possible transfer syntaxes
			const char** transferSyntaxes = new const char*[transferSyntaxList.size()];
			int transferSyntaxCount = 0;
			OFListConstIterator(OFString) s_cur = transferSyntaxList.begin();
			OFListConstIterator(OFString) s_end = transferSyntaxList.end();
			while (s_cur != s_end) {
				transferSyntaxes[transferSyntaxCount++] = (*s_cur).c_str();
				++s_cur;
			}

			OFCondition cond = ASC_addPresentationContext(params, presentationContextId,
														  abstractSyntax.c_str(), transferSyntaxes, transferSyntaxCount, proposedRole);

			delete[] transferSyntaxes;
			return cond;
		}

		static OFCondition addStoragePresentationContexts(T_ASC_Parameters *params, OFList<OFString>& sopClasses)
		{
			/*
			 * Each SOP Class will be proposed in two presentation contexts (unless
			 * the opt_combineProposedTransferSyntaxes global variable is true).
			 * The command line specified a preferred transfer syntax to use.
			 * This prefered transfer syntax will be proposed in one
			 * presentation context and a set of alternative (fallback) transfer
			 * syntaxes will be proposed in a different presentation context.
			 *
			 * Generally, we prefer to use Explicitly encoded transfer syntaxes
			 * and if running on a Little Endian machine we prefer
			 * LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax.
			 * Some SCP implementations will just select the first transfer
			 * syntax they support (this is not part of the standard) so
			 * organise the proposed transfer syntaxes to take advantage
			 * of such behaviour.
			 */

			// Which transfer syntax was preferred on the command line
			OFString preferredTransferSyntax;
			if (opt_networkTransferSyntax == EXS_Unknown) {
				/* gLocalByteOrder is defined in dcxfer.h */
				if (gLocalByteOrder == EBO_LittleEndian) {
					/* we are on a little endian machine */
					preferredTransferSyntax = UID_LittleEndianExplicitTransferSyntax;
				} else {
					/* we are on a big endian machine */
					preferredTransferSyntax = UID_BigEndianExplicitTransferSyntax;
				}
			} else {
				DcmXfer xfer(opt_networkTransferSyntax);
				preferredTransferSyntax = xfer.getXferID();
			}

			OFListIterator(OFString) s_cur;
			OFListIterator(OFString) s_end;


			OFList<OFString> fallbackSyntaxes;
			fallbackSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
			fallbackSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
			fallbackSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
			// Remove the preferred syntax from the fallback list
			fallbackSyntaxes.remove(preferredTransferSyntax);
			// If little endian implicit is preferred then we don't need any fallback syntaxes
			// because it is the default transfer syntax and all applications must support it.
			if (opt_networkTransferSyntax == EXS_LittleEndianImplicit) {
				fallbackSyntaxes.clear();
			}

			// created a list of transfer syntaxes combined from the preferred and fallback syntaxes
			OFList<OFString> combinedSyntaxes;
			s_cur = fallbackSyntaxes.begin();
			s_end = fallbackSyntaxes.end();
			combinedSyntaxes.push_back(preferredTransferSyntax);
			while (s_cur != s_end)
			{
				if (!isaListMember(combinedSyntaxes, *s_cur)) combinedSyntaxes.push_back(*s_cur);
				++s_cur;
			}

			if (!opt_proposeOnlyRequiredPresentationContexts) {
				// add the (short list of) known storage sop classes to the list
				// the array of Storage SOP Class UIDs comes from dcuid.h
				
				//FIXME: ÑAPA HECHA PARA QUE PUEDA SUBIR VIDEOS(EL -1 Y EL PUSH DE DESPUES)
				for (int i=0; i<numberOfDcmShortSCUStorageSOPClassUIDs-1; i++) {
					sopClasses.push_back(dcmShortSCUStorageSOPClassUIDs[i]);
				}
				//	UID_VideoPhotographicImageStorage
				sopClasses.push_back("1.2.840.10008.5.1.4.1.1.77.1.4.1");
			}

			// thin out the sop classes to remove any duplicates.
			OFList<OFString> sops;
			s_cur = sopClasses.begin();
			s_end = sopClasses.end();
			while (s_cur != s_end) {
				if (!isaListMember(sops, *s_cur)) {
					sops.push_back(*s_cur);
				}
				++s_cur;
			}

			// add a presentations context for each sop class / transfer syntax pair
			OFCondition cond = EC_Normal;
			int pid = 1; // presentation context id
			s_cur = sops.begin();
			s_end = sops.end();
			while (s_cur != s_end && cond.good()) {

				if (pid > 255) {
					LOG_WARN("C-GET", "Too many PresentationContexts setted");
					return cond;
				}

				if (opt_combineProposedTransferSyntaxes) {
					cond = addPresentationContext(params, pid, *s_cur, combinedSyntaxes);
					pid += 2;   /* only odd presentation context id's */
				} else {

					// sop class with preferred transfer syntax
					cond = addPresentationContext(params, pid, *s_cur, preferredTransferSyntax);
					pid += 2;   /* only odd presentation context id's */

					if (fallbackSyntaxes.size() > 0) {
						if (pid > 255) {
							//errmsg("Demasiados contextos de presentacion");
							return ASC_BADPRESENTATIONCONTEXTID;
						}

						// sop class with fallback transfer syntax
						cond = addPresentationContext(params, pid, *s_cur, fallbackSyntaxes);
						pid += 2;       /* only odd presentation context id's */
					}
				}
				++s_cur;
			}

			return cond;
		}

		static OFString intToString(int i)
		{
			char numbuf[32];
			sprintf(numbuf, "%d", i);
			return numbuf;
		}

		static int secondsSince1970()
		{
			time_t t = time(NULL);
			return (int)t;
		}

		static OFBool updateStringAttributeValue(DcmItem* dataset, const DcmTagKey& key, OFString& value)
		{
			DcmStack stack;
			DcmTag tag(key);

			OFCondition cond = EC_Normal;
			cond = dataset->search(key, stack, ESM_fromHere, OFFalse);
			if (cond != EC_Normal) {
				LOG_DEBUG(LOGGER,"error: updateStringAttributeValue: cannot find: " << tag.getTagName() << " " << key << ": "<< cond.text());
				return OFFalse;
			}

			DcmElement* elem = (DcmElement*) stack.top();

			DcmVR vr(elem->ident());
			if (elem->getLength() > vr.getMaxValueLength()) {
				LOG_DEBUG(LOGGER,"error: updateStringAttributeValue: INTERNAL ERROR: " << tag.getTagName()<< " " << key << ": value too large (max "<< vr.getMaxValueLength() << ") for " << vr.getVRName() << " value: " << value );
				return OFFalse;
			}

			cond = elem->putOFStringArray(value);
			if (cond != EC_Normal) {
				LOG_DEBUG(LOGGER, "error: updateStringAttributeValue: cannot put string in attribute: " << tag.getTagName()<< " " << key << ": "<< cond.text());
				return OFFalse;
			}

			return OFTrue;
		}

		static void replaceSOPInstanceInformation(DcmDataset* dataset)
		{
			static OFCmdUnsignedInt patientCounter = 0;
			static OFCmdUnsignedInt studyCounter = 0;
			static OFCmdUnsignedInt seriesCounter = 0;
			static OFCmdUnsignedInt imageCounter = 0;
			static OFString seriesInstanceUID;
			static OFString seriesNumber;
			static OFString studyInstanceUID;
			static OFString studyID;
			static OFString accessionNumber;
			static OFString patientID;
			static OFString patientName;

			if (seriesInstanceUID.length() == 0) seriesInstanceUID = GIL::DICOM::MakeUID(GIL::DICOM::GUID_SeriesRoot, (int)seriesCounter).c_str();
			if (seriesNumber.length() == 0) seriesNumber = intToString((int)seriesCounter);
			if (studyInstanceUID.length() == 0) studyInstanceUID = GIL::DICOM::MakeUID(GIL::DICOM::GUID_StudyRoot, (int)studyCounter).c_str();
			if (studyID.length() == 0) studyID = studyIDPrefix + intToString((int)secondsSince1970()) + intToString((int)studyCounter);
			if (accessionNumber.length() == 0) accessionNumber = accessionNumberPrefix + intToString(secondsSince1970()) + intToString((int)studyCounter);
			if (patientID.length() == 0) patientID = patientIDPrefix + intToString(secondsSince1970()) + intToString((int)patientCounter);
			if (patientName.length() == 0) patientName = patientNamePrefix + intToString(secondsSince1970()) + intToString((int)patientCounter);

			if (imageCounter >= opt_inventSeriesCount) {
				imageCounter = 0;
				seriesCounter++;
				seriesInstanceUID = GIL::DICOM::MakeUID(GIL::DICOM::GUID_SeriesRoot, (int)seriesCounter).c_str();
				seriesNumber = intToString((int)seriesCounter);
			}
			if (seriesCounter >= opt_inventStudyCount) {
				seriesCounter = 0;
				studyCounter++;				
				studyInstanceUID = GIL::DICOM::MakeUID(GIL::DICOM::GUID_StudyRoot, (int)studyCounter).c_str();
				studyID = studyIDPrefix + intToString(secondsSince1970()) + intToString((int)studyCounter);
				accessionNumber = accessionNumberPrefix + intToString(secondsSince1970()) + intToString((int)studyCounter);
			}
			if (studyCounter >= opt_inventPatientCount) {
				// we create as many patients as necessary */
				studyCounter = 0;
				patientCounter++;
				patientID = patientIDPrefix + intToString(secondsSince1970()) + intToString((int)patientCounter);
				patientName = patientNamePrefix + intToString(secondsSince1970()) + intToString((int)patientCounter);
			}

			OFString sopInstanceUID = GIL::DICOM::MakeUID(GIL::DICOM::GUID_InstanceRoot, (int)imageCounter).c_str();
			OFString imageNumber = intToString((int)imageCounter);

			if (opt_debug) {
				LOG_DEBUG(LOGGER, "Inventing Identifying Information (" <<
						  "pa" << patientCounter << ", st" << studyCounter <<
						  ", se" << seriesCounter << ", im" << imageCounter << "): " << std::endl
						  << "  PatientName=" << patientName << std::endl
						  << "  PatientID=" << patientID << std::endl
						  << "  StudyInstanceUID=" << studyInstanceUID << std::endl
						  << "  StudyID=" << studyID << std::endl
						  << "  SeriesInstanceUID=" << seriesInstanceUID << std::endl
						  << "  SeriesNumber=" << seriesNumber << std::endl
						  << "  SOPInstanceUID=" << sopInstanceUID << std::endl
						  << "  ImageNumber=" << imageNumber << std::endl);
			}

			updateStringAttributeValue(dataset, DCM_PatientName, patientName);
			updateStringAttributeValue(dataset, DCM_PatientID, patientID);
			updateStringAttributeValue(dataset, DCM_StudyInstanceUID, studyInstanceUID);
			updateStringAttributeValue(dataset, DCM_StudyID, studyID);
			updateStringAttributeValue(dataset, DCM_SeriesInstanceUID, seriesInstanceUID);
			updateStringAttributeValue(dataset, DCM_SeriesNumber, seriesNumber);
			updateStringAttributeValue(dataset, DCM_SOPInstanceUID, sopInstanceUID);
			updateStringAttributeValue(dataset, DCM_InstanceNumber, imageNumber);

			imageCounter++;
		}

		// Conversión de transferencias
		static OFBool decompressFile(DcmFileFormat fileformat, const char * fname, const char *outfname)
		{
			OFBool status = true;
			OFCondition cond;
			DcmXfer filexfer(fileformat.getDataset()->getOriginalXfer());

			if (filexfer.getXfer() == EXS_JPEG2000LosslessOnly || filexfer.getXfer() == EXS_JPEG2000)
			{
				status = DecompressJPEG2000(fname, outfname);
				LOG_WARN(LOGGER,"The file is being uncompressed from JPG2000. Some tags could be lost");
			}
			else {

				DcmDataset *dataset = fileformat.getDataset();

				// decompress data set if compressed
				dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);

				// check if everything went well
				if (dataset->canWriteXfer(EXS_LittleEndianExplicit))
				{
					fileformat.loadAllDataIntoMemory();
					unlink( outfname);
					cond = fileformat.saveFile( outfname, EXS_LittleEndianExplicit);
					status =  (cond.good()) ? true : false;
				}
				else
					status = false;
			}


			return status;
		}

		static OFBool compressFile(DcmFileFormat fileformat, const char * fname, const char *outfname)
		{
			OFCondition cond;
			OFBool status = true;
			DcmXfer filexfer(fileformat.getDataset()->getOriginalXfer());


			if (opt_networkTransferSyntax == EXS_JPEG2000)
			{
				status = CompressJPEG2000(fname, outfname);
				LOG_WARN(LOGGER,"Se ha comprimido a JPEG2000, podrían perderse algunos tags");
			}
			else if  (opt_networkTransferSyntax == EXS_JPEG2000LosslessOnly)
			{
				status = CompressJPEG2000(fname, outfname);
				LOG_WARN(LOGGER,"Se ha comprimido a JPEG2000, podrían perderse algunos tags");
			}
			else
			{
				DcmDataset *dataset = fileformat.getDataset();
				DcmItem *metaInfo = fileformat.getMetaInfo();
				DcmRepresentationParameter *params;
				DJ_RPLossy lossyParams(opt_Quality);
				DcmRLERepresentationParameter rleParams;
				DJ_RPLossless losslessParams; // codec parameters, we use the defaults
				if (opt_networkTransferSyntax == EXS_JPEGProcess14SV1TransferSyntax)
					params = &losslessParams;
				else if (opt_networkTransferSyntax == EXS_JPEGProcess2_4TransferSyntax)
					params = &lossyParams;
				else if (opt_networkTransferSyntax == EXS_RLELossless)
					params = &rleParams;
				else
					params = &losslessParams;


				// this causes the lossless JPEG version of the dataset to be created
				dataset->chooseRepresentation(opt_networkTransferSyntax, params);

				// check if everything went well
				if (dataset->canWriteXfer(opt_networkTransferSyntax))
				{
					// force the meta-header UIDs to be re-generated when storing the file
					// since the UIDs in the data set may have changed
					delete metaInfo->remove(DCM_MediaStorageSOPClassUID);
					delete metaInfo->remove(DCM_MediaStorageSOPInstanceUID);

					// store in lossless JPEG format

					fileformat.loadAllDataIntoMemory();

					unlink( outfname);

					cond = fileformat.saveFile( outfname, opt_networkTransferSyntax);
					status =  (cond.good()) ? true : false;
				}
				else {
					status = false;
				}
			}

			return status;
		}


		static long seed = 0;


		static OFCondition storeSCU(T_ASC_Association * assoc, const char *fname)
		/*
		 * This function will read all the information from the given file,
		 * figure out a corresponding presentation context which will be used
		 * to transmit the information over the network to the SCP, and it
		 * will finally initiate the transmission of all data to the SCP.
		 *
		 * Parameters:
		 *   assoc - [in] The association (network connection to another DICOM application).
		 *   fname - [in] Name of the file which shall be processed.
		 */
		{
			DIC_US msgId = assoc->nextMsgID++;
			T_ASC_PresentationContextID presId;
			T_DIMSE_C_StoreRQ req;
			T_DIMSE_C_StoreRSP rsp;
			DIC_UI sopClass;
			DIC_UI sopInstance;
			std::ostringstream outfnameStringStream;
			_StoreCallbackInfo storeCallbackData;

			outfnameStringStream << GNC::Entorno::Instance()->GetGinkgoTempDir().c_str() << (char) wxFileName::GetPathSeparator(wxPATH_NATIVE) << seed++ << ".dcm";

			std::string outfname = outfnameStringStream.str();

			LOG_DEBUG(LOGGER, "tempfile: [" << outfname.c_str() << "]" );

			//sprintf( outfname, "%s%ld.dcm",  "D:\\temp\\", seed++);

			unsuccessfulStoreEncountered = OFTrue; // assumption

			if (opt_debug) {
				LOG_DEBUG(LOGGER, "--------------------------\nSending file: "<< fname);
			}

			/* read information from file. After the call to DcmFileFormat::loadFile(...) the information */
			/* which is encapsulated in the file will be available through the DcmFileFormat object. */
			/* In detail, it will be available through calls to DcmFileFormat::getMetaInfo() (for */
			/* meta header information) and DcmFileFormat::getDataset() (for data set information). */
			DcmFileFormat dcmff;
			OFCondition cond = dcmff.loadFile(fname);

			/* NO LO PONEMOS A FUEGO COMPRIMIMOS Y DESCOMPRIMIMOS
			 opt_networkTransferSyntax = dcmff.getDataset()->getOriginalXfer();
			 */

			/* figure out if an error occured while the file was read*/
			if (cond.bad()) {
				LOG_DEBUG(LOGGER, "Bad DICOM file " << fname);
				return cond;
			}

			/* if required, invent new SOP instance information for the current data set (user option) */
			if (opt_inventSOPInstanceInformation) {
				replaceSOPInstanceInformation(dcmff.getDataset());
			}

			/* figure out which SOP class and SOP instance is encapsulated in the file */
			if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(),
													 sopClass, sopInstance, opt_correctUIDPadding)) {
				LOG_DEBUG(LOGGER, "No SOP Class & Instance UIDs in file " << fname);
				return DIMSE_BADDATA;
			}

			/* figure out which of the accepted presentation contexts should be used */
			DcmXfer filexfer(dcmff.getDataset()->getOriginalXfer());

			/* special case: if the file uses an unencapsulated transfer syntax (uncompressed
			 * or deflated explicit VR) and we prefer deflated explicit VR, then try
			 * to find a presentation context for deflated explicit VR first.
			 */
			if (filexfer.isNotEncapsulated() &&
				opt_networkTransferSyntax == EXS_DeflatedLittleEndianExplicit)
			{
				filexfer = EXS_DeflatedLittleEndianExplicit;
			}


			/************* do on the fly conversion here*********************/

			//printf("on the fly conversion\n");
			//we have a valid presentation ID,.Chaeck and see if file is consistent with it
			DcmXfer preferredXfer(opt_networkTransferSyntax);

			OFBool status = false;
			presId = ASC_findAcceptedPresentationContextID(assoc, sopClass, preferredXfer.getXferID());
			T_ASC_PresentationContext pc;
			ASC_findAcceptedPresentationContext(assoc->params, presId, &pc);
			DcmXfer proposedTransfer(pc.acceptedTransferSyntax);

			status = true;

			if (presId != 0)
			{
				if (filexfer.isNotEncapsulated() && proposedTransfer.isNotEncapsulated()) {
					// do nothing
					status = true;
				}
				else if (filexfer.isEncapsulated() && proposedTransfer.isNotEncapsulated()) {
					status = decompressFile(dcmff, fname, outfname.c_str());
				}
				else if (filexfer.isNotEncapsulated() && proposedTransfer.isEncapsulated()) {
					status = compressFile(dcmff, fname, outfname.c_str());
				}
				else if (filexfer.getXfer() != opt_networkTransferSyntax)
				{
					// The file is already compressed, we will re-compress the file.....
					//E_TransferSyntax fileTS = filexfer.getXfer();

					if( (filexfer.getXfer() == EXS_JPEG2000LosslessOnly && preferredXfer.getXfer() == EXS_JPEG2000) ||
					   (filexfer.getXfer() == EXS_JPEG2000 && preferredXfer.getXfer() == EXS_JPEG2000LosslessOnly))
					{
					}
					else {
						LOG_WARN(LOGGER,"Recompressing files that are already compressed, you should optimize your ts parameters to avoid this: presentation for syntax:" << dcmFindNameOfUID(filexfer.getXferID()) << " -> " << dcmFindNameOfUID(preferredXfer.getXferID()));
					}
					status = compressFile(dcmff, fname, outfname.c_str());
				}
			}
			else
				status = false;


			if (status)
			{
				cond = 	dcmff.loadFile( outfname.c_str());
				filexfer = dcmff.getDataset()->getOriginalXfer();

				/* figure out which SOP class and SOP instance is encapsulated in the file */
				if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(),
														 sopClass, sopInstance, opt_correctUIDPadding)) {
					LOG_DEBUG(LOGGER, "No SOP Class & Instance UIDs in file " << outfname);
					return DIMSE_BADDATA;
				}

				fname = outfname.c_str();
			} else {
				const char *modalityName = dcmSOPClassUIDToModality(sopClass);
				if (!modalityName) modalityName = dcmFindNameOfUID(sopClass);
				if (!modalityName) modalityName = "unknown SOP class";
				LOG_DEBUG(LOGGER, "No presentation context for:  " << modalityName << "  "<< sopClass);
				std::ostringstream ostr;
				ostr << _Std("Ginkgo CADx is unable to compress from ") << filexfer.getXferName() << _Std(" to ") << proposedTransfer.getXferName();
				errmsg(ostr.str(),true);
				return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
			}

			if (filexfer.getXfer() != EXS_Unknown)
				presId = ASC_findAcceptedPresentationContextID(assoc, sopClass, filexfer.getXferID());
			else
				presId = ASC_findAcceptedPresentationContextID(assoc, sopClass);

			if (presId == 0) {
				const char *modalityName = dcmSOPClassUIDToModality(sopClass);
				if (!modalityName) modalityName = dcmFindNameOfUID(sopClass);
				if (!modalityName) modalityName = "unknown SOP class";
				LOG_DEBUG(LOGGER, "No presentation context for:  " << modalityName << "  "<< sopClass);
				return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
			}

			/* if required, dump general information concerning transfer syntaxes */
			if (opt_verbose) {
				DcmXfer fileTransfer(dcmff.getDataset()->getOriginalXfer());
				T_ASC_PresentationContext pc;
				ASC_findAcceptedPresentationContext(assoc->params, presId, &pc);
				DcmXfer netTransfer(pc.acceptedTransferSyntax);
				LOG_DEBUG(LOGGER,"Transfer " << dcmFindNameOfUID(fileTransfer.getXferID()) << " -> " << dcmFindNameOfUID(netTransfer.getXferID()));
			}

			/* prepare the transmission of data */
			bzero((char*)&req, sizeof(req));
			req.MessageID = msgId;
			strcpy(req.AffectedSOPClassUID, sopClass);
			strcpy(req.AffectedSOPInstanceUID, sopInstance);
			req.DataSetType = DIMSE_DATASET_PRESENT;
			req.Priority = DIMSE_PRIORITY_LOW;

			/* if required, dump some more general information */
			if (opt_debug) {
				LOG_DEBUG(LOGGER,"Store SCU RQ: MsgID (" <<msgId <<") "<< dcmSOPClassUIDToModality(sopClass));
			}

			/* finally conduct transmission of data */
			{
				storeCallbackData.assoc = assoc;
				DcmDataset *statusDetail = NULL;
				cond = DIMSE_storeUser(assoc, presId, &req,
									   NULL, dcmff.getDataset(), GIL::DICOM::DicomStoreAssociation::progressCallback, &storeCallbackData,
									   opt_blockMode, opt_dimse_timeout,
									   &rsp, &statusDetail, NULL, OFStandard::getFileSize(fname));

				/*
				 * If store command completed normally, with a status
				 * of success or some warning then the image was accepted.
				 */
				if (cond == EC_Normal && (rsp.DimseStatus == STATUS_Success || DICOM_WARNING_STATUS(rsp.DimseStatus))) {
					unsuccessfulStoreEncountered = OFFalse;
					/* dump status detail information if there is some */
					if (statusDetail != NULL) {
						LOG_DEBUG(LOGGER, "DIMSE_storeUser(): Estado: " << std::endl << DumpDataset(statusDetail));
					}
				} else {
					//error!!!
					std::stringstream os;
					os<< "Error al enviar el archivo";
					if(statusDetail!=NULL)
						statusDetail->print(os);
					os << ": " << cond.text();
					errorEncontrado = os.str();
					LOG_ERROR(LOGGER, errorEncontrado);
				}
				delete statusDetail;
				statusDetail = NULL;
			}

			/* remember the response's status for later transmissions of data */
			lastStatusCode = rsp.DimseStatus;

			/* dump some more general information */
			if (cond == EC_Normal)
			{
				if (opt_debug) {
					LOG_DEBUG(LOGGER,"Fichero Subido Correctamente " << fname);
				}
			}
			else
			{
				LOG_DEBUG(LOGGER,"Store failed: " << fname);
				DimseCondition::dump(cond);
			}

			if( status)
			{
				// We created a temporary file. Delete it now.

				unlink( outfname.c_str());
			}

			/* return */
			return cond;
		}


		static OFCondition cstore(T_ASC_Association * assoc, const OFString& fname)
		/*
		 * This function will process the given file as often as is specified by opt_repeatCount.
		 * "Process" in this case means "read file, send C-STORE-RQ, receive C-STORE-RSP".
		 *
		 * Parameters:
		 *   assoc - [in] The association (network connection to another DICOM application).
		 *   fname - [in] Name of the file which shall be processed.
		 */
		{
			OFCondition cond = EC_Normal;

			/* opt_repeatCount specifies how many times a certain file shall be processed */
			int n = (int)opt_repeatCount;

			/* as long as no error occured and the counter does not equal 0 */
			while ((cond.good()) && n-- && !(opt_haltOnUnsuccessfulStore && unsuccessfulStoreEncountered))
			{
				/* process file (read file, send C-STORE-RQ, receive C-STORE-RSP) */
				cond = storeSCU(assoc, fname.c_str());
			}

			// we don't want to return an error code if --no-halt was specified.
			if (! opt_haltOnUnsuccessfulStore)
			{
				cond = EC_Normal;
			}

			/* return result value */
			return cond;
		}

		DicomStoreAssociation::DicomStoreAssociation() :
			m_Validate(false),
			m_TLS(false),
			m_UseUserPass(false),
			assoc(NULL),
			net(NULL)
		{
			
		}

		DicomStoreAssociation::~DicomStoreAssociation()
		{
#ifdef _WIN32
			WSACleanup();
#endif
			//no limpiamos no sea que los haya registrado otro que no seamos nosotros!!!
			FreeMemory();
		}

		void DicomStoreAssociation::Store(ListaRutas listaFicheros, DicomServer* server, std::string nombreAETLocal,GNC::IProxyNotificadorProgreso* pNotificador, GIL::DICOM::TipoTransferSyntaxEnvio transferSyntax)
		{
			if (listaFicheros.size() == 0)
				return;

			m_pNotificadorProgreso = pNotificador;


			std::string nombreAETPar = server->AET;
			opt_maxReceivePDULength = server->PDU;
			std::string host = server->HostName;
			unsigned int puerto = server->Port;
			//TIMEOUT 60 SEGUNDOS
			unsigned int dicomtimeout = 60;
			bool _shouldAbort=false;
			DcmFileFormat fileformat;
			errorEncontrado ="";

			E_TransferSyntax transferSyntaxFichero;

			{
				DcmFileFormat dcmff;
				OFCondition cond;
				cond = dcmff.loadFile(listaFicheros.front().c_str(),EXS_Unknown,EGL_noChange,DCM_TagInfoLength);
				//dcmff.loadAllDataIntoMemory();
				if (cond.bad()) {
					errmsg(std::string("no se ha podido leer el dicom"),true);
				} else {
					DcmElement* elemento;
					cond = dcmff.getDataset()->findAndGetElement(DcmTagKey(0x0010,0x0020),elemento);
					if(cond.bad()) {
						std::stringstream strStream;
						strStream << "Es necesario que este establecido el nombre y el ID del paciente";
						errmsg(strStream.str(),true);
						LOG_ERROR("C-STORE", strStream.str().c_str());
					}
					cond = dcmff.getDataset()->findAndGetElement(DcmTagKey(0x0010,0x0010),elemento);
					if(cond.bad()) {
						std::stringstream strStream;
						strStream << "Es necesario que este establecido el nombre y el ID del paciente";
						errmsg(strStream.str(),true);
						LOG_ERROR("C-STORE", strStream.str().c_str());
					}
				}
				transferSyntaxFichero = dcmff.getDataset()->getOriginalXfer();
			}


			//se pilla el nombre del estudio y dlpaciente... ahora se comienza
			OFCondition cond;
			const char *opt_peer = NULL;
			OFCmdUnsignedInt opt_port = 104;
			const char *opt_peerTitle = PEERAPPLICATIONTITLE;
			const char *opt_ourTitle = APPLICATIONTITLE;

			if(nombreAETPar.size()>0){
				opt_peerTitle = nombreAETPar.c_str();
			}

			if(nombreAETLocal.size()>0){
				opt_ourTitle = nombreAETLocal.c_str();
			}

			OFList<OFString> fileNameList;       // list of files to transfer to SCP
			OFList<OFString> sopClassUIDList;    // the list of sop classes

			T_ASC_Parameters *params;
			DIC_NODENAME localHost;
			DIC_NODENAME peerHost;
			DcmAssociationConfiguration asccfg; // handler for association configuration profiles

			opt_peer = host.c_str();
			opt_port = puerto;

			//verbose option set to true for now
			opt_showPresentationContexts = OFFalse;

			//debug code off for now
			opt_debug = GNC::GCS::IControladorLog::Instance()->GetActiveLogLevel() == GNC::GCS::IControladorLog::DebugLog;

			switch (transferSyntax) {
				case SendExplicitLittleEndian:
					opt_networkTransferSyntax = EXS_LittleEndianExplicit;
					break;
				case SendJPEG2000Lossless:
					opt_networkTransferSyntax = EXS_JPEG2000LosslessOnly;
					opt_Quality = 0;
					break;
				case SendJPEG2000Lossy10:
					opt_networkTransferSyntax = EXS_JPEG2000;
					opt_Quality = 1;
					break;
				case SendJPEG2000Lossy20:
					opt_networkTransferSyntax = EXS_JPEG2000;
					opt_Quality = 2;
					break;
				case SendJPEG2000Lossy50:
					opt_networkTransferSyntax = EXS_JPEG2000;
					opt_Quality = 3;
					break;
				case SendJPEGLossless:
					opt_networkTransferSyntax = EXS_JPEGProcess14SV1TransferSyntax;
					break;
				case SendJPEGLossy9:
					opt_networkTransferSyntax = EXS_JPEGProcess2_4TransferSyntax;
					opt_Quality = 90;
					break;
				case SendJPEGLossy8:
					opt_networkTransferSyntax = EXS_JPEGProcess2_4TransferSyntax;
					opt_Quality = 80;
					break;
				case SendJPEGLossy7:
					opt_networkTransferSyntax = EXS_JPEGProcess2_4TransferSyntax;
					opt_Quality = 70;
					break;
				case SendImplicitLittleEndian:
					opt_networkTransferSyntax = EXS_LittleEndianImplicit;
					break;
					/*	case SendRLE:
					 opt_networkTransferSyntax = EXS_RLELossless;
					 break;
					 case SendExplicitBigEndian:
					 opt_networkTransferSyntax = EXS_BigEndianExplicit;
					 break;
					 case SendBZip:
					 opt_networkTransferSyntax = EXS_DeflatedLittleEndianExplicit;
					 break;*/
				case SendDefault:
					opt_networkTransferSyntax = transferSyntaxFichero;
					//FIXME esta ñapa es necesaria porque el pacs de IRE no acepta baseline
					if (GNC::GCS::IControladorPermisos::Instance()->Get("core.pacs.limits","force_lossless")) {
						if (opt_networkTransferSyntax == EXS_JPEGProcess1TransferSyntax)
						{
							opt_networkTransferSyntax = EXS_JPEGProcess14SV1TransferSyntax;
						}
					}
					//
					break;
			}

			opt_proposeOnlyRequiredPresentationContexts = OFFalse;

			dcmEnableUnknownVRGeneration.set(OFTrue);
			dcmEnableUnlimitedTextVRGeneration.set(OFTrue);

			//	enable-new-vr
			dcmEnableUnknownVRGeneration.set(OFTrue);
			dcmEnableUnlimitedTextVRGeneration.set(OFTrue);

			//Timeout
			OFCmdSignedInt opt_timeout = dicomtimeout;
			dcmConnectionTimeout.set((Sint32) opt_timeout);

			//acse-timeout
			opt_acse_timeout = OFstatic_cast(int, opt_timeout);

			//dimse-timeout
			opt_dimse_timeout = OFstatic_cast(int, opt_timeout);
			opt_blockMode = DIMSE_NONBLOCKING;


			int paramCount = listaFicheros.size();
			const char *currentFilename = NULL;
			char sopClassUID[128];
			char sopInstanceUID[128];
			OFBool ignoreName;

			ListaRutas::iterator it = listaFicheros.begin();

			for (int i=0; i < paramCount; i++, ++it)
			{
				ignoreName = OFFalse;
				currentFilename = (*it).c_str();

				if (access(currentFilename, R_OK) < 0)
				{
					std::stringstream strStream;
					strStream << "No se puede acceder al fichero: " << currentFilename;
					errmsg(strStream.str(), opt_haltOnUnsuccessfulStore);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}
				else
				{
					if (!DU_findSOPClassAndInstanceInFile(currentFilename, sopClassUID, sopInstanceUID))
					{
						ignoreName = OFTrue;
						std::stringstream strStream;
						strStream << "SOP class (o instance) no establecido en fichero: " << currentFilename;
						errmsg(strStream.str(), opt_haltOnUnsuccessfulStore);
						LOG_ERROR("C-STORE", strStream.str().c_str());
					}
					else if (!dcmIsaStorageSOPClassUID(sopClassUID))
					{
						ignoreName = OFTrue;
						std::stringstream strStream;
						strStream << "Storage sop class desconocido en fichero: " << currentFilename << ": " << sopClassUID;
						errmsg(strStream.str(), opt_haltOnUnsuccessfulStore);
						LOG_ERROR("C-STORE", strStream.str().c_str());
					}
					else
					{
						bool found = false;
						for (OFIterator<OFString> itsc =sopClassUIDList.begin(); itsc != sopClassUIDList.end(); ++itsc) {
							if ((*itsc).compare(sopClassUID) == 0) {
								found = true;
								break;
							}
						}
						if (!found) {
							sopClassUIDList.push_back(sopClassUID);
						}
					}
					if (!ignoreName) fileNameList.push_back(currentFilename);
				}
			}

			/* make sure data dictionary is loaded */
			if (!dcmDataDict.isDictionaryLoaded()) {
				LOG_WARN(LOGGER, "Warning: no data dictionary loaded, check environment variable");
			}

#ifdef _WIN32
			WORD wVersionRequested;
			WSADATA wsaData;

			wVersionRequested = MAKEWORD(1, 1);

			WSAStartup(wVersionRequested, &wsaData);
#endif

			/* initialize network, i.e. create an instance of T_ASC_Network*. */

			cond = ASC_initializeNetwork(NET_REQUESTOR, puerto, opt_acse_timeout, &net);
			if (cond.bad())
			{
				DimseCondition::dump(cond);
				std::stringstream strStream;
				strStream << "DICOM Network Failure (storescu) ASC_initializeNetwork; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
				LOG_ERROR("C-STORE", strStream.str().c_str());
				errmsg(strStream.str(),true);
			}

			GTLSTransportLayer *tLayer = NULL;


			if (IsSecure())
			{
				tLayer = new GTLSTransportLayer(DICOM_APPLICATION_REQUESTOR, NULL);
				if (tLayer == NULL)
				{
					DimseCondition::dump(cond);
					std::stringstream strStream;
					strStream << "Unable to create TLS transport layer";
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}
				tLayer->setCertificateFromString(GetCliCert());
				tLayer->setPrivateKeyFromString(GetCliKey());

				if (! tLayer->checkPrivateKeyMatchesCertificate())
				{
					std::stringstream strStream;
					strStream << "private key and certificate do not match";
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}

				tLayer->addSystemTrustedCertificates();

				if (GetValidate()) {
					tLayer->setCertificateVerification(DCV_requireCertificate);
				}
				else {
					tLayer->setCertificateVerification(DCV_ignoreCertificate);
				}
				/*
				 if (opt_dhparam && ! (tLayer->setTempDHParameters(opt_dhparam)))
				 {
				 LOG_WARN(assoc->ambitolog, "unable to load temporary DH parameters. Ignoring");
				 }
				 */

				cond = ASC_setTransportLayer(net, tLayer, 0);
				if (cond.bad())
				{
					LOG_ERROR("C-STORE", "Error al insertar capa de transporte segura: " << cond.text());
				}
			}

			/* initialize asscociation parameters, i.e. create an instance of T_ASC_Parameters*. */
			cond = ASC_createAssociationParameters(&params, opt_maxReceivePDULength);
			DimseCondition::dump(cond);
			if (cond.bad())
			{
				DimseCondition::dump(cond);
				std::stringstream strStream;
				strStream << "DICOM Network Failure (storescu) ASC_createAssociationParameters; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
				errmsg(strStream.str(),true);
				LOG_ERROR("C-STORE", strStream.str().c_str());
			}

			if (UseUserPass()) {
				cond = ASC_setIdentRQUserPassword(params, GetUser().c_str(), GetPass().c_str());
				DimseCondition::dump(cond);
			}
			
			if (cond.bad()) {
				std::stringstream strStream;
				strStream << "DICOM Network Failure (storescu) ASC_createAssociationParameters; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
				errmsg(strStream.str(),true);
				LOG_ERROR("C-STORE", strStream.str().c_str());
			 }

			/* sets this application's title and the called application's title in the params */
			/* structure. The default values to be set here are "STORESCU" and "ANY-SCP". */
			ASC_setAPTitles(params, opt_ourTitle, opt_peerTitle, NULL);

			/* Set the transport layer type (type of network connection) in the params */
			/* strucutre. The default is an insecure connection; where OpenSSL is  */
			/* available the user is able to request an encrypted,secure connection. */
			cond = ASC_setTransportLayerType(params, IsSecure());
			if (cond.bad()) {
				DimseCondition::dump(cond);
				std::stringstream strStream;
				strStream << "DICOM Network Failure (storescu) ASC_setTransportLayerType; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
				errmsg(strStream.str(),true);
				LOG_ERROR("C-STORE", strStream.str().c_str());
			}

			/* Figure out the presentation addresses and copy the */
			/* corresponding values into the association parameters.*/
			gethostname(localHost, sizeof(localHost) - 1);
			sprintf(peerHost, "%s:%d", opt_peer, (int)opt_port);
			//NSLog(@"peer host: %s", peerHost);
			cond = ASC_setPresentationAddresses(params, localHost, peerHost);
			if (cond.bad()) {
				DimseCondition::dump(cond);
				std::stringstream strStream;
				strStream << "DICOM Network Failure (storescu) ASC_setPresentationAddresses; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
				errmsg(strStream.str(),true);
				LOG_ERROR("C-STORE", strStream.str().c_str());
			}

			/* Set the presentation contexts which will be negotiated */
			/* when the network connection will be established */
			cond = addStoragePresentationContexts(params, sopClassUIDList);
			if (cond.bad()) {
				DimseCondition::dump(cond);
				std::stringstream strStream;
				strStream << "DICOM Network Failure (storescu) addStoragePresentationContexts; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
				errmsg(strStream.str(),true);
				LOG_ERROR("C-STORE", strStream.str().c_str());
			}


			/* dump presentation contexts if required */
			if(opt_debug){
				std::stringstream strStream;
				strStream << "Request Parameters:\n";
				ASC_dumpParameters(params, strStream);
				LOG_DEBUG(LOGGER,strStream.str());
			}

			/* create association, i.e. try to establish a network connection to another */
			/* DICOM application. This call creates an instance of T_ASC_Association*. */
			LOG_DEBUG(LOGGER,"Requesting Association");
			cond = ASC_requestAssociation(net, params, &assoc);
			if (cond.bad()) {
				if (cond == DUL_ASSOCIATIONREJECTED) {
					T_ASC_RejectParameters rej;
					ASC_getRejectParameters(params, &rej);
					ASC_printRejectParameters(stderr, &rej);
					DimseCondition::dump(cond);
					std::stringstream strStream;
					strStream << "DICOM Network Failure (storescu) ASC_requestAssociation[DUL_ASSOCIATIONREJECTED]; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				} else {
					DimseCondition::dump(cond);
					std::stringstream strStream;
					strStream << "DICOM Network Failure (storescu) ASC_requestAssociation[Unknown]; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}
			}

			/* dump the connection parameters if in debug mode*/
			if(opt_debug)
			{
				std::stringstream strStream;
				strStream << "Connection Parameters:\n";
				ASC_dumpConnectionParameters(assoc, strStream);
				LOG_DEBUG(LOGGER,strStream.str());
			}

			/* dump the presentation contexts which have been accepted/refused */
			if(opt_debug){
				std::stringstream strStream;
				strStream << "Association Parameters Negotiated:\n";
				ASC_dumpParameters(params, strStream);
				LOG_DEBUG(LOGGER,strStream.str());
			}

			/* count the presentation contexts which have been accepted by the SCP */
			/* If there are none, finish the execution */
			if (ASC_countAcceptedPresentationContexts(params) == 0) {
				std::stringstream strStream;
				strStream << "DICOM Network Failure (storescu) Contextos de presentacion no aceptados";
				errmsg(strStream.str(),true);
				LOG_WARN("C-STORE", strStream.str().c_str());
			}

			/* dump general information concerning the establishment of the network connection if required */
			if (opt_debug) {
				std::stringstream strStream;
				strStream << "Association Accepted (Max Send PDV: "<< assoc->sendPDVLength;
				LOG_DEBUG(LOGGER,strStream.str());
			}

			/* do the real work, i.e. for all files which were specified in the */
			/* command line, transmit the encapsulated DICOM objects to the SCP. */
			cond = EC_Normal;
			OFListIterator(OFString) iter = fileNameList.begin();
			OFListIterator(OFString) enditer = fileNameList.end();

			int _numberSent=0;
			//int _numberOfFiles =0;
			//int _numberErrors =0;
			OFString mensaje;

			ResetearMedida(true);
			while ((iter != enditer) && (cond == EC_Normal) && !_shouldAbort) // compare with EC_Normal since DUL_PEERREQUESTEDRELEASE is also good()
			{
				wxString msg = wxString::Format(_("Uploading file %d of %d"),(_numberSent+1), listaFicheros.size());
				m_mensaje = std::string(msg.ToUTF8());
				pNotificador->NotificarProgreso(0.0f, m_mensaje);

				cond = cstore(assoc, *iter);
				++iter;
				if (!unsuccessfulStoreEncountered)
					_numberSent++;
				else {
					//error cerramos las conexiones...
					break;
				}

				//TODO CONFIGURACION MAXIMO NUMERO DFICHEROS Q SEPUEDE SUBIR, MAXIMO NUMERO DE ERRORES...
			}


			/* tear down association, i.e. terminate network connection to SCP */
			if (cond == EC_Normal)
			{
				if (opt_abortAssociation)
				{
					cond = ASC_abortAssociation(assoc);
					if (cond.bad())
					{
						LOG_DEBUG(LOGGER,"Abortando Asociacion: \n");
						DimseCondition::dump(cond);
						std::stringstream strStream;
						strStream << "DICOM Network Failure (storescu) Association Abort Failed; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
						errmsg(strStream.str(),true);
						LOG_ERROR("C-STORE", strStream.str().c_str());
					}
				} else
				{
					/* release association */
					LOG_DEBUG(LOGGER,"Abortando Asociacion: \n");
					cond = ASC_releaseAssociation(assoc);
					if (cond.bad())
					{
						ASC_dropAssociation(assoc);
						DimseCondition::dump(cond);
						std::stringstream strStream;
						strStream << "DICOM Network Failure (storescu) Association Abort Failed; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
						errmsg(strStream.str(),true);
						LOG_ERROR("C-STORE", strStream.str().c_str());
					}
				}
			}
			else if (cond == DUL_PEERREQUESTEDRELEASE)
			{
				LOG_DEBUG(LOGGER,"Abortando Asociacion [PEERREQUESTEDRELEASE]");

				cond = ASC_abortAssociation(assoc);
				if (cond.bad())
				{
					std::stringstream strStream;
					strStream << "DICOM Network Failure (storescu) Association Abort Failed; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}
			}
			else if (cond == DUL_PEERABORTEDASSOCIATION)
			{
				std::stringstream strStream;
				strStream << "Fallo en la red DICOM (storescu) El servidor ha cerrado la conexion ";
				errmsg(strStream.str(),true);
				LOG_ERROR("C-STORE", strStream.str().c_str());
			}
			else
			{
				DimseCondition::dump(cond);
				LOG_DEBUG(LOGGER,"Abortando Asociacion");

				cond = ASC_abortAssociation(assoc);
				if (cond.bad())
				{
					DimseCondition::dump(cond);
					std::stringstream strStream;
					strStream << "DICOM Network Failure (storescu) Protocol Error: SCU Failed; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}
			}

			if ( unsuccessfulStoreEncountered)
			{
				throw GIL::DICOM::PACSException(errorEncontrado);
			}
		}

		void DicomStoreAssociation::FreeMemory()
		{
			/* destroy the association, i.e. free memory of T_ASC_Association* structure. This */
			/* call is the counterpart of ASC_requestAssociation(...) which was called above. */
			if (assoc != NULL) {
				OFCondition cond = ASC_destroyAssociation(&assoc);
				if (cond.bad())
				{
					DimseCondition::dump(cond);
					std::stringstream strStream;
					strStream << "DICOM Network Failure (storescu) Protocol Error: ASC_destroyAssociation; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}
			}
			/* drop the network, i.e. free memory of T_ASC_Network* structure. This call */
			/* is the counterpart of ASC_initializeNetwork(...) which was called above. */

			if (net!= NULL) {
				OFCondition cond = ASC_dropNetwork(&net);
				if (cond.bad())
				{
					DimseCondition::dump(cond);
					std::stringstream strStream;
					strStream << "DICOM Network Failure (storescu) Protocol Error: ASC_dropNetwork; Modulo: "<< cond.module() << "; Code:"<< cond.code() << "; " << cond.text();
					errmsg(strStream.str(),true);
					LOG_ERROR("C-STORE", strStream.str().c_str());
				}
			}
		}
	}
}

void GIL::DICOM::DicomStoreAssociation::progressCallback(void * callbackData,
													T_DIMSE_StoreProgress *progress,
													T_DIMSE_C_StoreRQ * /*req*/)
{
	_StoreCallbackInfo *cbdata = static_cast<_StoreCallbackInfo*>(callbackData);

	if (progress->state == DIMSE_StoreProgressing) {
		//si ha pasado medio segundo...
		std::stringstream ostr;
		ostr << m_mensaje;
		ostr.setf(std::ios::floatfield, std::ios::fixed );
		ostr.precision(2);
		ostr << " a " << TasaTransferencia(progress->progressBytes) << " kb/s";
		std::string msg(ostr.str());

		if(!m_pNotificadorProgreso->NotificarProgreso((float)progress->progressBytes/progress->totalBytes,msg))
		{
			ASC_abortAssociation(cbdata->assoc);
		}
	}
	if (progress->state == DIMSE_StoreEnd) {
		ResetearMedida();
	}
}

