context: #region License
  
  // Copyright (c) 2013, ClearCanvas Inc.
  // All rights reserved.
  // http://www.clearcanvas.ca
  //
  // This file is part of the ClearCanvas RIS/PACS open source project.
  //
  // The ClearCanvas RIS/PACS open source project is free software: you can
  // redistribute it and/or modify it under the terms of the GNU General Public
  // License as published by the Free Software Foundation, either version 3 of the
  // License, or (at your option) any later version.
  //
  // The ClearCanvas RIS/PACS open source project is distributed in the hope that it
  // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
  // Public License for more details.
  //
  // You should have received a copy of the GNU General Public License along with
  // the ClearCanvas RIS/PACS open source project.  If not, see
  // <http://www.gnu.org/licenses/>.
  
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  using System.Security.Permissions;
  using System.Threading;
  using ClearCanvas.Common;
  using ClearCanvas.Common.Utilities;
  using ClearCanvas.Enterprise.Common;
  using ClearCanvas.Enterprise.Core;
  using ClearCanvas.Enterprise.Core.Printing;
  using ClearCanvas.Healthcare;
  using ClearCanvas.Healthcare.Brokers;
  using ClearCanvas.Healthcare.Printing;
  using ClearCanvas.Healthcare.Workflow.Reporting;
  using ClearCanvas.Ris.Application.Common;
  using ClearCanvas.Ris.Application.Common.ReportingWorkflow;
  using ClearCanvas.Workflow;
  using Iesi.Collections.Generic;
  using AuthorityTokens = ClearCanvas.Ris.Application.Common.AuthorityTokens;
  
  namespace ClearCanvas.Ris.Application.Services.ReportingWorkflow
  {
  	[ServiceImplementsContract(typeof(IReportingWorkflowService))]
  	[ExtensionOf(typeof(ApplicationServiceExtensionPoint))]
  	public class ReportingWorkflowService : WorkflowServiceBase, IReportingWorkflowService
  	{
  		#region IReportingWorkflowService Members
  
  		[ReadOperation]
  		public GetDocumentationStatusResponse GetDocumentationStatus(GetDocumentationStatusRequest request)
  		{
  			var procedure = this.PersistenceContext.Load<Procedure>(request.ProcedureRef);
  
  			var message = "";
  			var isIncomplete = false;
  
  			if (!procedure.Order.AreAllActiveProceduresPerformed)
  			{
  				isIncomplete = true;
  				message = SR.MessageNotAllProceduresPerformed;
  			}
  			else if (!procedure.IsDocumented)
  			{
  				isIncomplete = true;
  				message = SR.MessageDocumentationIncomplete;
  			}
  
  			return new GetDocumentationStatusResponse(isIncomplete, message);
  		}
  
  		[ReadOperation]
  		[AuditRecorder(typeof(WorkflowServiceRecorder.SearchWorklists))]
  		public TextQueryResponse<ReportingWorklistItemSummary> SearchWorklists(WorklistItemTextQueryRequest request)
  		{
  			var procedureStepClass = request.ProcedureStepClassName == null ? null
  				: ProcedureStep.GetSubClass(request.ProcedureStepClassName, PersistenceContext);
  
  			// decide which broker/projection to use for searching
  			var isReporting = typeof (ReportingProcedureStep).IsAssignableFrom(procedureStepClass);
  			var broker = isReporting ?
  				(IWorklistItemBroker)PersistenceContext.GetBroker<IReportingWorklistItemBroker>()
  				: PersistenceContext.GetBroker<IProtocolWorklistItemBroker>();
  
  			var projection = isReporting ? 
  				WorklistItemProjection.ReportingWorklistSearch :
  				WorklistItemProjection.ProtocolWorklistSearch;
  			
  
  			var assembler = new ReportingWorkflowAssembler();
  			return SearchHelper<ReportingWorklistItem, ReportingWorklistItemSummary>(
  				request,
  				broker,
  				projection,
  				item => assembler.CreateWorklistItemSummary(item, PersistenceContext));
  		}
  
  		[ReadOperation]
  		[ResponseCaching("GetQueryWorklistCacheDirective")]
  		public QueryWorklistResponse<ReportingWorklistItemSummary> QueryWorklist(QueryWorklistRequest request)
  		{
  			var assembler = new ReportingWorkflowAssembler();
  
  			return QueryWorklistHelper<ReportingWorklistItem, ReportingWorklistItemSummary>(
  				request,
  				item => assembler.CreateWorklistItemSummary(item, this.PersistenceContext));
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanStartInterpretation")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Create)]
  		public StartInterpretationResponse StartInterpretation(StartInterpretationRequest request)
  		{
  			var interpretation = this.PersistenceContext.Load<InterpretationStep>(request.InterpretationStepRef, EntityLoadFlags.CheckVersion);
  			var staffAssignedBeforeStart = interpretation.AssignedStaff;
  
  			var linkedInterpretations = new List<InterpretationStep>();
  			if (request.LinkedInterpretationStepRefs != null && request.LinkedInterpretationStepRefs.Count > 0)
  			{
  				linkedInterpretations = CollectionUtils.Map<EntityRef, InterpretationStep>(
  					request.LinkedInterpretationStepRefs,
  					stepRef => this.PersistenceContext.Load<InterpretationStep>(stepRef));
  			}
  
  			var op = new Operations.StartInterpretation();
  			op.Execute(interpretation, this.CurrentUserStaff, linkedInterpretations, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  			return new StartInterpretationResponse(interpretation.GetRef(),
  				staffAssignedBeforeStart == null ? null : interpretation.AssignedStaff.GetRef());
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanStartTranscriptionReview")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Create)]
  		public StartTranscriptionReviewResponse StartTranscriptionReview(StartTranscriptionReviewRequest request)
  		{
  			var transcriptionReviewStep = this.PersistenceContext.Load<TranscriptionReviewStep>(request.TranscriptionReviewStepRef, EntityLoadFlags.CheckVersion);
  
  			var op = new Operations.StartTranscriptionReview();
  			op.Execute(transcriptionReviewStep, this.CurrentUserStaff);
  
  			this.PersistenceContext.SynchState();
  			return new StartTranscriptionReviewResponse(transcriptionReviewStep.GetRef());
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanCompleteInterpretationForTranscription")]
  		[AuditRecorder(typeof(ReportingWorkflowServiceRecorder.CompleteInterpretation))]
  		public CompleteInterpretationForTranscriptionResponse CompleteInterpretationForTranscription(CompleteInterpretationForTranscriptionRequest request)
  		{
  			var interpretation = this.PersistenceContext.Load<ReportingProcedureStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  			var supervisor = ResolveSupervisor(interpretation, request.SupervisorRef);
  
  			SaveReportHelper(request.ReportPartExtendedProperties, interpretation, supervisor, true);
  			UpdatePriority(interpretation, request.Priority);
  
  			ValidateReportTextExists(interpretation);
  
  			var op = new Operations.CompleteInterpretationForTranscription();
  			var nextStep = op.Execute(interpretation, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  			return new CompleteInterpretationForTranscriptionResponse
  					{
  						InterpretationStepRef = interpretation.GetRef(),
  						TranscriptionStepRef = nextStep.GetRef()
  					};
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanCompleteInterpretationForVerification")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.SubmitForReview)]
  		[AuditRecorder(typeof(ReportingWorkflowServiceRecorder.CompleteInterpretation))]
  		public CompleteInterpretationForVerificationResponse CompleteInterpretationForVerification(CompleteInterpretationForVerificationRequest request)
  		{
  			var interpretation = this.PersistenceContext.Load<ReportingProcedureStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  			var supervisor = ResolveSupervisor(interpretation, request.SupervisorRef);
  
  			SaveReportHelper(request.ReportPartExtendedProperties, interpretation, supervisor, true);
  			UpdatePriority(interpretation, request.Priority);
  
  			ValidateReportTextExists(interpretation);
  
  			var op = new Operations.CompleteInterpretationForVerification();
  			var nextStep = op.Execute(interpretation, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  			return new CompleteInterpretationForVerificationResponse
  					{
  						InterpretationStepRef = interpretation.GetRef(),
  						VerificationStepRef = nextStep.GetRef()
  					};
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanCompleteInterpretationAndVerify")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Verify)]
  		[AuditRecorder(typeof(ReportingWorkflowServiceRecorder.CompleteVerification))]
  		public CompleteInterpretationAndVerifyResponse CompleteInterpretationAndVerify(CompleteInterpretationAndVerifyRequest request)
  		{
  			var interpretation = this.PersistenceContext.Load<ReportingProcedureStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  			var supervisor = ResolveSupervisor(interpretation, request.SupervisorRef);
  
  			SaveReportHelper(request.ReportPartExtendedProperties, interpretation, supervisor, true);
  			UpdatePriority(interpretation, request.Priority);
  
  			ValidateReportTextExists(interpretation);
  
  			var op = new Operations.CompleteInterpretationAndVerify();
  			var nextStep = op.Execute(interpretation, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  			return new CompleteInterpretationAndVerifyResponse
  					{
  						InterpretationStepRef = interpretation.GetRef(),
  						PublicationStepRef = nextStep.GetRef()
  					};
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanCancelReportingStep")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Create)]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Cancel)]
  		[AuditRecorder(typeof(ReportingWorkflowServiceRecorder.Discard))]
  		public CancelReportingStepResponse CancelReportingStep(CancelReportingStepRequest request)
  		{
  			var step = this.PersistenceContext.Load<ReportingProcedureStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  			var reassignStaff = request.ReassignedToStaff != null
  				? this.PersistenceContext.Load<Staff>(request.ReassignedToStaff, EntityLoadFlags.CheckVersion)
  				: null;
  
  			// demand authority token if trying to cancel a step that is assigned to someone else
  			if (step.AssignedStaff != null && !Equals(step.AssignedStaff, this.CurrentUserStaff))
  			{
  				var permission = new PrincipalPermission(null, AuthorityTokens.Workflow.Report.Cancel);
  				permission.Demand();
  			}
  
  			var op = new Operations.CancelReportingStep();
  			var scheduledInterpretations = op.Execute(step, this.CurrentUserStaff, reassignStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  
  			return new CancelReportingStepResponse(step.GetRef(),
  				CollectionUtils.Map<InterpretationStep, EntityRef>(scheduledInterpretations, s => s.GetRef()));
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanReviseResidentReport")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.SubmitForReview)]
  		public ReviseResidentReportResponse ReviseResidentReport(ReviseResidentReportRequest request)
  		{
  			var step = this.PersistenceContext.Load<VerificationStep>(request.VerificationStepRef, EntityLoadFlags.CheckVersion);
  
  			var op = new Operations.ReviseResidentReport();
  			var interpretation = op.Execute(step, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  			return new ReviseResidentReportResponse(GetWorklistItemSummary(interpretation));
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanReturnToInterpreter")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Verify)]
  		public ReturnToInterpreterResponse ReturnToInterpreter(ReturnToInterpreterRequest request)
  		{
  			var step = this.PersistenceContext.Load<ReportingProcedureStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  			var supervisor = ResolveSupervisor(step, request.SupervisorRef);
  
  			SaveReportHelper(request.ReportPartExtendedProperties, step, supervisor, true);
  			UpdatePriority(step, request.Priority);
  
  			ValidateReportTextExists(step);
  
  			var op = new Operations.ReturnToInterpreter();
  			var newStep = op.Execute(step, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  			return new ReturnToInterpreterResponse(newStep.GetRef());
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanStartVerification")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Verify)]
  		public StartVerificationResponse StartVerification(StartVerificationRequest request)
  		{
  			var verification = this.PersistenceContext.Load<VerificationStep>(request.VerificationStepRef, EntityLoadFlags.CheckVersion);
  
  			var op = new Operations.StartVerification();
  			op.Execute(verification, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  			return new StartVerificationResponse(verification.GetRef());
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanCompleteVerification")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Verify)]
  		[AuditRecorder(typeof(ReportingWorkflowServiceRecorder.CompleteVerification))]
  		public CompleteVerificationResponse CompleteVerification(CompleteVerificationRequest request)
  		{
  			var verification = this.PersistenceContext.Load<VerificationStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  			var supervisor = ResolveSupervisor(verification, request.SupervisorRef);
  
  			SaveReportHelper(request.ReportPartExtendedProperties, verification, supervisor, true);
  			UpdatePriority(verification, request.Priority);
  
  			ValidateReportTextExists(verification);
  
  			var op = new Operations.CompleteVerification();
  			var publication = op.Execute(verification, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  
  			return new CompleteVerificationResponse
  					{
  						VerificationStepRef = verification.GetRef(),
  						PublicationStepRef = publication.GetRef()
  					};
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanCreateAddendum")]
  		public CreateAddendumResponse CreateAddendum(CreateAddendumRequest request)
  		{
  			var procedure = this.PersistenceContext.Load<Procedure>(request.ProcedureRef);
  
  			var op = new Operations.CreateAddendum();
  			var interpretation = op.Execute(procedure, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  
  			return new CreateAddendumResponse
  					{
  						ReportingWorklistItem = GetWorklistItemSummary(interpretation)
  					};
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanReviseUnpublishedReport")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Verify)]
  		[AuditRecorder(typeof(ReportingWorkflowServiceRecorder.Revise))]
  		public ReviseUnpublishedReportResponse ReviseUnpublishedReport(ReviseUnpublishedReportRequest request)
  		{
  			var publication = this.PersistenceContext.Load<PublicationStep>(request.PublicationStepRef, EntityLoadFlags.CheckVersion);
  
  			var op = new Operations.ReviseUnpublishedReport();
  			var verification = op.Execute(publication, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			this.PersistenceContext.SynchState();
  
  			return new ReviseUnpublishedReportResponse(GetWorklistItemSummary(verification));
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanPublishReport")]
  		public PublishReportResponse PublishReport(PublishReportRequest request)
  		{
  			var publication = this.PersistenceContext.Load<PublicationStep>(request.PublicationStepRef, EntityLoadFlags.CheckVersion);
  
  			var op = new Operations.PublishReport();
  			op.Execute(publication, this.CurrentUserStaff, new PersistentWorkflow(this.PersistenceContext));
  
  			LogicalHL7Event.ReportPublished.EnqueueEvents(publication.Report);
  
  			this.PersistenceContext.SynchState();
  			return new PublishReportResponse(publication.GetRef());
  		}
  
  		[ReadOperation]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Create)]
  		public LoadReportForEditResponse LoadReportForEdit(LoadReportForEditRequest request)
  		{
  			var step = this.PersistenceContext.Load<ReportingProcedureStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  
  			var reportAssembler = new ReportAssembler();
  			var reportDetail = reportAssembler.CreateReportDetail(step.ReportPart.Report, false, this.PersistenceContext);
  
  			var orderAssembler = new OrderAssembler();
  			var orderDetailOptions = new OrderAssembler.CreateOrderDetailOptions {IncludeExtendedProperties = true};
  			var orderDetail = orderAssembler.CreateOrderDetail(step.Procedure.Order, orderDetailOptions, this.PersistenceContext);
  
  			return new LoadReportForEditResponse(
  				reportDetail,
  				step.ReportPart.Index,
  				orderDetail,
  				EnumUtils.GetEnumValueList<OrderPriorityEnum>(PersistenceContext));
  		}
  
  		[UpdateOperation]
  		[OperationEnablement("CanSaveReport")]
  		[PrincipalPermission(SecurityAction.Demand, Role = AuthorityTokens.Workflow.Report.Create)]
  		public SaveReportResponse SaveReport(SaveReportRequest request)
  		{
  			var step = this.PersistenceContext.Load<ReportingProcedureStep>(request.ReportingStepRef, EntityLoadFlags.CheckVersion);
  			var supervisor = ResolveSupervisor(step, request.SupervisorRef);
  
  			// saving a draft does not require supervisor validation
  			SaveReportHelper(request.ReportPartExtendedProperties, step, supervisor, false);
  			UpdatePriority(step, request.Priority);
  
  			this.PersistenceContext.SynchState();
  
  			return new SaveReportResponse(step.GetRef());
  		}
  
  		[ReadOperation]
  		public GetPriorsResponse GetPriors(GetPriorsRequest request)
  		{
  			Platform.CheckForNullReference(request, "request");
  
gt: 			if (request.OrderRef == null && request.ReportRef == null)