/// <summary>
        /// Retrieves a workflow item for the work item.
        /// </summary>
        /// <param name="session">A secure session.</param>
        /// <param name="workItem">A work item.</param>
        /// <returns>An application workflow item (<see cref="ApplicationWorkflowItem"/>).</returns>
        private ApplicationWorkflowItem GetWorkflowItem(SecureSession session, UnitOfWork<ApplicationWorkItem> workItem)
        {
            ProductVersionList versionList = null;
            ProductDefinition currentVersion = null;

            foreach (var item in this.productCache)
            {
                if (item.Key == workItem.Item.Application.FormId)
                {
                    versionList = item.Value;
                    break;
                }
            }

            if (versionList == null)
            {
                versionList = new ProductVersionList();
                this.productCache.Add(workItem.Item.Application.FormId, versionList);
            }

            // If current version is null, we have to load it and add it to the list.
            currentVersion = versionList.GetCurrentVersion();
            if (currentVersion == null)
            {
                currentVersion = this.formServiceGateway.GetProduct(session, workItem.Item.Application.FormId, null);
                versionList.Add(currentVersion);
            }

            if (workItem.Item.Application.UseFormVersion &&
                workItem.Item.Application.FormVersion != null &&
                !versionList.HasVersion(workItem.Item.Application.FormVersion.Value))
            {
                ProductDefinition specificVersion = this.formServiceGateway.GetProduct(session, workItem.Item.Application.FormId, workItem.Item.Application.FormVersion.Value);
                versionList.Add(specificVersion);
            }

            ProductDefinition linkedVersion = workItem.Item.Application.UseFormVersion &&
                                                workItem.Item.Application.FormVersion != null
                ? versionList.GetVersion(workItem.Item.Application.FormVersion.Value)
                : currentVersion;

            ApplicationWorkflowItem workflowItem = new ApplicationWorkflowItem(
                workItem.Item.Application,
                workItem.Item.Application.ApplicationData,
                linkedVersion.FormDefinition.Pages.AllControls)
            {
                FormVersion = linkedVersion.Version
            };

            return workflowItem;
        }
        /// <summary>
        /// Sets the metadata of <paramref name="application" /> and returns a <see cref="Product" />.
        /// </summary>
        /// <param name="secureSession">The session data.</param>
        /// <param name="application">The application to set metadata for.</param>
        /// <param name="formVersion">The form version. Defaults to <see langword="null" /> (latest).</param>
        /// <param name="publicDidAuthenticate">If set to <c>true</c> [a public user authenticated before saving].</param>
        /// <param name="updateAsignee">If set to <c>true</c> the assignee will be updated to the current user.</param>
        /// <returns>
        /// A <see cref="Product" /> object with the appropriate version for the supplied <paramref name="application" />.
        /// </returns>
        private ProductDefinition UpdateApplicationMetadata(SecureSession secureSession, Application application, int? formVersion = null, bool publicDidAuthenticate = false, bool updateAsignee = false)
        {
            IApplicationUser appUser = secureSession.AuthenticatedUser != null ?
                new IUserMapper().Map(secureSession.AuthenticatedUser, new AuthenticatedApplicationUser()) :
                new AnonymousApplicationUser(secureSession.SessionId) as IApplicationUser;

            ProductDefinition product;
            application.Modified = DateTime.Now;
            application.ModifiedBy = appUser.Id;

            if (application.IsNew)
            {
                product = this.DataAccess.GetProduct(application.FormId, formVersion);
                ApplicationWorkflowItem workflowItem = new ApplicationWorkflowItem(application);

                application.AssignedTo = appUser.Id;
                application.AssignedToDisplayName = secureSession.AuthenticatedUser != null ? secureSession.AuthenticatedUser.DisplayName : string.Empty;
                application.Created = DateTime.Now;
                application.CreatedBy = appUser;
                application.OrganisationId = product.OrganisationId;
                application.FormOrganisationId = product.OrganisationId;
                application.FormVersion = product.Version;
                application.UseFormVersion = product.LinkApplicationsToVersion;
                application.VersionNumber = 1;
                application.WorkflowState = application.WorkflowState ?? this.workflowService.GetInitialState(workflowItem, product.Version).Name;
                application.StateTransitionDate = application.Created;
            }
            else
            {
                Application existingApplication = this.GetApplication(application.Id);
                IApplicationUser originator = existingApplication.CreatedBy == null || (existingApplication.CreatedBy.Type == ApplicationUserType.Anonymous && appUser.Type == ApplicationUserType.Authenticated && publicDidAuthenticate) ? appUser : existingApplication.CreatedBy;

                application.Created = existingApplication.Created;
                application.CreatedBy = originator;
                application.OrganisationId = existingApplication.OrganisationId ?? application.OrganisationId;
                application.FormOrganisationId = application.OrganisationId;
                application.FormVersion = existingApplication.FormVersion;
                application.UseFormVersion = existingApplication.UseFormVersion;
                application.VersionNumber = existingApplication.VersionNumber + 1;
                application.WorkflowState = existingApplication.WorkflowState;
                application.StateTransitionDate = existingApplication.StateTransitionDate;
                application.ApplicationIdDisplay = existingApplication.ApplicationIdDisplay;

                if (updateAsignee)
                {
                    application.AssignedTo = secureSession.AuthenticatedUser != null
                        ? secureSession.AuthenticatedUser.Id
                        : originator.Id;
                    application.AssignedToDisplayName = secureSession.AuthenticatedUser != null
                        ? secureSession.AuthenticatedUser.DisplayName
                        : existingApplication.AssignedToDisplayName;
                }
                else
                {
                    application.AssignedTo = existingApplication.AssignedTo;
                    application.AssignedToDisplayName = existingApplication.AssignedToDisplayName;
                }

                product = this.DataAccess.GetProduct(application.FormId, application.UseFormVersion ? application.FormVersion : null);
            }

            if (string.IsNullOrEmpty(application.OrganisationId))
            {
                Organisation org = this.organisationManager.GetOrganisation(application.OrganisationId);
                application.OrganisationName = org.Name;
            }

            return product;
        }
        /// <summary>
        /// Logs workflow state changes.
        /// </summary>
        /// <param name="secureSession">The session data.</param>
        /// <param name="application">The saved application.</param>
        /// <param name="isNew">A value indicating whether the application is new.</param>
        /// <param name="workflowItem">The workflow item to log.</param>
        /// <param name="previousState">The previous state of <paramref name="application"/>.</param>
        /// <param name="newState">The new state of <paramref name="application"/>.</param>
        private void LogWorkflow(SecureSession secureSession, Application application, bool isNew, ApplicationWorkflowItem workflowItem, ItemState previousState, ItemState newState)
        {
            if (workflowItem == null && !isNew)
            {
                return;
            }

            workflowItem = workflowItem ?? new ApplicationWorkflowItem
            {
                FormId = application.FormId,
                UserId = secureSession.AuthenticatedUser != null ? secureSession.AuthenticatedUser.Id : null
            };

            workflowItem.ApplicationId = application.ApplicationId;
            workflowItem.VersionNumber = application.VersionNumber;
            workflowItem.PostedData = application.ApplicationData;

            if (isNew && newState != null && previousState.Name != newState.Name)
            {
                // Special case to log from blank to the first state when we are moving straight into the next state.
                this.workflowService.LogStateChange(workflowItem, new ItemState(null), previousState);
            }

            if (isNew && (newState == null || previousState.Name == newState.Name))
            {
                newState = previousState;
                previousState = new ItemState(null);
            }

            this.workflowService.LogStateChange(workflowItem, previousState, newState);
        }
        /// <summary>
        /// Performs workflow on behalf of the submit.
        /// </summary>
        /// <param name="secureSession">The session data.</param>
        /// <param name="application">The application to perform workflow for.</param>
        /// <param name="existingApplicationData">The existing application data.</param>
        /// <param name="product">The product definition.</param>
        /// <param name="scopedPageList">The list of pages required to process the application in its current state.</param>
        /// <param name="currentItemState">The current workflow state.</param>
        /// <param name="workflowState">The new / requested workflow state.</param>
        /// <param name="assignee">The assignee value from the workflow transition.</param>
        /// <param name="assigneeType">The assignee type from the workflow transition.</param>
        /// <param name="newState">After a successful transition, will be the new workflow state, otherwise <see langword="null"/>.</param>
        /// <returns>After a successful transition, the workflow item, otherwise <see langword="null"/>.</returns>
        private ApplicationWorkflowItem PerformWorkflow(SecureSession secureSession, Application application, ApplicationData existingApplicationData, ProductDefinition product, PageList scopedPageList, ItemState currentItemState, string workflowState, string assignee, ComparisonType? assigneeType, out ItemState newState)
        {
            newState = currentItemState;

            WorkflowConfigurationContainer workflowContainer = this.workflowService.GetWorkflowConfiguration(WorkflowTargetType.FormApplication, application.FormId, product.Version);
            WorkflowState state = workflowContainer.Configuration.States.FirstOrDefault(s => s.Name == currentItemState.Name);

            WorkflowTransition transition = (state == null) ?
                                                       null :
                                                            string.IsNullOrWhiteSpace(workflowState) ?
                                                            null :
                                                                state.Transitions.FirstOrDefault(t => t.Trigger != TriggerEvent.Submit && t.ExitStateName == workflowState);

            Organisation entitleTo;

            ApplicationWorkflowItem workflowItem = new ApplicationWorkflowItem(application, existingApplicationData, scopedPageList.AllControls)
            {
                FormVersion = product.Version
            };

            // Do non-submit transition. Only Service accounts can perform this operation.
            if (transition != null && secureSession.AuthenticatedUser != null && secureSession.AuthenticatedUser.AccountType == AccountType.Service)
            {
                User assignTo = this.DetermineAssignee(assignee, assigneeType, application);
                application.WorkflowState = transition.ExitStateName;
                application.StateTransitionDate = DateTime.Now;
                application.AssignedTo = (assignTo != null) ? assignTo.Id : null;
                application.AssignedToDisplayName = (assignTo != null) ? assignTo.DisplayName : null;

                entitleTo = this.DetermineOrganisation(transition.ExitOrganisation, application);
                application.OrganisationId = (entitleTo != null) ? entitleTo.Id : application.OrganisationId;
                application.OrganisationName = (entitleTo != null) ? entitleTo.Name : application.OrganisationName;

                return workflowItem;
            }

            // Do submit transition.
            WorkflowResult result = this.workflowService.Trigger(workflowItem, new WorkflowTransition { Trigger = TriggerEvent.Submit }, currentItemState);

            // If result = null, there were NO valid transitions, so we leave the application essentially as is, but unassign it.
            if (result == null)
            {
                application.AssignedTo = null;
                application.AssignedToDisplayName = null;
            }

            if (result != null)
            {
                newState = result.ExitState;
                if (newState != null && string.Compare(newState.Name, application.WorkflowState, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    application.StateTransitionDate = DateTime.Now;
                }

                application.WorkflowState = (newState == null) ? null : newState.Name;

                User user = this.DetermineAssignee(result.ExitAssignee, result.ExitAssigneeType, application);
                application.AssignedTo = (user != null) ? user.Id : null;
                application.AssignedToDisplayName = (user != null) ? user.DisplayName : null;

                entitleTo = this.DetermineOrganisation(result.ExitOrganisation, application);
                application.OrganisationId = (entitleTo != null) ? entitleTo.Id : application.OrganisationId;
                application.OrganisationName = (entitleTo != null) ? entitleTo.Name : application.OrganisationName;
            }

            return workflowItem;
        }
        /// <summary>
        /// Gets the control entitlements for a user.
        /// </summary>
        /// <param name="sessionData">The session data.</param>
        /// <param name="application">The application.</param>
        /// <param name="product">The product definition.</param>
        /// <param name="pageList">The list of pages.</param>
        /// <returns>The control access for a user.</returns>
        private List<ControlAccess> GetControlAccess(SessionData sessionData, Application application, Product product, PageList pageList)
        {
            User user = this.userManager.GetUserById(sessionData.UserId);
            SecureSession secureSession = new SecureSession(sessionData.DeserializationSource, sessionData.SessionId, user);
            if (application.IsNew)
            {
                ApplicationWorkflowItem workflowItem = new ApplicationWorkflowItem(application.FormId, sessionData.UserId);
                application.WorkflowState = this.workflowService.GetInitialState(workflowItem, product.Version).Name;
            }

            var roleList = this.DataAccess.GetRoleList();

            List<ControlAccess> controlsAccess = this.entitlementProvider.GetControlsAccess(secureSession, application, pageList.AllControls, roleList, product.Version);
            return controlsAccess;
        }