/// <summary>
        /// Compute the copy permission for the <paramref name="thingToCopy"/>
        /// </summary>
        /// <param name="thingToCopy">The <see cref="Thing"/> to copy</param>
        /// <param name="targetContainer">The target container</param>
        /// <returns>The <see cref="CopyPermissionResult"/></returns>
        public CopyPermissionResult ComputeCopyPermission(Thing thingToCopy, Thing targetContainer)
        {
            if (thingToCopy == null)
            {
                throw new ArgumentNullException("thingToCopy");
            }

            if (targetContainer == null)
            {
                throw new ArgumentNullException("targetContainer", "The destination of the thing to copy cannot be null.");
            }

            if (!this.CheckContainement(thingToCopy, targetContainer))
            {
                throw new InvalidOperationException("The container is invalid for the thing to copy.");
            }

            if (!(thingToCopy is ElementDefinition) && thingToCopy.GetContainerOfType <ElementDefinition>() == null)
            {
                throw new NotImplementedException("The method is only implemented for things contained by ElementDefinition. The RequiredRdls property needs to be implemented in all things.");
            }

            var result = new CopyPermissionResult();

            result.errorList      = new Dictionary <Thing, string>();
            result.copyableThings = new List <Thing>();

            var model     = targetContainer.TopContainer as EngineeringModel;
            var iteration = targetContainer is Iteration it ? it : targetContainer.GetContainerOfType <Iteration>();

            this.changedOwner = this.session.OpenIterations.Single(x => x.Key.Iid == iteration.Iid).Value.Item1;

            if (model != null)
            {
                if (thingToCopy.TopContainer == targetContainer.TopContainer)
                {
                    throw new InvalidOperationException("The copy operation is only supported between 2 different models.");
                }

                this.ComputeAvailableRdl(model);
                this.ComputeActiveDomains(model);
                this.ComputeModelCopyPermission(thingToCopy, targetContainer, result);
            }

            return(result);
        }
        /// <summary>
        /// Compute the copy permissions for the dependencies of the <paramref name="usage"/>
        /// </summary>
        /// <param name="usage">The <see cref="ElementUsage"/> to copy</param>
        /// <param name="targetContainer">The target container for the <paramref name="usage"/></param>
        /// <param name="permissionResult">The <see cref="CopyPermissionResult"/> that is updated</param>
        /// <returns>
        /// True if the <see cref="ElementDefinition"/> for the <paramref name="usage"/> can be copied
        /// with all its <see cref="Parameter"/>
        /// </returns>
        private bool ComputeDependenciesCopyPermission(ElementUsage usage, ElementDefinition targetContainer, CopyPermissionResult permissionResult)
        {
            if (!permissionResult.copyableThings.Contains(usage.ElementDefinition) &&
                !permissionResult.errorList.ContainsKey(usage.ElementDefinition))
            {
                this.ComputeModelCopyPermission(usage.ElementDefinition, targetContainer.Container, permissionResult);
            }

            var errorThingIds = permissionResult.errorList.Select(err => err.Key.Iid).ToList();

            // the element definition should be copyable along its parameters
            var mandatoryIds = usage.ElementDefinition.Parameter.Select(p => p.Iid).ToList();

            mandatoryIds.Add(usage.ElementDefinition.Iid);

            return(!errorThingIds.Intersect(mandatoryIds).Any());
        }
        /// <summary>
        /// Compute the copy permissions for the dependencies of the <paramref name="thingToCopy"/>
        /// </summary>
        /// <param name="thingToCopy">The <see cref="Thing"/> to copy</param>
        /// <param name="targetContainer">The target container for the <paramref name="thingToCopy"/></param>
        /// <param name="permissionResult">The <see cref="CopyPermissionResult"/> that is updated</param>
        /// <returns>True if the mandatory data of the dependencies can be copied</returns>
        private bool ComputeDependenciesCopyPermission(Thing thingToCopy, Thing targetContainer, CopyPermissionResult permissionResult)
        {
            switch (thingToCopy.ClassKind)
            {
            case ClassKind.ElementUsage:
                return(this.ComputeDependenciesCopyPermission((ElementUsage)thingToCopy, (ElementDefinition)targetContainer, permissionResult));

            default:
                return(true);
            }
        }
        /// <summary>
        /// Compute the copy permission for the contained <see cref="Thing"/>s of the <paramref name="thingToCopy"/>
        /// </summary>
        /// <param name="thingToCopy">The <see cref="Thing"/> to copy</param>
        /// <param name="targetContainer">The target container for <paramref name="thingToCopy"/></param>
        /// <param name="permissionResult">The <see cref="CopyPermissionResult"/></param>
        private void ComputeModelContainedThingPermission(Thing thingToCopy, Thing targetContainer, CopyPermissionResult permissionResult)
        {
            var thingToCopyClone = thingToCopy.Clone(false);

            thingToCopyClone.Container = targetContainer;

            foreach (var containerList in thingToCopyClone.ContainerLists)
            {
                foreach (Thing thing in containerList)
                {
                    this.ComputeModelCopyPermission(thing, thingToCopyClone, permissionResult);
                }
            }
        }
        /// <summary>
        /// Compute the copy permission for the <see cref="Thing"/> contained by an <see cref="EngineeringModel"/>,
        /// its contained <see cref="Thing"/>s and its dependencies.
        /// </summary>
        /// <param name="thingToCopy">The <see cref="Thing"/> to copy</param>
        /// <param name="targetContainer">The target container</param>
        /// <param name="permissionResult">The <see cref="CopyPermissionResult"/> containing the result of the copy permission computation</param>
        private void ComputeModelCopyPermission(Thing thingToCopy, Thing targetContainer, CopyPermissionResult permissionResult)
        {
            if (thingToCopy.ClassKind == ClassKind.ParameterValueSet ||
                thingToCopy.ClassKind == ClassKind.ParameterOverrideValueSet ||
                thingToCopy.ClassKind == ClassKind.ParameterSubscriptionValueSet)
            {
                // value sets are not copied
                return;
            }

            if (!this.permissionService.CanWrite(thingToCopy.ClassKind, targetContainer))
            {
                permissionResult.errorList.Add(thingToCopy, string.Format("You do not have permission to copy the {0} with id {1}.", thingToCopy.ClassKind, thingToCopy.Iid));
                return;
            }

            // all the required rdls for the thing to copy shall be available in the model destination
            var requiredRdls = thingToCopy.RequiredRdls.ToList();

            if (this.availableRdls.Intersect(requiredRdls).Count() != requiredRdls.Count)
            {
                permissionResult.errorList.Add(thingToCopy, string.Format("The copy operation cannot be performed for the {0} with id {1} as some required reference data libraries are missing in the target model.", thingToCopy.ClassKind, thingToCopy.Iid));
                return;
            }

            var ownedThing = thingToCopy as IOwnedThing;

            if (ownedThing != null && !this.ownerIsChanged && !this.activeDomains.Contains(ownedThing.Owner))
            {
                permissionResult.errorList.Add(thingToCopy, string.Format("The copy operation cannot be performed for the {0} with id {1}. The owner is not active in the target model", thingToCopy.ClassKind, thingToCopy.Iid));
                return;
            }

            // compute copy dependencies, if fail, dont copy
            if (!this.ComputeDependenciesCopyPermission(thingToCopy, targetContainer, permissionResult))
            {
                permissionResult.errorList.Add(thingToCopy, string.Format("The copy operation cannot be performed for the {0} with id {1}. Some of its dependencies cannot be copied.", thingToCopy.ClassKind, thingToCopy.Iid));
                return;
            }

            var subscription = thingToCopy as ParameterSubscription;

            if (subscription != null && this.ownerIsChanged && (subscription.Owner == this.changedOwner || !this.activeDomains.Contains(ownedThing.Owner)))
            {
                permissionResult.errorList.Add(thingToCopy, string.Format("The parameter subscription {0} will not be copied. The owner of the subscribed parameter or override in the target destination is the same of the subscription or is not active in the target model.", thingToCopy.Iid));
                return;
            }

            permissionResult.copyableThings.Add(thingToCopy);
            this.ComputeModelContainedThingPermission(thingToCopy, targetContainer, permissionResult);
        }