/// <summary>
		/// Process the duplicate with the supplied <see cref="DuplicateProcessingEnum"/>
		/// </summary>
		/// <param name="context">The processing context</param>
		/// <param name="file">The file</param>
		/// <param name="data">The data</param>
		/// <param name="duplicate">How the processor should handle the duplicate</param>
		public static void ProcessStoredDuplicate(SopInstanceProcessorContext context,
		                                          DicomFile file,
		                                          StudyProcessWorkQueueData data,
		                                          DuplicateProcessingEnum duplicate)
		{
			SaveDuplicate(context, file);
			var uidData = new WorkQueueUidData
				{
					Extension = ServerPlatform.DuplicateFileExtension,
					GroupId = context.Group,
					DuplicateProcessing = duplicate
				};

			if (context.Request != null)
				uidData.OperationToken = context.Request.OperationToken;

			context.CommandProcessor.AddCommand(
				new UpdateWorkQueueCommand(file, context.StudyLocation, true, data, uidData, context.Request));
		}
		private DicomProcessingResult HandleDuplicateFile(string sopInstanceUid, StudyStorageLocation studyLocation, ServerCommandProcessor commandProcessor, DicomMessageBase message, string sourceFilename, StudyProcessWorkQueueData data)
		{
			Study study = studyLocation.Study ??
						  studyLocation.LoadStudy(ServerExecutionContext.Current.PersistenceContext);
			if (study != null)
				Platform.Log(LogLevel.Info, "Received duplicate SOP {0} (A#:{1} StudyUid:{2}  Patient: {3}  ID:{4})",
							 sopInstanceUid,
							 study.AccessionNumber, study.StudyInstanceUid,
							 study.PatientsName, study.PatientId);
			else
				Platform.Log(LogLevel.Info,
							 "Received duplicate SOP {0} (StudyUid:{1}). Existing files haven't been processed.",
							 sopInstanceUid, studyLocation.StudyInstanceUid);

			var sopProcessingContext = new SopInstanceProcessorContext(commandProcessor, studyLocation, _context.ContextID,
																_context.Request)
			{
				DuplicateProcessing = _context.DuplicateProcessing
			};
			DicomProcessingResult result = DuplicateSopProcessorHelper.Process(sopProcessingContext, message, data,
			                                                                   sourceFilename);
			return result;
		}
		private void HandleNonDuplicateFile(string seriesInstanceUid, string sopInstanceUid, StudyStorageLocation studyLocation, ServerCommandProcessor commandProcessor, DicomMessageBase message, string sourcePath, string path, bool dupImage, StudyProcessWorkQueueData data)
		{
			commandProcessor.AddCommand(new CreateDirectoryCommand(path));

			path = Path.Combine(path, studyLocation.PartitionFolder);
			commandProcessor.AddCommand(new CreateDirectoryCommand(path));

			path = Path.Combine(path, studyLocation.StudyFolder);
			commandProcessor.AddCommand(new CreateDirectoryCommand(path));

			path = Path.Combine(path, studyLocation.StudyInstanceUid);
			commandProcessor.AddCommand(new CreateDirectoryCommand(path));

			path = Path.Combine(path, seriesInstanceUid);
			commandProcessor.AddCommand(new CreateDirectoryCommand(path));

			path = Path.Combine(path, sopInstanceUid);
			path += ServerPlatform.DicomFileExtension;

			commandProcessor.AddCommand(new RenameFileCommand(sourcePath, path, true));

			WorkQueueUidData uidData = null;
			if (_context.Request != null && !string.IsNullOrEmpty(_context.Request.OperationToken))
			{
				uidData = new WorkQueueUidData
				{
					OperationToken = _context.Request.OperationToken
				};
			}

			commandProcessor.AddCommand(
				new UpdateWorkQueueCommand(message, studyLocation, dupImage, data, uidData, _context.Request));

			#region SPECIAL CODE FOR TESTING
			if (Diagnostics.Settings.SimulateFileCorruption)
			{
				commandProcessor.AddCommand(new CorruptDicomFileCommand(path));
			}
			#endregion
		}
        /// <summary>
        /// Imports the specified <see cref="DicomMessageBase"/> object into the system.
        /// The object will be inserted into the <see cref="WorkQueue"/> for processing and
        /// if it's a duplicate, proper checks will be done and depending on the policy, it will be 
        /// ignored, rejected or inserted into the <see cref="StudyIntegrityQueue"/> for manual intervention.
        /// </summary>
        /// <param name="message">The DICOM object to be imported.</param>
        /// <returns>An instance of <see cref="DicomProcessingResult"/> that describes the result of the processing.</returns>
        /// <exception cref="DicomDataException">Thrown when the DICOM object contains invalid data</exception>
        public DicomProcessingResult Import(DicomMessageBase message)
        {
            Platform.CheckForNullReference(message, "message");
            String studyInstanceUid;
            String seriesInstanceUid;
            String sopInstanceUid;
            String accessionNumber;
            String patientsName;

	        LoadMessageUids(message, out studyInstanceUid, out seriesInstanceUid, out sopInstanceUid,
	                             out accessionNumber, out patientsName);

        	DicomFile file = null;

            // Scrub the name for invalid characters.
            string newName = XmlUtils.XmlCharacterScrub(patientsName);
            if (!newName.Equals(patientsName))
                message.DataSet[DicomTags.PatientsName].SetStringValue(newName);

			var result = new DicomProcessingResult
			                               	{
			                               		Successful = true,
			                               		StudyInstanceUid = studyInstanceUid,
			                               		SeriesInstanceUid = seriesInstanceUid,
			                               		SopInstanceUid = sopInstanceUid,
			                               		AccessionNumber = accessionNumber
			                               	};

        	try
			{
				Validate(message);
			}
			catch (DicomDataException e)
			{
				result.SetError(DicomStatuses.ProcessingFailure, e.Message);
				return result;
			}

            // Use the command processor for rollback capabilities.
            using (var commandProcessor = new ServerCommandProcessor(String.Format("Processing Sop Instance {0}", sopInstanceUid)))
            {
                try
                {
                    string failureMessage;
                    StudyStorageLocation studyLocation = GetWritableOnlineStorage(message);

                    // GetWritableOnlineStorage should throw an exception if the study location cannot be found.
                    Platform.CheckForNullReference(studyLocation, "studyLocation");

                    if (!studyLocation.QueueStudyStateEnum.Equals(QueueStudyStateEnum.Idle)
                        && (!studyLocation.QueueStudyStateEnum.Equals(QueueStudyStateEnum.ProcessingScheduled)))
                    {
                        failureMessage = String.Format("Study {0} on partition {1} is being processed: {2}, can't accept new images.",
                                                       studyLocation.StudyInstanceUid, _context.Partition.Description, studyLocation.QueueStudyStateEnum.Description);
                        result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage);
                        return result;
                    }
                	if (studyLocation.StudyStatusEnum.Equals(StudyStatusEnum.OnlineLossy))
                	{
                		if (studyLocation.IsLatestArchiveLossless)
                		{
                			result.DicomStatus = DicomStatuses.StorageStorageOutOfResources;
                			failureMessage = String.Format("Study {0} on partition {1} can't accept new images due to lossy compression of the study.  Restoring study.",
                			                               studyLocation.StudyInstanceUid, _context.Partition.Description);
                			Platform.Log(LogLevel.Error, failureMessage);
                			if (ServerHelper.InsertRestoreRequest(studyLocation) == null)
                			{
                				Platform.Log(LogLevel.Warn, "Unable to insert Restore Request for Study");
                			}

                			result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage);
                            result.RestoreRequested = true;
                			return result;
                		}
                	}

                	String path = studyLocation.FilesystemPath;
                    String finalDest = studyLocation.GetSopInstancePath(seriesInstanceUid, sopInstanceUid);
                    file = ConvertToDicomFile(message, finalDest, _context.SourceAE);

                    if (HasUnprocessedCopy(studyLocation.Key, seriesInstanceUid, sopInstanceUid))
                    {
                        var accept = false;

                        // This is a special case: #10569
                        // Allow user to revive an orphaned study by reprocessing the files found in the filesystem
                        if (File.Exists(finalDest))
                        {
                            accept = DuplicatePolicy.IsParitionDuplicatePolicyOverridden(studyLocation);
                        }
                        
                        if (!accept)
                        {
                            failureMessage = string.Format("Another copy of the SOP Instance was received but has not been processed: {0}", sopInstanceUid);
                            result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage);
                            return result;
                        }
                    }

                    var data = new StudyProcessWorkQueueData
                        {
                            ReceivingAeTitle = _context.AlternateAe == null
                                                  ? _context.Partition.AeTitle
                                                  : _context.AlternateAe.AeTitle
                        };

                	if (File.Exists(finalDest))
                	{
                		result = HandleDuplicate(sopInstanceUid, studyLocation, commandProcessor, file, data);
                		if (!result.Successful)
                			return result;
                	}
                	else
                	{
                	    HandleNonDuplicate(seriesInstanceUid, sopInstanceUid, studyLocation, commandProcessor, file, path,
                	                       false, data);
                	}

                	if (commandProcessor.Execute())
                	{
                		result.DicomStatus = DicomStatuses.Success;
                	}
                	else
                	{
                		failureMessage =
                			String.Format("Failure processing message: {0}. Sending failure status.",
                			              commandProcessor.FailureReason);
                		result.SetError(DicomStatuses.ProcessingFailure, failureMessage);
                		// processor already rolled back
                		return result;
                	}
                }
                catch(NoWritableFilesystemException)
                {
                    String failureMessage = String.Format("Unable to process image, no writable filesystem found for Study UID {0}.", sopInstanceUid);
                    commandProcessor.Rollback();
                    result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage);
                }
                catch(StudyIsNearlineException e)
                {
                    String failureMessage = e.RestoreRequested
                                                ? String.Format("{0}. Restore has been requested.", e.Message)
                                                : e.Message;

                    Platform.Log(LogLevel.Error, failureMessage);
                    commandProcessor.Rollback();
                    result.SetError(DicomStatuses.ProcessingFailure, failureMessage);
                }
                catch (FilesystemNotWritableException)
                {
					commandProcessor.Rollback();

                	string folder;
					if (!FilesystemMonitor.Instance.GetWriteableIncomingFolder(_context.Partition, out folder))
					{
						String failureMessage =
							String.Format("Unable to process image, study storage location is missing or not writeable: {0}.", sopInstanceUid);
						result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage);
						return result;
					}

                	if (file == null)
                		file = ConvertToDicomFile(message, string.Empty, _context.SourceAE);

                	if (!SaveToFolder(folder, sopInstanceUid, studyInstanceUid, file))
                	{
						String failureMessage =
							String.Format("Study storage location not writeable and no writeable incoming folder: {0}.", sopInstanceUid);
						result.SetError(DicomStatuses.StorageStorageOutOfResources, failureMessage);
						return result;
                	}

                	Platform.Log(LogLevel.Info, "Saved existing SOP without writeable storage location to {0} folder: {1}",
                	             FilesystemMonitor.ImportDirectorySuffix, sopInstanceUid);
					result.DicomStatus = DicomStatuses.Success;
                	return result;
                }
                catch (Exception e)
                {
                    Platform.Log(LogLevel.Error, e, "Unexpected exception when {0}.  Rolling back operation.", commandProcessor.Description);
                    commandProcessor.Rollback();
                    result.SetError(result.DicomStatus ?? DicomStatuses.ProcessingFailure, e.Message);
                }
            }

            return result;
        }
		/// <summary>
		/// Process the duplicate with the supplied <see cref="DuplicateProcessingEnum"/>
		/// </summary>
		/// <param name="context">The processing context</param>
		/// <param name="message">A subset of the message stored in <paramref name="sourceFilename"/></param>
		/// <param name="sourceFilename">The location of the filename that is a duplicate</param>
		/// <param name="data">The data</param>
		/// <param name="duplicate">How the processor should handle the duplicate</param>
		public static void ProcessStoredDuplicateFile(SopInstanceProcessorContext context,
													  string sourceFilename,
													  DicomMessageBase message,
													  StudyProcessWorkQueueData data,
													  DuplicateProcessingEnum duplicate)
		{
			SaveDuplicateFile(context, message.DataSet[DicomTags.SopInstanceUid].ToString(), sourceFilename);
			var uidData = new WorkQueueUidData
			{
				Extension = ServerPlatform.DuplicateFileExtension,
				GroupId = context.Group,
				DuplicateProcessing = duplicate
			};

			if (context.Request != null)
				uidData.OperationToken = context.Request.OperationToken;

			context.CommandProcessor.AddCommand(
				new UpdateWorkQueueCommand(message, context.StudyLocation, true, data, uidData, context.Request));
		}
		/// <summary>
		/// Create Duplicate SIQ Entry
		/// </summary>
		/// <param name="file"></param>
		/// <param name="location"></param>
		/// <param name="sourcePath"></param>
		/// <param name="queue"></param>
		/// <param name="uid"></param>
		/// <param name="data"></param>
		public static void CreateDuplicateSIQEntry(DicomFile file, StudyStorageLocation location, string sourcePath,
		                                           WorkQueue queue, WorkQueueUid uid, StudyProcessWorkQueueData data)
		{
			Platform.Log(LogLevel.Info, "Creating Work Queue Entry for duplicate...");
			String uidGroup = queue.GroupID ?? queue.GetKey().Key.ToString();
			using (var commandProcessor = new ServerCommandProcessor("Insert Work Queue entry for duplicate"))
			{
				commandProcessor.AddCommand(new FileDeleteCommand(sourcePath, true));

				var sopProcessingContext = new SopInstanceProcessorContext(commandProcessor, location, uidGroup);
				DicomProcessingResult result = Process(sopProcessingContext, file, data);
				if (!result.Successful)
				{
					FailUid(uid, true);
					return;
				}

				commandProcessor.AddCommand(new DeleteWorkQueueUidCommand(uid));

				if (!commandProcessor.Execute())
				{
					Platform.Log(LogLevel.Error, "Unexpected error when creating duplicate study integrity queue entry: {0}",
					             commandProcessor.FailureReason);
					FailUid(uid, true);
				}
			}
		}
		/// <summary>
		/// Inserts the duplicate DICOM file into the <see cref="WorkQueue"/> for processing (if applicable).
		/// </summary>
		/// <param name="context">The processing context.</param>
		/// <param name="file">The duplicate DICOM file being processed.</param>
		/// <param name="data">Extra data to insert for the WorkQueue item.</param>
		/// <param name="sourceFilename">Optional source filename already saved to disk to import.</param>
		/// <returns>A <see cref="DicomProcessingResult"/> that contains the result of the processing.</returns>
		/// <remarks>
		/// This method inserts a <see cref="CommandBase"/> into <paramref name="context.CommandProcessor"/>.
		/// The outcome of the operation depends on the <see cref="DuplicateSopPolicyEnum"/> of the <see cref="ServerPartition"/>.
		/// If it is set to <see cref="DuplicateSopPolicyEnum.CompareDuplicates"/>, the duplicate file will be
		/// inserted into the <see cref="WorkQueue"/> for processing.
		/// </remarks>
		public static DicomProcessingResult Process(SopInstanceProcessorContext context, DicomMessageBase file,
		                                            StudyProcessWorkQueueData data, string sourceFilename=null)
		{
			Platform.CheckForNullReference(file, "file");
			Platform.CheckForNullReference(context, "context");
			Platform.CheckMemberIsSet(context.Group, "parameters.Group");
			Platform.CheckMemberIsSet(context.CommandProcessor, "parameters.CommandProcessor");
			Platform.CheckMemberIsSet(context.StudyLocation, "parameters.StudyLocation");
			if (string.IsNullOrEmpty(sourceFilename))
				Platform.CheckForNullReference(file as DicomFile, "file");

			var result = new DicomProcessingResult
				{
					DicomStatus = DicomStatuses.Success,
					Successful = true,
					StudyInstanceUid = file.DataSet[DicomTags.StudyInstanceUid].GetString(0, string.Empty),
					SeriesInstanceUid = file.DataSet[DicomTags.SeriesInstanceUid].GetString(0, string.Empty),
					SopInstanceUid = file.DataSet[DicomTags.SopInstanceUid].GetString(0, string.Empty),
					SopClassUid = file.DataSet[DicomTags.SopClassUid].GetString(0, string.Empty),
					AccessionNumber = file.DataSet[DicomTags.AccessionNumber].GetString(0, string.Empty)
				};

			string failureMessage;

			if (context.DuplicateProcessing.HasValue && context.DuplicateProcessing.Value.Equals(DuplicateProcessingEnum.Reject))
			{
				failureMessage = String.Format("Duplicate SOP Instance received, rejecting {0}", result.SopInstanceUid);
				Platform.Log(LogLevel.Info, failureMessage);
				result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage);
				return result;
			}

			if (SopClassIsReport(result.SopClassUid) && context.StudyLocation.ServerPartition.AcceptLatestReport)
			{
				Platform.Log(LogLevel.Info, "Duplicate Report received, overwriting {0}", result.SopInstanceUid);
				if (string.IsNullOrEmpty(sourceFilename))
					ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.OverwriteReport);
				else
					ProcessStoredDuplicateFile(context, sourceFilename, file, data, DuplicateProcessingEnum.OverwriteReport);
				
				return result;
			}

			if (DuplicatePolicy.IsParitionDuplicatePolicyOverridden(context.StudyLocation))
			{
				Platform.Log(LogLevel.Warn,
				             "Duplicate instance received for study {0} on Partition {1}. Duplicate policy overridden. Will overwrite {2}",
				             result.StudyInstanceUid, context.StudyLocation.ServerPartition.AeTitle, result.SopInstanceUid);
				if (string.IsNullOrEmpty(sourceFilename))
					ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.OverwriteSop);
				else
					ProcessStoredDuplicateFile(context, sourceFilename, file, data, DuplicateProcessingEnum.OverwriteSop);
				return result;
			}

			if (context.DuplicateProcessing.HasValue)
			{
				Platform.Log(LogLevel.Info, context.DuplicateProcessing.Value.Equals(DuplicateProcessingEnum.Compare)
					                            ? "Duplicate SOP Instance received, comparing {0}"
					                            : "Duplicate SOP Instance received, overwriting {0}", result.SopInstanceUid);
				if (string.IsNullOrEmpty(sourceFilename))
					ProcessStoredDuplicate(context, file as DicomFile, data, context.DuplicateProcessing.Value);
				else
					ProcessStoredDuplicateFile(context, sourceFilename, file, data, context.DuplicateProcessing.Value);

				return result;
			}

			if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.AcceptLatest))
			{
				Platform.Log(LogLevel.Info, "Duplicate SOP Instance received, overwriting {0}", result.SopInstanceUid);
				if (string.IsNullOrEmpty(sourceFilename))
					ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.OverwriteSopAndUpdateDatabase);
				else
					ProcessStoredDuplicateFile(context, sourceFilename, file, data, DuplicateProcessingEnum.OverwriteSopAndUpdateDatabase);
				return result;
			}

			if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.SendSuccess))
			{
				Platform.Log(LogLevel.Info, "Duplicate SOP Instance received, sending success response {0}", result.SopInstanceUid);
				return result;
			}

			if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.RejectDuplicates))
			{
				failureMessage = String.Format("Duplicate SOP Instance received, rejecting {0}", result.SopInstanceUid);
				Platform.Log(LogLevel.Info, failureMessage);
				result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage);
				return result;
			}

			if (context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum.Equals(DuplicateSopPolicyEnum.CompareDuplicates))
			{
				if (string.IsNullOrEmpty(sourceFilename))
					ProcessStoredDuplicate(context, file as DicomFile, data, DuplicateProcessingEnum.Compare);
				else
					ProcessStoredDuplicateFile(context,sourceFilename,file,data,DuplicateProcessingEnum.Compare);
			}
			else
			{
				failureMessage = String.Format("Duplicate SOP Instance received. Unsupported duplicate policy {0}.",
				                               context.StudyLocation.ServerPartition.DuplicateSopPolicyEnum);
				result.SetError(DicomStatuses.DuplicateSOPInstance, failureMessage);
				return result;
			}

			return result;
		}