using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Media;
using Ursa.Controls.OverlayShared;
using Avalonia.Styling;
using Irihi.Avalonia.Shared.Shapes;

namespace Ursa.Controls;

public partial class OverlayDialogHost: Canvas
{
    private static readonly Animation _maskAppearAnimation;
    private static readonly Animation _maskDisappearAnimation;

    private readonly List<DialogPair> _layers = new List<DialogPair>();
    
    private class DialogPair
    {
        internal PureRectangle? Mask;
        internal OverlayFeedbackElement Element;
        internal bool Modal;

        public DialogPair(PureRectangle? mask, OverlayFeedbackElement element, bool modal = true)
        {
            Mask = mask;
            Element = element;
            Modal = modal;
        }
    }

    private int _modalCount = 0;
    
    

    public static readonly DirectProperty<OverlayDialogHost, bool> HasModalProperty = AvaloniaProperty.RegisterDirect<OverlayDialogHost, bool>(
        nameof(HasModal), o => o.HasModal);
    private bool _hasModal;
    public bool HasModal
    {
        get => _hasModal;
        private set => SetAndRaise(HasModalProperty, ref _hasModal, value);
    }
    
    static OverlayDialogHost()
    {
        ClipToBoundsProperty.OverrideDefaultValue<OverlayDialogHost>(true);
        _maskAppearAnimation = CreateOpacityAnimation(true);
        _maskDisappearAnimation = CreateOpacityAnimation(false);
    }
    
    private static Animation CreateOpacityAnimation(bool appear)
    {
        var animation = new Animation();
        animation.FillMode = FillMode.Forward;
        var keyFrame1 = new KeyFrame{ Cue = new Cue(0.0) };
        keyFrame1.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 0.0 : 1.0 });
        var keyFrame2 = new KeyFrame{ Cue = new Cue(1.0) };
        keyFrame2.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 1.0 : 0.0 });
        animation.Children.Add(keyFrame1);
        animation.Children.Add(keyFrame2);
        animation.Duration = TimeSpan.FromSeconds(0.2);
        return animation;
    }
    
    public string? HostId { get; set; }
    
    public DataTemplates DialogDataTemplates { get; set; } = new DataTemplates();
    
    public static readonly StyledProperty<IBrush?> OverlayMaskBrushProperty =
        AvaloniaProperty.Register<OverlayDialogHost, IBrush?>(
            nameof(OverlayMaskBrush));

    public IBrush? OverlayMaskBrush
    {
        get => GetValue(OverlayMaskBrushProperty);
        set => SetValue(OverlayMaskBrushProperty, value);
    }
    
    private PureRectangle CreateOverlayMask(bool modal, bool canCloseOnClick)
    {
        PureRectangle rec = new()
        {
            Width = this.Bounds.Width,
            Height = this.Bounds.Height,
            IsVisible = true,
        };
        if (modal)
        {
            rec[!PureRectangle.BackgroundProperty] = this[!OverlayMaskBrushProperty];
        }
        else if(canCloseOnClick) 
        { 
            rec.SetCurrentValue(PureRectangle.BackgroundProperty, Brushes.Transparent);
        }
        if (canCloseOnClick)
        {
            rec.AddHandler(PointerReleasedEvent, ClickMaskToCloseDialog);
        }
        return rec;
    }
    
    private void ClickMaskToCloseDialog(object sender, PointerReleasedEventArgs e)
    {
        if (sender is PureRectangle border)
        {
            var layer = _layers.FirstOrDefault(a => a.Mask == border);
            if (layer is not null)
            {
                layer.Element.Close();
                border.RemoveHandler(PointerReleasedEvent, ClickMaskToCloseDialog);
            }
        }
    }
    
    protected sealed override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnAttachedToVisualTree(e);
        OverlayDialogManager.RegisterHost(this, HostId);
    }
    
    protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
    {
        while (_layers.Count>0)
        {
            _layers[0].Element.Close();
        }
        OverlayDialogManager.UnregisterHost(HostId);
        base.OnDetachedFromVisualTree(e);
    }
    
    
    protected sealed override void OnSizeChanged(SizeChangedEventArgs e)
    {
        base.OnSizeChanged(e);
        for (int i = 0; i < _layers.Count; i++)
        {
            if (_layers[i].Mask is { } rect)
            {
                rect.Width = this.Bounds.Width;
                rect.Height = this.Bounds.Height;
            }
            if (_layers[i].Element is DialogControlBase d)
            {
                ResetDialogPosition(d, e.NewSize);
            }
            else if (_layers[i].Element is DrawerControlBase drawer)
            {
                ResetDrawerPosition(drawer, e.NewSize);
            }
        }
    }
    
    private void ResetZIndices()
    {
        int index = 0;
        for (int i = 0; i < _layers.Count; i++)
        {
            if(_layers[i].Mask is { } mask)
            {
                mask.ZIndex = index;
                index++;
            }
            if(_layers[i].Element is { } dialog)
            {
                dialog.ZIndex = index;
                index++;
            }
        }
    }
    
    internal IDataTemplate? GetDataTemplate(object? o)
    {
        if (o is null) return null;
        IDataTemplate? result = null;
        var templates = this.DialogDataTemplates;
        result = templates.FirstOrDefault(a => a.Match(o));
        if (result != null) return result;
        var keys = this.Resources.Keys;
        foreach (var key in keys)
        {
            if (Resources.TryGetValue(key, out var value) && value is IDataTemplate t)
            {
                result = t;
                break;
            }
        }
        return result;
    }
}