Option Strict On
Option Explicit On

Imports SkiaSharp

Namespace Nexamas.UI.FloatRuntime

    ' ============================================================
    ' MASFloatRuntime Contract
    ' Class: MASFloatPresentationEngine
    '
    ' Purpose:
    '   Resolves pixel-space placement and bounds for a Float.
    '
    ' Responsibility:
    '   Converts MASFloatPresentation placement intent plus Request geometry
    '   into final pixel-space bounds.
    '
    ' Owns:
    '   No runtime state.
    '   No host state.
    '   No lifecycle state.
    '
    ' Does Not Own:
    '   Show authorization.
    '   Close authorization.
    '   Transition animation.
    '   Drawing.
    '   Input routing.
    '   Focus ownership.
    '   ThemeContext.
    '
    ' Allowed Dependencies:
    '   MASFloatResolvedRequest.
    '   MASFloatPresentation.
    '   MASFloatPlacement.
    '   SkiaSharp.SKRect.
    '
    ' Forbidden Dependencies:
    '   Legacy Overlay classes.
    '   OverlayHost.
    '   OverlayManager.
    '   OverlayContent.
    '   OverlayLayer.
    '   OverlayService.
    '   MASFloatCoordinator lifecycle execution.
    '   MASFloatHost visual implementation details.
    '   Feature-owned lifecycle services.
    '
    ' Lifecycle Role:
    '   Called by Coordinator/Host flow after policy accepts a show request
    '   and before host insertion.
    '
    ' Threading:
    '   Stateless and reusable.
    '   Caller owns UI-thread marshaling when used inside lifecycle flow.
    '
    ' Mutation Rules:
    '   Must not mutate resolved requests.
    '   Must not mutate host state.
    '
    ' Invariants:
    '   All geometry is pixel-space.
    '   Ambiguous Bounds names are forbidden.
    '   Anchor placements use AnchorRectPx.
    '   ExplicitBounds uses InitialBoundsPx when callers have already resolved popup geometry.
    '   AvailableBoundsPx defines the allowed visual area.
    '
    ' Failure Rules:
    '   Missing resolved request returns SKRect.Empty.
    '   Missing or empty available bounds falls back to InitialBoundsPx
    '   where possible.
    '   Final bounds are clamped to AvailableBoundsPx when available.
    ' ============================================================
    Friend NotInheritable Class MASFloatPresentationEngine

        Friend Function ResolveBoundsPx(
            resolvedRequest As MASFloatResolvedRequest,
            hostAvailableBoundsPx As SKRect
        ) As SKRect
            If resolvedRequest Is Nothing OrElse
               resolvedRequest.Request Is Nothing Then
                Return SKRect.Empty
            End If

            Dim request As MASFloatRequest = resolvedRequest.Request
            Dim presentation As MASFloatPresentation =
                If(resolvedRequest.Presentation, New MASFloatPresentation())

            Dim availablePx As SKRect = NormalizeRect(request.AvailableBoundsPx)
            If Not HasArea(availablePx) Then
                availablePx = NormalizeRect(hostAvailableBoundsPx)
            End If
            Dim initialPx As SKRect = NormalizeRect(request.InitialBoundsPx)
            Dim anchorPx As SKRect = NormalizeRect(request.AnchorRectPx)

            Select Case presentation.Placement
                Case MASFloatPlacement.Fullscreen
                    Return ResolveFullscreenPx(availablePx, initialPx)

                Case MASFloatPlacement.ExplicitBounds
                    Return ResolveExplicitBoundsPx(availablePx, initialPx)

                Case MASFloatPlacement.Center
                    Return ResolveCenterPx(availablePx, initialPx)

                Case MASFloatPlacement.DockLeft
                    Return ResolveDockLeftPx(availablePx, initialPx)

                Case MASFloatPlacement.DockRight
                    Return ResolveDockRightPx(availablePx, initialPx)

                Case MASFloatPlacement.DockTop
                    Return ResolveDockTopPx(availablePx, initialPx)

                Case MASFloatPlacement.DockBottom
                    Return ResolveDockBottomPx(availablePx, initialPx)

                Case MASFloatPlacement.AnchorBelow
                    Return ResolveAnchorBelowPx(availablePx, initialPx, anchorPx)

                Case MASFloatPlacement.AnchorAbove
                    Return ResolveAnchorAbovePx(availablePx, initialPx, anchorPx)

                Case MASFloatPlacement.AnchorLeft
                    Return ResolveAnchorLeftPx(availablePx, initialPx, anchorPx)

                Case MASFloatPlacement.AnchorRight
                    Return ResolveAnchorRightPx(availablePx, initialPx, anchorPx)

                Case Else
                    Return ResolveCenterPx(availablePx, initialPx)
            End Select
        End Function

        Private Shared Function ResolveFullscreenPx(
            availablePx As SKRect,
            initialPx As SKRect
        ) As SKRect
            If HasArea(availablePx) Then
                Return availablePx
            End If

            Return initialPx
        End Function


        Private Shared Function ResolveExplicitBoundsPx(
            availablePx As SKRect,
            initialPx As SKRect
        ) As SKRect
            If Not HasArea(initialPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            If Not HasArea(availablePx) Then
                Return initialPx
            End If

            Return ClampToAvailablePx(initialPx, availablePx)
        End Function

        Private Shared Function ResolveCenterPx(
            availablePx As SKRect,
            initialPx As SKRect
        ) As SKRect
            If Not HasArea(initialPx) Then
                If HasArea(availablePx) Then
                    Return availablePx
                End If

                Return SKRect.Empty
            End If

            If Not HasArea(availablePx) Then
                Return initialPx
            End If

            Dim width As Single = System.Math.Min(initialPx.Width, availablePx.Width)
            Dim height As Single = System.Math.Min(initialPx.Height, availablePx.Height)

            Dim left As Single = availablePx.Left + ((availablePx.Width - width) / 2.0F)
            Dim top As Single = availablePx.Top + ((availablePx.Height - height) / 2.0F)

            Return ClampToAvailablePx(
                New SKRect(left, top, left + width, top + height),
                availablePx
            )
        End Function

        Private Shared Function ResolveDockLeftPx(
            availablePx As SKRect,
            initialPx As SKRect
        ) As SKRect
            If Not HasArea(availablePx) Then
                Return initialPx
            End If

            Dim width As Single =
                If(HasArea(initialPx), System.Math.Min(initialPx.Width, availablePx.Width), availablePx.Width)

            Return New SKRect(
                availablePx.Left,
                availablePx.Top,
                availablePx.Left + width,
                availablePx.Bottom
            )
        End Function

        Private Shared Function ResolveDockRightPx(
            availablePx As SKRect,
            initialPx As SKRect
        ) As SKRect
            If Not HasArea(availablePx) Then
                Return initialPx
            End If

            Dim width As Single =
                If(HasArea(initialPx), System.Math.Min(initialPx.Width, availablePx.Width), availablePx.Width)

            Return New SKRect(
                availablePx.Right - width,
                availablePx.Top,
                availablePx.Right,
                availablePx.Bottom
            )
        End Function

        Private Shared Function ResolveDockTopPx(
            availablePx As SKRect,
            initialPx As SKRect
        ) As SKRect
            If Not HasArea(availablePx) Then
                Return initialPx
            End If

            Dim height As Single =
                If(HasArea(initialPx), System.Math.Min(initialPx.Height, availablePx.Height), availablePx.Height)

            Return New SKRect(
                availablePx.Left,
                availablePx.Top,
                availablePx.Right,
                availablePx.Top + height
            )
        End Function

        Private Shared Function ResolveDockBottomPx(
            availablePx As SKRect,
            initialPx As SKRect
        ) As SKRect
            If Not HasArea(availablePx) Then
                Return initialPx
            End If

            Dim height As Single =
                If(HasArea(initialPx), System.Math.Min(initialPx.Height, availablePx.Height), availablePx.Height)

            Return New SKRect(
                availablePx.Left,
                availablePx.Bottom - height,
                availablePx.Right,
                availablePx.Bottom
            )
        End Function

        Private Shared Function ResolveAnchorBelowPx(
            availablePx As SKRect,
            initialPx As SKRect,
            anchorPx As SKRect
        ) As SKRect
            If Not HasArea(initialPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            If Not HasArea(anchorPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            Dim rect As New SKRect(
                anchorPx.Left,
                anchorPx.Bottom,
                anchorPx.Left + initialPx.Width,
                anchorPx.Bottom + initialPx.Height
            )

            Return ClampToAvailablePx(rect, availablePx)
        End Function

        Private Shared Function ResolveAnchorAbovePx(
            availablePx As SKRect,
            initialPx As SKRect,
            anchorPx As SKRect
        ) As SKRect
            If Not HasArea(initialPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            If Not HasArea(anchorPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            Dim rect As New SKRect(
                anchorPx.Left,
                anchorPx.Top - initialPx.Height,
                anchorPx.Left + initialPx.Width,
                anchorPx.Top
            )

            Return ClampToAvailablePx(rect, availablePx)
        End Function

        Private Shared Function ResolveAnchorLeftPx(
            availablePx As SKRect,
            initialPx As SKRect,
            anchorPx As SKRect
        ) As SKRect
            If Not HasArea(initialPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            If Not HasArea(anchorPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            Dim rect As New SKRect(
                anchorPx.Left - initialPx.Width,
                anchorPx.Top,
                anchorPx.Left,
                anchorPx.Top + initialPx.Height
            )

            Return ClampToAvailablePx(rect, availablePx)
        End Function

        Private Shared Function ResolveAnchorRightPx(
            availablePx As SKRect,
            initialPx As SKRect,
            anchorPx As SKRect
        ) As SKRect
            If Not HasArea(initialPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            If Not HasArea(anchorPx) Then
                Return ResolveCenterPx(availablePx, initialPx)
            End If

            Dim rect As New SKRect(
                anchorPx.Right,
                anchorPx.Top,
                anchorPx.Right + initialPx.Width,
                anchorPx.Top + initialPx.Height
            )

            Return ClampToAvailablePx(rect, availablePx)
        End Function

        Private Shared Function ClampToAvailablePx(
            rectPx As SKRect,
            availablePx As SKRect
        ) As SKRect
            If Not HasArea(rectPx) Then
                Return SKRect.Empty
            End If

            If Not HasArea(availablePx) Then
                Return rectPx
            End If

            Dim width As Single = System.Math.Min(rectPx.Width, availablePx.Width)
            Dim height As Single = System.Math.Min(rectPx.Height, availablePx.Height)

            Dim left As Single = rectPx.Left
            Dim top As Single = rectPx.Top

            If left < availablePx.Left Then
                left = availablePx.Left
            End If

            If top < availablePx.Top Then
                top = availablePx.Top
            End If

            If left + width > availablePx.Right Then
                left = availablePx.Right - width
            End If

            If top + height > availablePx.Bottom Then
                top = availablePx.Bottom - height
            End If

            Return New SKRect(left, top, left + width, top + height)
        End Function

        Private Shared Function NormalizeRect(rect As SKRect) As SKRect
            Dim left As Single = System.Math.Min(rect.Left, rect.Right)
            Dim right As Single = System.Math.Max(rect.Left, rect.Right)
            Dim top As Single = System.Math.Min(rect.Top, rect.Bottom)
            Dim bottom As Single = System.Math.Max(rect.Top, rect.Bottom)

            Return New SKRect(left, top, right, bottom)
        End Function

        Private Shared Function HasArea(rect As SKRect) As Boolean
            Return rect.Width > 0.0F AndAlso rect.Height > 0.0F
        End Function

    End Class

End Namespace
