using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Media;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
using System;
using System.Threading.Tasks;

namespace Harmonia.UI.Controls;

public class AnimatedMenuFlyout : MenuFlyout
{
    protected override Control CreatePresenter()
    {
        Control presenter = base.CreatePresenter();
        presenter.AttachedToVisualTree += OnPresenterAttachedToVisualTree;

        return presenter;
    }

    private async void OnPresenterAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
    {
        if (sender is not MenuFlyoutPresenter menuFlyoutPresenter)
            return;

        await ApplyAnimationAsync(menuFlyoutPresenter);
    }

    private async Task ApplyAnimationAsync(MenuFlyoutPresenter presenter)
    {
        double translateYStart = ShouldSlideDown(presenter) ? - 100 : 100;

        Animation animation = new()
        {
            Duration = TimeSpan.FromMilliseconds(200),
            Children =
            {
                new KeyFrame
                {
                    Cue = new Cue(0f),
                    Setters =
                    {
                        new Setter(Visual.OpacityProperty, 0.0),
                        new Setter(TranslateTransform.YProperty, translateYStart)
                    }
                },
                new KeyFrame
                {
                    Cue = new Cue(1f),
                    Setters =
                    {
                        new Setter(Visual.OpacityProperty, 1.0),
                        new Setter(TranslateTransform.YProperty, 0.0)
                    }
                }
            }
        };

        await animation.RunAsync(presenter);
    }

    private static bool ShouldSlideDown(MenuFlyoutPresenter presenter)
    {
        if (presenter.Parent is not Control targetControl)
            return true;

        Rect? topLevelBounds = GetTopLevelBounds();

        if (topLevelBounds == null)
            return true;

        Rect targetBounds = targetControl.Bounds;

        double availableSpaceBelow = topLevelBounds.Value.Height - (targetBounds.Y + targetBounds.Height);
        double availableSpaceAbove = targetBounds.Y;

        return availableSpaceBelow >= availableSpaceAbove; // Slide down if more space below
    }

    private static Rect? GetTopLevelBounds()
    {
        //Desktop
        if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            return desktop.MainWindow?.Bounds;
        }
        //Android (and iOS?)
        else if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
        {
            //IRenderRoot? visualRoot = singleViewPlatform.MainView?.GetVisualRoot();

            Visual? visualRoot = singleViewPlatform.MainView?.GetPresentationSource()?.RootVisual;

            if (visualRoot is TopLevel topLevel)
            {
                return topLevel.Bounds;
            }
        }

        return null;
    }
}