using Microsoft.Graphics.Canvas.Effects;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;

namespace NixExampleWinUI.Helpers;

/// <summary>
/// Brush for tiling an image using Win2D. 
/// Based on https://www.codeproject.com/Tips/1236452/UWP-TiledBrush
/// </summary>
public class TiledBrush : XamlCompositionBrushBase
{
    #region Properties

    #region Compositor

    private static Compositor Compositor
    {
        get 
        {
            return CompositionTarget.GetCompositorForCurrentThread(); 
        }
    }

    #endregion

    #region TextureUri

    public Uri TextureUri
    {
        get 
        { 
            return (Uri) GetValue(TextureUriProperty); 
        }
        set 
        { 
            SetValue(TextureUriProperty, value); 
        }
    }

    public static readonly DependencyProperty TextureUriProperty =
        DependencyProperty.Register(
            "TextureUri", 
            typeof(Uri), 
            typeof(TiledBrush), 
            new PropertyMetadata(0, OnTextureUriChanged));

    private static void OnTextureUriChanged(
        DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
    {
        var tiledBrush = d as TiledBrush;
        if (tiledBrush is not null && tiledBrush.CompositionBrush is not null)
            tiledBrush.UpdateSurface(tiledBrush.TextureUri);
    }

    #endregion

    #endregion

    #region Methods

    #region UpdateSurface

    private CompositionSurfaceBrush? surfaceBrush;
    private LoadedImageSurface? surface;

    private void UpdateSurface(Uri uri)
    {
        using (surface)
        {
            surface = uri is not null ? 
                LoadedImageSurface.StartLoadFromUri(uri) : 
                null;
            if (surfaceBrush is not null) 
                surfaceBrush.Surface = surface;
        }
    }

    #endregion

    #region OnConnected

    private CompositionEffectFactory? borderEffectFactory;
    private CompositionEffectBrush? borderEffectBrush;
    private BorderEffect? borderEffect;

    protected override void OnConnected()
    {
        base.OnConnected();
        if (CompositionBrush is null)
        {
            surfaceBrush = Compositor.CreateSurfaceBrush();
            surfaceBrush.Stretch = CompositionStretch.None;
            UpdateSurface(TextureUri);
            borderEffect = new BorderEffect()
            {
                Source = new CompositionEffectSourceParameter("source"),
                ExtendX = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap,
                ExtendY = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap
            };
            borderEffectFactory = Compositor.CreateEffectFactory(borderEffect);
            borderEffectBrush = borderEffectFactory.CreateBrush();
            borderEffectBrush.SetSourceParameter("source", surfaceBrush);
            CompositionBrush = borderEffectBrush;
        }
    }

    #endregion

    #region OnDisconnected

    protected override void OnDisconnected()
    {
        using (borderEffectFactory)
        using (borderEffectBrush)
        using (borderEffect)
        using (CompositionBrush)
        using (surfaceBrush)
        using (surface)
        {
            CompositionBrush = null;
            borderEffectFactory = null;
            borderEffectBrush = null;
            borderEffect = null;
            surfaceBrush = null;
            surface = null;
        }
        base.OnDisconnected();
    }

    #endregion

    #endregion
}