/// <summary>
        /// Called when Package Manager has completed a remove request.
        /// </summary>
        static void CompleteUninstallEvent()
        {
            var targetPack    = s_CurrentEvent.targetPack;
            var removeRequest = s_CurrentEvent.request as RemoveRequest;

            if (removeRequest == null)
            {
                return;
            }

            if (removeRequest.Status == StatusCode.Failure)
            {
                targetPack.InstallStatus &= ~(InstallationStatus.UninstallRequested | InstallationStatus.Locked);
                OnUninstallContent?.Invoke(false, removeRequest.Error.message);
                OnContentChanged?.Invoke();
                return;
            }

            targetPack.InstallStatus = InstallationStatus.Unknown;
            OnUninstallContent?.Invoke(true, $"Successfully uninstalled {s_CurrentEvent.targetPack.PackageName}");
            OnContentChanged?.Invoke();

            // We do an update here to ensure icon state is maintained - enough can change between dependencies and cross-references that just changing the target pack can be misleading
            if (QueuedEvents.Count == 0)
            {
                UpdateContentStatus();
            }
        }
        /// <summary>
        /// Attempts to install the sample associated with the currently active Content Pack.
        /// </summary>
        static void ProcessSampleEvent()
        {
            var targetPack = s_CurrentEvent.targetPack;
            var samples    = Sample.FindByPackage(targetPack.PackageName, targetPack.Version);

            // We manually set the event null this time because we have no asynchronous request to process
            s_CurrentEvent = null;

            if (!samples.Any())
            {
                targetPack.InstallStatus &= ~InstallationStatus.Installing;
                OnInstallContent?.Invoke(false, $"Can't install content - no sample available.");
                OnContentChanged?.Invoke();
                return;
            }

            var targetSample = samples.First();

            if (!targetSample.Import())
            {
                targetPack.InstallStatus &= ~InstallationStatus.Installing;
                OnInstallContent?.Invoke(false, $"Failed to import associated content sample.");
                OnContentChanged?.Invoke();
                return;
            }

            FinishInstallation(targetPack);
        }
        /// <summary>
        /// Attempts to embed a package specified within the currently active Content Pack.
        /// </summary>
        static void StartEmbedEvent()
        {
            var targetPack = s_CurrentEvent.targetPack;

            ConditionalLog($"Start Embed Event {targetPack.DisplayName}");
            targetPack.InstallStatus |= InstallationStatus.Locked;
            s_CurrentEvent.request    = Client.Embed(targetPack.PackageName);
            OnContentChanged?.Invoke();
        }
        static void FinishInstallation(ContentPack targetPack)
        {
            targetPack.InstallStatus = InstallationStatus.Installed;
            OnInstallContent?.Invoke(true, $"Successfully installed {targetPack.PackageName}");
            OnContentChanged?.Invoke();

            // We do an update here to ensure icon state is maintained - enough can change between dependencies and cross-references that just changing the target pack can be misleading
            if (QueuedEvents.Count == 0)
            {
                UpdateContentStatus();
            }
        }
        /// <summary>
        /// Attempts to remove the package that the currently active event contains.
        /// </summary>
        static void StartUninstallEvent()
        {
            // If the package is not installed, do nothing.
            if (!s_CurrentEvent.targetPack.InstallStatus.HasFlag(InstallationStatus.Installed))
            {
                OnUninstallContent?.Invoke(true, $"{s_CurrentEvent.targetPack.PackageName} not installed");
                s_CurrentEvent = null;
                return;
            }

            s_CurrentEvent.targetPack.InstallStatus |= InstallationStatus.Locked;
            s_CurrentEvent.request = Client.Remove(s_CurrentEvent.targetPack.PackageName);
            OnContentChanged?.Invoke();
        }
        /// <summary>
        /// Queues up an uninstallation of a Content Pack.
        /// </summary>
        /// <param name="contentToUninstall">The Content Pack to uninstall</param>
        internal static void UninstallContent(ContentPack contentToUninstall)
        {
            ConditionalLog($"UninstallContent {contentToUninstall.DisplayName}");

            // Check if this content is already queued up for uninstall
            if (contentToUninstall.InstallStatus.HasFlag(InstallationStatus.UninstallQueued))
            {
                return;
            }

            StartEditorUpdates();
            contentToUninstall.InstallStatus |= InstallationStatus.UninstallQueued;
            QueuedEvents.Add(new ContentEvent {
                action = PackageAction.Remove, targetPack = contentToUninstall
            });
            OnContentChanged?.Invoke();
        }
        /// <summary>
        /// Called internally to go from installing a package to installing a sample.
        /// </summary>
        /// <param name="contentToInstall">The Content Pack that contains a sample to install</param>
        static void InstallContentSample(ContentPack contentToInstall)
        {
            ConditionalLog($"InstallContentSample {contentToInstall.DisplayName}");

            // Content must be installed or installing to add a sample
            if (!(contentToInstall.InstallStatus.HasFlag(InstallationStatus.Installed) || contentToInstall.InstallStatus.HasFlag(InstallationStatus.Installing)))
            {
                return;
            }

            StartEditorUpdates();
            contentToInstall.InstallStatus |= InstallationStatus.InstallQueued;

            // We stick the sample event at the beginning of the list because it happens automatically after an install
            QueuedEvents.Insert(0, new ContentEvent {
                action = PackageAction.SampleInstall, targetPack = contentToInstall
            });
            OnContentChanged?.Invoke();
        }
        /// <summary>
        /// Called internally to go from installing a package to making it writeable.
        /// </summary>
        /// <param name="contentToEmbed">The Content Pack to make a writeable package</param>
        static void EmbedContent(ContentPack contentToEmbed)
        {
            ConditionalLog($"EmbedContent {contentToEmbed.DisplayName}");

            // Content must be installed or installing to embed
            if (!(contentToEmbed.InstallStatus.HasFlag(InstallationStatus.Installed) || contentToEmbed.InstallStatus.HasFlag(InstallationStatus.Installing)))
            {
                return;
            }

            StartEditorUpdates();
            contentToEmbed.InstallStatus |= InstallationStatus.InstallQueued;

            // We stick the embed event at the beginning of the list because it happens automatically after an install
            QueuedEvents.Insert(0, new ContentEvent {
                action = PackageAction.Embed, targetPack = contentToEmbed
            });
            OnContentChanged?.Invoke();
        }
        /// <summary>
        /// Stops any pending Content Manager actions relating to a particular Content Pack.
        /// </summary>
        /// <param name="contentToCancel">The Content Pack to stop all actions for</param>
        internal static void CancelContentAction(ContentPack contentToCancel)
        {
            var eventQueue = QueuedEvents;

            ConditionalLog($"CancelContentAction {contentToCancel.DisplayName}");

            // Search through the queued content list for events concerning this content pack
            var cancelableEvents = eventQueue.Where(entry => entry.targetPack == contentToCancel).ToList();

            // Remove them
            foreach (var targetEvent in cancelableEvents)
            {
                eventQueue.Remove(targetEvent);
            }

            // Undo any queued flags
            contentToCancel.InstallStatus &= ~(InstallationStatus.InstallQueued | InstallationStatus.UninstallRequested);
            OnContentChanged?.Invoke();
        }
        /// <summary>
        /// Called when Package Manager has completed the embed request.
        /// </summary>
        static void CompleteEmbedEvent()
        {
            var targetPack   = s_CurrentEvent.targetPack;
            var embedRequest = s_CurrentEvent.request as EmbedRequest;

            ConditionalLog($"Complete Embed Event {targetPack.DisplayName}");

            if (embedRequest == null)
            {
                return;
            }

            if (embedRequest.Status == StatusCode.Failure)
            {
                targetPack.InstallStatus &= ~InstallationStatus.Installing;
                OnInstallContent?.Invoke(false, $"Embed: {embedRequest.Error.message}");
                OnContentChanged?.Invoke();
                return;
            }

            FinishInstallation(targetPack);
        }
        /// <summary>
        /// Called when the Package Manager has completed the add request.
        /// Queues up additional actions if the content is set to be installed to the project folder.
        /// </summary>
        static void CompleteAddEvent()
        {
            var targetPack = s_CurrentEvent.targetPack;
            var addRequest = s_CurrentEvent.request as AddRequest;

            if (addRequest == null)
            {
                return;
            }

            if (addRequest.Status == StatusCode.Failure)
            {
                targetPack.InstallStatus &= ~InstallationStatus.Installing;
                OnInstallContent?.Invoke(false, addRequest.Error.message);
                OnContentChanged?.Invoke();
                return;
            }

            switch (targetPack.InstallType)
            {
            case InstallationType.Package:
                FinishInstallation(targetPack);
                break;

            case InstallationType.WriteablePackage:
                EmbedContent(targetPack);
                break;

            case InstallationType.UnityPackage:
                InstallContentSample(targetPack);

                // Update the status so the sample can be found
                UpdateContentStatus();
                break;
            }
        }
        /// <summary>
        /// Attempts to install a package specified within the currently active Content Pack.
        /// </summary>
        static void StartAddEvent()
        {
            // Is the package already installed? Do nothing
            // Otherwise, start an add action
            if (s_CurrentEvent.targetPack.InstallStatus.HasFlag(InstallationStatus.Installed) && !s_CurrentEvent.targetPack.InstallStatus.HasFlag(InstallationStatus.DifferentVersion))
            {
                OnInstallContent?.Invoke(true, $"{s_CurrentEvent.targetPack.PackageName} already installed");
                s_CurrentEvent = null;
                return;
            }

            s_CurrentEvent.targetPack.InstallStatus |= InstallationStatus.Locked;
            var currentUrl = s_CurrentEvent.targetPack.Url.ToLower();

            if (currentUrl.StartsWith(k_PackageReferenceKey))
            {
                // Local content packs can be in embedded packages, which is fine
                // Or they can be in the library folder, in which case we need to copy them to a temporary folder for installation
                currentUrl = Path.GetFullPath(currentUrl).ToLower();

                if (currentUrl.StartsWith(s_CachedPackagePath))
                {
                    try
                    {
                        // The content pack is located within the library folder - we must copy it to a temporary folder
                        var localPackageInfo = new DirectoryInfo(currentUrl);
                        if (localPackageInfo.Exists)
                        {
                            var localFolder = localPackageInfo.Name;
                            var targetPath  = $"{Path.GetDirectoryName(Application.dataPath)}{Path.DirectorySeparatorChar}{k_LocalPackagePath}{Path.DirectorySeparatorChar}{localFolder}";

                            Directory.CreateDirectory(targetPath);
                            FileUtil.ReplaceDirectory(currentUrl.Replace(Path.DirectorySeparatorChar, '/'), targetPath.Replace(Path.DirectorySeparatorChar, '/'));
                            currentUrl = $"file:{ToRelativeUnityPath(targetPath)}";
                        }
                        else
                        {
                            OnInstallContent?.Invoke(false, $"{s_CurrentEvent.targetPack.PackageName} cannot be found");
                            s_CurrentEvent = null;
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        OnInstallContent?.Invoke(false, $"Exception installing {s_CurrentEvent?.targetPack.PackageName}:{ex.Message}");
                        s_CurrentEvent = null;
                        return;
                    }
                }
                else
                {
                    // Convert the packages path to a relative path for version control niceness
                    currentUrl = $"file:{ToRelativeUnityPath(Path.GetFullPath(currentUrl))}";
                }
            }

            if (!string.IsNullOrEmpty(s_CurrentEvent.targetPack.AutoVersion))
            {
                currentUrl += $"@{s_CurrentEvent.targetPack.AutoVersion}";
            }

            s_CurrentEvent.request = Client.Add(currentUrl);
            OnContentChanged?.Invoke();
        }