using Aprillz.MewUI.Rendering;

namespace Aprillz.MewUI.Controls;

/// <summary>
/// WPF-like decorator that draws background/border and hosts a single child element.
/// </summary>
public sealed class Border : Control, IVisualTreeHost
{
    public static readonly MewProperty<bool> ClipToBoundsProperty =
        MewProperty<bool>.Register<Border>(nameof(ClipToBounds), false, MewPropertyOptions.AffectsRender);

    protected override UIElement? OnHitTest(Point point)
    {
        if (!IsVisible || !IsHitTestVisible || !IsEffectivelyEnabled)
        {
            return null;
        }

        if (Child != null)
        {
            var hit = Child.HitTest(point);
            if (hit != null)
            {
                return hit;
            }
        }

        return base.OnHitTest(point);
    }

    public UIElement? Child
    {
        get;
        set
        {
            if (field == value)
            {
                return;
            }

            if (field != null)
            {
                field.Parent = null;
            }

            field = value;
            if (field != null)
            {
                field.Parent = this;
            }

            InvalidateMeasure();
            InvalidateVisual();
        }
    }

    public bool ClipToBounds
    {
        get => GetValue(ClipToBoundsProperty);
        set => SetValue(ClipToBoundsProperty, value);
    }

    protected override Size MeasureContent(Size availableSize)
    {
        var border = BorderThickness > 0 ? new Thickness(BorderThickness) : Thickness.Zero;
        var slot = availableSize.Deflate(border).Deflate(Padding);

        if (Child == null)
        {
            return new Size(0, 0).Inflate(Padding).Inflate(border);
        }

        Child.Measure(slot);
        return Child.DesiredSize.Inflate(Padding).Inflate(border);
    }

    protected override void ArrangeContent(Rect bounds)
    {
        var snapped = GetSnappedBorderBounds(bounds);
        var border = BorderThickness > 0 ? new Thickness(BorderThickness) : Thickness.Zero;
        var inner = snapped.Deflate(border).Deflate(Padding);
        Child?.Arrange(inner);
    }

    protected override void OnRender(IGraphicsContext context)
    {
        var radius = Math.Max(0, CornerRadius);

        DrawBackgroundAndBorder(context, Bounds, Background, BorderBrush, radius);

        if (Child != null)
        {
            var border = BorderThickness > 0 ? new Thickness(BorderThickness) : Thickness.Zero;
            var inner = GetSnappedBorderBounds(Bounds).Deflate(border).Deflate(Padding);
            var dpiScale = GetDpi() / 96.0;
            if (dpiScale <= 0)
            {
                dpiScale = 1.0;
            }

            if (ClipToBounds)
            {
                context.Save();
                context.SetClip(LayoutRounding.MakeClipRect(inner, dpiScale));
            }

            Child.Render(context);

            if (ClipToBounds)
            {
                context.Restore();
            }
        }
    }

    bool IVisualTreeHost.VisitChildren(Func<Element, bool> visitor)
        => Child == null || visitor(Child);
}
