namespace RioJoy.Core.Overlay;

/// <summary>Which part of a region's box a drag grabs.</summary>
public enum OverlayBoxHandle
{
    None,
    Move,
    North, South, East, West,
    NorthWest, NorthEast, SouthWest, SouthEast,
}

/// <summary>
/// The pure math behind the wallpaper maker's box editor: which handle a
/// template-space point grabs on a region, and the region's new rectangle after
/// dragging that handle. Kept UI-free so it is unit-testable; the canvas only
/// converts mouse pixels to template space and applies the result.
/// </summary>
public static class OverlayRegionEdit
{
    /// <summary>
    /// The handle at a template-space point: corners win over edges, edges over
    /// <see cref="OverlayBoxHandle.Move"/> (anywhere else inside the box), all
    /// within <paramref name="tolerance"/> template pixels of the border.
    /// </summary>
    public static OverlayBoxHandle HitHandle(OverlayRegion region, double x, double y, double tolerance)
    {
        if (region is null) throw new ArgumentNullException(nameof(region));

        double l = region.X, t = region.Y, r = region.X + region.Width, b = region.Y + region.Height;
        if (x < l - tolerance || x > r + tolerance || y < t - tolerance || y > b + tolerance)
            return OverlayBoxHandle.None;

        bool west = Math.Abs(x - l) <= tolerance;
        bool east = Math.Abs(x - r) <= tolerance;
        bool north = Math.Abs(y - t) <= tolerance;
        bool south = Math.Abs(y - b) <= tolerance;

        if (north && west) return OverlayBoxHandle.NorthWest;
        if (north && east) return OverlayBoxHandle.NorthEast;
        if (south && west) return OverlayBoxHandle.SouthWest;
        if (south && east) return OverlayBoxHandle.SouthEast;
        if (north) return OverlayBoxHandle.North;
        if (south) return OverlayBoxHandle.South;
        if (west) return OverlayBoxHandle.West;
        if (east) return OverlayBoxHandle.East;

        return x >= l && x < r && y >= t && y < b ? OverlayBoxHandle.Move : OverlayBoxHandle.None;
    }

    /// <summary>
    /// The rectangle after dragging <paramref name="handle"/> by (dx, dy) template
    /// pixels from the drag-start rectangle. Resizes clamp so width/height never
    /// drop below <paramref name="minSize"/> (the opposite edge stays put).
    /// </summary>
    public static (double X, double Y, double Width, double Height) Apply(
        double x, double y, double width, double height,
        OverlayBoxHandle handle, double dx, double dy, double minSize = 4)
    {
        double l = x, t = y, r = x + width, b = y + height;

        switch (handle)
        {
            case OverlayBoxHandle.Move:
                return (x + dx, y + dy, width, height);
            case OverlayBoxHandle.West:
            case OverlayBoxHandle.NorthWest:
            case OverlayBoxHandle.SouthWest:
                l = Math.Min(l + dx, r - minSize);
                break;
        }

        switch (handle)
        {
            case OverlayBoxHandle.East:
            case OverlayBoxHandle.NorthEast:
            case OverlayBoxHandle.SouthEast:
                r = Math.Max(r + dx, l + minSize);
                break;
        }

        switch (handle)
        {
            case OverlayBoxHandle.North:
            case OverlayBoxHandle.NorthWest:
            case OverlayBoxHandle.NorthEast:
                t = Math.Min(t + dy, b - minSize);
                break;
            case OverlayBoxHandle.South:
            case OverlayBoxHandle.SouthWest:
            case OverlayBoxHandle.SouthEast:
                b = Math.Max(b + dy, t + minSize);
                break;
        }

        return (l, t, r - l, b - t);
    }
}
