﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;

namespace Microsoft.CodeAnalysis.Editor.QuickInfo
{
    internal partial class ContentControlService
    {
        /// <summary>
        /// Class which allows us to provide a delay-created tooltip for our reference entries.
        /// </summary>
        private class LazyToolTip : ForegroundThreadAffinitizedObject
        {
            private readonly Func<DisposableToolTip> _createToolTip;
            private readonly FrameworkElement _element;

            private DisposableToolTip _disposableToolTip;

            private LazyToolTip(
                IThreadingContext threadingContext,
                FrameworkElement element,
                Func<DisposableToolTip> createToolTip)
                : base(threadingContext, assertIsForeground: true)
            {
                _element = element;
                _createToolTip = createToolTip;

                // Set ourselves as the tooltip of this text block.  This will let WPF know that 
                // it should attempt to show tooltips here.  When WPF wants to show the tooltip 
                // though we'll hear about it "ToolTipOpening".  When that happens, we'll swap
                // out ourselves with a real tooltip that is lazily created.  When that tooltip
                // is the dismissed, we'll release the resources associated with it and we'll
                // reattach ourselves.
                _element.ToolTip = this;

                element.ToolTipOpening += this.OnToolTipOpening;
                element.ToolTipClosing += this.OnToolTipClosing;
            }

            public static void AttachTo(FrameworkElement element, IThreadingContext threadingContext, Func<DisposableToolTip> createToolTip)
                => new LazyToolTip(threadingContext, element, createToolTip);

            private void OnToolTipOpening(object sender, ToolTipEventArgs e)
            {
                try
                {
                    AssertIsForeground();

                    Debug.Assert(_element.ToolTip == this);
                    Debug.Assert(_disposableToolTip == null);

                    _disposableToolTip = _createToolTip();
                    _element.ToolTip = _disposableToolTip.ToolTip;
                }
                catch (Exception ex) when (FatalError.ReportAndCatch(ex))
                {
                    // Do nothing, since this is a WPF event handler and propagating the exception would cause a crash
                }
            }

            private void OnToolTipClosing(object sender, ToolTipEventArgs e)
            {
                try
                {
                    AssertIsForeground();

                    Debug.Assert(_disposableToolTip != null);
                    Debug.Assert(_element.ToolTip == _disposableToolTip.ToolTip);

                    _element.ToolTip = this;

                    _disposableToolTip.Dispose();
                    _disposableToolTip = null;
                }
                catch (Exception ex) when (FatalError.ReportAndCatch(ex))
                {
                    // Do nothing, since this is a WPF event handler and propagating the exception would cause a crash
                }
            }
        }
    }
}
