Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Host
Imports Nexamas.UI.Layout
Imports SkiaSharp

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Official window geometry surface for DPI and viewport conversions.
    ''' This class does not create any runtime; it delegates to the owning MASSkiaRootHost.
    ''' </summary>
    Public NotInheritable Class MASApplicationWindowGeometry

        Private ReadOnly _rootHost As MASSkiaRootHost

        Private ReadOnly Property RootHost As MASSkiaRootHost
            Get
                _rootHost.ThrowIfDisposedCore()
                Return _rootHost
            End Get
        End Property

        Friend Sub New(rootHost As MASSkiaRootHost)
            If rootHost Is Nothing Then Throw New ArgumentNullException(NameOf(rootHost))
            _rootHost = rootHost
        End Sub

        Public ReadOnly Property SurfaceSizePx As SKSizeI
            Get
                Dim measuredSize As SKSizeI = RootHost.SurfaceSizePx

                If measuredSize.Width > 0 AndAlso measuredSize.Height > 0 Then
                    Return measuredSize
                End If

                ' During the first Setup/Rebuild pass the SK paint surface may not have
                ' reported its measured size yet. Use the RootHost viewport helper as the
                ' official fallback because it already knows how to read the hosted Skia
                ' control without exposing that native control to SDK consumers.
                Dim viewport As SKRect = RootHost.GetSurfaceViewportPx()

                If Not viewport.IsEmpty AndAlso viewport.Width > 0.0F AndAlso viewport.Height > 0.0F Then
                    Return New SKSizeI(
                        CInt(Math.Ceiling(CDbl(viewport.Width))),
                        CInt(Math.Ceiling(CDbl(viewport.Height)))
                    )
                End If

                Return SKSizeI.Empty
            End Get
        End Property

        Public ReadOnly Property Dpi As Single
            Get
                Return RootHost.GetDpi()
            End Get
        End Property

        ''' <summary>
        ''' Returns the usable surface viewport in physical pixels.
        ''' The optional top inset is expressed in logical units and converted by the RootHost.
        ''' </summary>
        Public Function SurfaceViewportPx(Optional topInsetLogical As Single = 0.0F) As SKRect
            Return RootHost.GetSurfaceViewportPx(topInsetLogical)
        End Function

        ''' <summary>
        ''' Converts logical bounds to physical-pixel bounds using the owning window DPI.
        ''' Use this before passing anchor rectangles to float/popup APIs that explicitly require *Px.
        ''' </summary>
        Public Function LogicalToPx(boundsLogical As SKRect) As SKRect
            Return RootHost.LogicalToPx(boundsLogical)
        End Function

        ''' <summary>
        ''' Converts physical-pixel bounds back to logical bounds using the owning window DPI.
        ''' </summary>
        Public Function PxToLogical(boundsPx As SKRect) As SKRect
            Return RootHost.PxToLogical(boundsPx)
        End Function

        ''' <summary>
        ''' Intent-revealing alias for LogicalToPx when preparing popup/menu/float anchors.
        ''' </summary>
        Public Function AnchorLogicalToPx(anchorRectLogical As SKRect) As SKRect
            Return LogicalToPx(anchorRectLogical)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned outer bounds in logical/DIP units.
        ''' This is a geometry gateway over MASLayoutSlot; consumers do not read slots directly.
        ''' </summary>
        Public Function ControlOuterBoundsLogical(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolveLogical(control, MASLayoutSlotBoundsRole.Outer)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned visual bounds in logical/DIP units.
        ''' Use this for rendering adorners whose visuals must include overflow facts.
        ''' </summary>
        Public Function ControlVisualBoundsLogical(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolveLogical(control, MASLayoutSlotBoundsRole.Visual)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned content bounds in logical/DIP units.
        ''' Use this when placing owned content inside a surface/control.
        ''' </summary>
        Public Function ControlContentBoundsLogical(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolveLogical(control, MASLayoutSlotBoundsRole.Content)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned hit-test bounds in logical/DIP units.
        ''' </summary>
        Public Function ControlHitBoundsLogical(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolveLogical(control, MASLayoutSlotBoundsRole.Hit)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned focus-ring bounds in logical/DIP units.
        ''' </summary>
        Public Function ControlFocusBoundsLogical(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolveLogical(control, MASLayoutSlotBoundsRole.Focus)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned float-anchor bounds in logical/DIP units.
        ''' Use this for popup/dropdown/tooltip anchors instead of BoundsLogical.
        ''' </summary>
        Public Function ControlFloatAnchorBoundsLogical(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolveFloatAnchorLogical(control)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned outer bounds in physical pixels.
        ''' </summary>
        Public Function ControlOuterBoundsPx(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolvePixel(control, MASLayoutSlotBoundsRole.Outer, Dpi)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned visual bounds in physical pixels.
        ''' </summary>
        Public Function ControlVisualBoundsPx(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolvePixel(control, MASLayoutSlotBoundsRole.Visual, Dpi)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned content bounds in physical pixels.
        ''' </summary>
        Public Function ControlContentBoundsPx(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolvePixel(control, MASLayoutSlotBoundsRole.Content, Dpi)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned hit-test bounds in physical pixels.
        ''' </summary>
        Public Function ControlHitBoundsPx(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolvePixel(control, MASLayoutSlotBoundsRole.Hit, Dpi)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned focus-ring bounds in physical pixels.
        ''' </summary>
        Public Function ControlFocusBoundsPx(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolvePixel(control, MASLayoutSlotBoundsRole.Focus, Dpi)
        End Function

        ''' <summary>
        ''' Returns the control's layout-owned float-anchor bounds in physical pixels.
        ''' This is the safest gateway for FloatRuntime and popup anchor rectangles.
        ''' </summary>
        Public Function ControlFloatAnchorBoundsPx(control As MASControlBase) As SKRect
            Return MASLayoutSlotBoundsResolver.ResolveFloatAnchorPixel(control, Dpi)
        End Function

        ''' <summary>
        ''' Intent-revealing alias for ControlFloatAnchorBoundsPx.
        ''' </summary>
        Public Function ControlAnchorPx(control As MASControlBase) As SKRect
            Return ControlFloatAnchorBoundsPx(control)
        End Function

        ''' <summary>
        ''' Returns True only when the surface viewport can be safely used for placement.
        ''' </summary>
        Public Function TryGetSurfaceViewportPx(ByRef viewportPx As SKRect,
                                                Optional topInsetLogical As Single = 0.0F) As Boolean
            viewportPx = SurfaceViewportPx(topInsetLogical)
            Return IsUsable(viewportPx)
        End Function

        Public Function IsUsable(bounds As SKRect,
                                 Optional minWidth As Single = 1.0F,
                                 Optional minHeight As Single = 1.0F) As Boolean
            Return MASSkiaViewportTools.IsUsable(bounds, minWidth, minHeight)
        End Function

    End Class

End Namespace
