/// <summary> /// Finds the best index (and parent) at which to insert a new part that is propagated after being added to one of the bases of this asset. /// </summary> /// <param name="baseAsset">The base asset for the part that has been added.</param> /// <param name="newBasePart">The new part that has been added to the base.</param> /// <param name="newBasePartParent">The parent part of the part that has been added to the base.</param> /// <param name="instanceId">The id of the instance for which we are looking for an index and parent.</param> /// <param name="instanceParent">The parent in which to insert the new instance part. If null, the new part will be inserted as root of the hierarchy.</param> /// <returns>The index at which to insert the new part in the instance, or a negative value if the part should be discarded.</returns> protected virtual int FindBestInsertIndex(AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> baseAsset, TAssetPartDesign newBasePart, TAssetPart newBasePartParent, Guid instanceId, out TAssetPartDesign instanceParent) { instanceParent = null; var insertIndex = -1; // First, let's find out where it is the best to insert this new part if (newBasePartParent == null) { // The part is a root, so we must place it according to its sibling (since no parent exists). var partIndex = baseAsset.Hierarchy.RootParts.IndexOf(x => x.Id == newBasePart.Part.Id); // Let's try to find a sibling in the parts preceding it, in order for (var i = partIndex - 1; i >= 0 && insertIndex < 0; --i) { var sibling = baseAsset.Hierarchy.Parts[baseAsset.Hierarchy.RootParts[i].Id]; var instanceSibling = Asset.Hierarchy.Parts.Values.FirstOrDefault(x => x.Base?.InstanceId == instanceId && x.Base?.BasePartId == sibling.Part.Id); // This sibling still exists instance-side, let's get its parent. if (instanceSibling != null) { // If the sibling itself has a parent instance-side, let's use the same parent and insert after it // Otherwise the sibling is root, let's insert after it in the root parts var parent = Asset.GetParent(instanceSibling.Part); instanceParent = parent != null ? Asset.Hierarchy.Parts[parent.Id] : null; insertIndex = Asset.IndexOf(instanceSibling.Part) + 1; break; } } // Let's try to find a sibling in the parts following it, in order for (var i = partIndex + 1; i < baseAsset.Hierarchy.RootParts.Count && insertIndex < 0; ++i) { var sibling = baseAsset.Hierarchy.Parts[baseAsset.Hierarchy.RootParts[i].Id]; var instanceSibling = Asset.Hierarchy.Parts.Values.FirstOrDefault(x => x.Base?.InstanceId == instanceId && x.Base?.BasePartId == sibling.Part.Id); // This sibling still exists instance-side, let's get its parent. if (instanceSibling != null) { // If the sibling itself has a parent instance-side, let's use the same parent and insert after it // Otherwise the sibling is root, let's insert after it in the root parts var parent = Asset.GetParent(instanceSibling.Part); instanceParent = parent != null ? Asset.Hierarchy.Parts[parent.Id] : null; insertIndex = Asset.IndexOf(instanceSibling.Part); break; } } } else { // The new part is not root, it has a parent. instanceParent = Asset.Hierarchy.Parts.Values.FirstOrDefault(x => x.Base?.InstanceId == instanceId && x.Base?.BasePartId == newBasePartParent.Id); // If the parent has been removed instance side, the hierarchy to the new part does not exist anymore. We can discard it if (instanceParent != null) { var partIndex = baseAsset.IndexOf(newBasePart.Part); // Let's try to find a sibling in the parts preceding it, in order for (var i = partIndex - 1; i >= 0 && insertIndex < 0; --i) { var sibling = baseAsset.GetChild(newBasePartParent, i); var instanceSibling = Asset.Hierarchy.Parts.Values.FirstOrDefault(x => x.Base?.InstanceId == instanceId && x.Base?.BasePartId == sibling.Id); // This sibling still exists instance-side, let's insert after it if (instanceSibling != null) { insertIndex = i + 1; } } // Let's try to find a sibling in the parts following it, in order for (var i = partIndex + 1; i < baseAsset.GetChildCount(newBasePartParent) && insertIndex < 0; ++i) { var sibling = baseAsset.GetChild(newBasePartParent, i); var instanceSibling = Asset.Hierarchy.Parts.Values.FirstOrDefault(x => x.Base?.InstanceId == instanceId && x.Base?.BasePartId == sibling.Id); // This sibling still exists instance-side, let's insert before it if (instanceSibling != null) { insertIndex = i - 1; } } // Default position is first index if (insertIndex < 0) { insertIndex = 0; } } } if (insertIndex < 0) { // We couldn't find any parent/sibling in the instance. Either the parent has been removed, in which case we'll discard the part, // or the base is a single part that has been moved around, and we'll rely on the last known common ancestor of this instance to re-insert it. var isAlone = Asset.Hierarchy.Parts.Values.All(x => x.Base?.InstanceId != instanceId); if (isAlone) { Guid parentId; if (instancesCommonAncestors.TryGetValue(instanceId, out parentId) && parentId != Guid.Empty) { // FIXME: instancesCommonAncestors should be synchronized with existing instances, i.e. if the instance has been compltely removed then the common ancestor should have been cleared. if (Asset.Hierarchy.Parts.TryGetValue(parentId, out instanceParent)) { insertIndex = 0; } } } } return(insertIndex); }