/// <summary>
        /// Creates a copy of this contact point, assigning it to the specified owner.
        /// </summary>
        /// <param name="owner"></param>
        /// <returns></returns>
        public virtual ExternalPractitionerContactPoint CreateCopy(ExternalPractitioner owner)
        {
            var copy = new ExternalPractitionerContactPoint(
                owner,
                _name,
                _description,
                _preferredResultCommunicationMode,
                _informationAuthority,
                _isDefaultContactPoint,
                CollectionUtils.Map(_telephoneNumbers, (TelephoneNumber tn) => (TelephoneNumber)tn.Clone()),
                CollectionUtils.Map(_addresses, (Address a) => (Address)a.Clone()),
                CollectionUtils.Map(_emailAddresses, (EmailAddress e) => (EmailAddress)e.Clone()),
                null);

            copy.MarkDeactivated(_deactivated);

            owner.ContactPoints.Add(copy);
            return(copy);
        }
        /// <summary>
        /// Creates a new contact point that is the result of merging the two specified contact points.
        /// </summary>
        /// <remarks>
        /// Any phone numbers, addresses and email addresses in the supplied arguments will supercede those
        /// from the source contact points.
        /// </remarks>
        public static ExternalPractitionerContactPoint MergeContactPoints(
            ExternalPractitionerContactPoint right,
            ExternalPractitionerContactPoint left,
            string name,
            string description,
            ResultCommunicationMode preferredCommunicationMode,
            InformationAuthorityEnum informationAuthority,
            IList <TelephoneNumber> phoneNumbers,
            IList <Address> addresses,
            IList <EmailAddress> emailAddresses
            )
        {
            // sanity checks
            if (Equals(right, left))
            {
                throw new WorkflowException("Cannot merge a contact point with itself.");
            }
            if (!Equals(right.Practitioner, left.Practitioner))
            {
                throw new WorkflowException("Only contact points belonging to the same practitioner can be merged.");
            }
            if (right.Deactivated || left.Deactivated)
            {
                throw new WorkflowException("Cannot merge a contact point that is de-activated.");
            }
            if (right.IsMerged || left.IsMerged)
            {
                throw new WorkflowException("Cannot merge a contact point that has already been merged.");
            }

            var practitioner = right.Practitioner;             // same practitioner for both

            // create new contact point, using specified name and description
            // the new contact point is default if either of the source contact points were the default
            // start with empty value collections
            var result = new ExternalPractitionerContactPoint(
                practitioner,
                name,
                description,
                preferredCommunicationMode,
                informationAuthority,
                right.IsDefaultContactPoint || left.IsDefaultContactPoint,
                phoneNumbers ?? new List <TelephoneNumber>(),
                addresses ?? new List <Address>(),
                emailAddresses ?? new List <EmailAddress>(),
                null);

            practitioner.ContactPoints.Add(result);

            // merge value collections from source contact points, and update source contact points appropriately
            foreach (var contactPoint in new[] { right, left })
            {
                // merge all value collections into the result
                MergeValueCollection(result.TelephoneNumbers, contactPoint.TelephoneNumbers, (x, y) => x.IsSameNumber(y));
                MergeValueCollection(result.Addresses, contactPoint.Addresses, (x, y) => x.IsSameAddress(y));
                MergeValueCollection(result.EmailAddresses, contactPoint.EmailAddresses, (x, y) => x.IsSameEmailAddress(y));

                // mark as merged
                contactPoint.SetMergedInto(result);
            }

            // mark the practitioner as being edited
            practitioner.MarkEdited();

            return(result);
        }
 /// <summary>
 /// Marks this contact point as being merged into the specified other.
 /// </summary>
 /// <param name="other"></param>
 protected internal virtual void SetMergedInto(ExternalPractitionerContactPoint other)
 {
     _mergedInto            = other;
     _deactivated           = true;
     _isDefaultContactPoint = false;
 }
        /// <summary>
        /// Creates a new practitioner that is the result of merging the two specified practitioners.
        /// </summary>
        /// <param name="right"></param>
        /// <param name="left"></param>
        /// <param name="name"></param>
        /// <param name="licenseNumber"></param>
        /// <param name="billingNumber"></param>
        /// <param name="extendedProperties"></param>
        /// <param name="defaultContactPoint"></param>
        /// <param name="deactivatedContactPoints"></param>
        /// <param name="contactPointReplacements"></param>
        /// <returns></returns>
        public static ExternalPractitioner MergePractitioners(
            ExternalPractitioner right,
            ExternalPractitioner left,
            PersonName name,
            string licenseNumber,
            string billingNumber,
            IDictionary <string, string> extendedProperties,
            ExternalPractitionerContactPoint defaultContactPoint,
            ICollection <ExternalPractitionerContactPoint> deactivatedContactPoints,
            IDictionary <ExternalPractitionerContactPoint, ExternalPractitionerContactPoint> contactPointReplacements)
        {
            // sanity check
            if (Equals(right, left))
            {
                throw new WorkflowException("Cannot merge a practitioner with itself.");
            }
            if (right.Deactivated || left.Deactivated)
            {
                throw new WorkflowException("Cannot merge a practitioner that is de-activated.");
            }
            if (right.IsMerged || left.IsMerged)
            {
                throw new WorkflowException("Cannot merge a practitioner that has already been merged.");
            }
            if (defaultContactPoint != null && defaultContactPoint.IsMerged)
            {
                throw new WorkflowException("Cannot assigned a merged contact point as default");
            }

            // update properties on result record
            var result = new ExternalPractitioner {
                Name = name, LicenseNumber = licenseNumber, BillingNumber = billingNumber
            };

            ExtendedPropertyUtils.Update(result.ExtendedProperties, extendedProperties);

            // construct the set of retained contact points
            var retainedContactPoints = new HashedSet <ExternalPractitionerContactPoint>();

            retainedContactPoints.AddAll(contactPointReplacements.Values);

            // some of the replacement contact points are merged.  This should not be allowed.
            if (CollectionUtils.Contains(contactPointReplacements.Values, cp => cp.IsMerged))
            {
                throw new WorkflowException("Cannot replace a contact point with another that has already been merged.");
            }

            // add any existing contact point that was not in the replacement list (because it is implicitly being retained)
            foreach (var contactPoint in CollectionUtils.Concat(right.ContactPoints, left.ContactPoints))
            {
                // No need to retain a merged contact point.  Because its replacement would already be retained.
                if (!contactPointReplacements.ContainsKey(contactPoint) && !contactPoint.IsMerged)
                {
                    retainedContactPoints.Add(contactPoint);
                }
            }

            // for all retained contact points, create a copy attached to the result practitioner,
            // and mark the original as having been merged into the copy
            foreach (var original in retainedContactPoints)
            {
                var copy = original.CreateCopy(result);
                result.ContactPoints.Add(copy);

                copy.IsDefaultContactPoint = original.Equals(defaultContactPoint);
                copy.MarkDeactivated(original.Deactivated || deactivatedContactPoints.Contains(original));
                original.SetMergedInto(copy);
            }

            // for all replaced contact points, mark the original as being merged into the
            // copy of the replacement
            foreach (var kvp in contactPointReplacements)
            {
                kvp.Key.SetMergedInto(kvp.Value.MergedInto);
            }

            // mark both left and right as edited and merged
            foreach (var practitioner in new[] { right, left })
            {
                practitioner.MarkEdited();
                practitioner.SetMergedInto(result);
            }

            // mark the result as being edited
            result.MarkEdited();
            return(result);
        }