Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Components
Imports Nexamas.UI.Host
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports SkiaSharp

Namespace Nexamas.UI.FloatRuntime

    Friend NotInheritable Class MASMessageBoxFloatRequestBuilder

        Private Sub New()
        End Sub

        Friend Shared Function CreateRequest(rootHost As MASSkiaRootHost,
                                             ownerKey As String,
                                             messageBox As MessageBoxRequest,
                                             Optional initialBoundsPx As SKRect = Nothing,
                                             Optional showPolicy As MASFloatShowPolicy = MASFloatShowPolicy.QueueAfterCurrentClosed,
                                             Optional shield As MASFloatShieldOptions = Nothing,
                                             Optional onOpened As Action(Of MASFloatHandle) = Nothing,
                                             Optional onClosed As Action(Of MASFloatClosedEventArgs) = Nothing) As MASFloatRequest

            If rootHost Is Nothing Then
                Throw New ArgumentNullException(NameOf(rootHost))
            End If

            If String.IsNullOrWhiteSpace(ownerKey) Then
                Throw New ArgumentException("MessageBox Float requires a non-empty ownerKey.", NameOf(ownerKey))
            End If

            Dim safeRequest As MessageBoxRequest =
                If(messageBox, New MessageBoxRequest())

            Dim content As New MASMessageBoxFloatContent(
                request:=safeRequest,
                invalidate:=AddressOf rootHost.RequestInvalidate)

            Dim request As New MASFloatRequest() With {
                .FloatKey = MASFloatKeys.MessageBox,
                .OwnerKey = ownerKey.Trim(),
                .Content = content,
                .ShowPolicy = showPolicy,
                .Shield = If(shield, MASFloatShieldOptions.None),
                .OnOpened = onOpened,
                .OnClosed = onClosed
            }

            Dim availablePx As SKRect = rootHost.GetSurfaceViewportPx()

            If IsUsableRect(availablePx) Then
                request.AvailableBoundsPx = availablePx
            End If

            If IsUsableRect(initialBoundsPx) Then
                request.InitialBoundsPx = initialBoundsPx
            Else
                request.InitialBoundsPx =
                    CreateDefaultCenteredBoundsPx(
                        rootHost:=rootHost,
                        availablePx:=availablePx,
                        messageBox:=safeRequest,
                        surface:=content)
            End If

            Return request

        End Function

#Region "Convenience"

        Friend Shared Function Information(rootHost As MASSkiaRootHost,
                                           ownerKey As String,
                                           message As String,
                                           Optional title As String = "Information",
                                           Optional detail As String = "",
                                           Optional onClosed As Action(Of MASFloatClosedEventArgs) = Nothing) As MASFloatRequest

            Return CreateRequest(
                rootHost:=rootHost,
                ownerKey:=ownerKey,
                messageBox:=MessageBoxRequestFactory.Info(
                    message:=message,
                    title:=title,
                    detail:=detail),
                onClosed:=onClosed)

        End Function

        Friend Shared Function Success(rootHost As MASSkiaRootHost,
                                       ownerKey As String,
                                       message As String,
                                       Optional title As String = "Success",
                                       Optional detail As String = "",
                                       Optional onClosed As Action(Of MASFloatClosedEventArgs) = Nothing) As MASFloatRequest

            Return CreateRequest(
                rootHost:=rootHost,
                ownerKey:=ownerKey,
                messageBox:=MessageBoxRequestFactory.Success(
                    message:=message,
                    title:=title,
                    detail:=detail),
                onClosed:=onClosed)

        End Function

        Friend Shared Function Warning(rootHost As MASSkiaRootHost,
                                       ownerKey As String,
                                       message As String,
                                       Optional title As String = "Warning",
                                       Optional detail As String = "",
                                       Optional onClosed As Action(Of MASFloatClosedEventArgs) = Nothing) As MASFloatRequest

            Return CreateRequest(
                rootHost:=rootHost,
                ownerKey:=ownerKey,
                messageBox:=MessageBoxRequestFactory.Warning(
                    message:=message,
                    title:=title,
                    detail:=detail),
                onClosed:=onClosed)

        End Function

        Friend Shared Function [Error](rootHost As MASSkiaRootHost,
                                       ownerKey As String,
                                       message As String,
                                       Optional title As String = "Error",
                                       Optional detail As String = "",
                                       Optional onClosed As Action(Of MASFloatClosedEventArgs) = Nothing) As MASFloatRequest

            Return CreateRequest(
                rootHost:=rootHost,
                ownerKey:=ownerKey,
                messageBox:=MessageBoxRequestFactory.Error(
                    message:=message,
                    title:=title,
                    detail:=detail),
                onClosed:=onClosed)

        End Function

        Friend Shared Function Question(rootHost As MASSkiaRootHost,
                                        ownerKey As String,
                                        title As String,
                                        message As String,
                                        Optional detail As String = "",
                                        Optional onClosed As Action(Of MASFloatClosedEventArgs) = Nothing) As MASFloatRequest

            Return CreateRequest(
                rootHost:=rootHost,
                ownerKey:=ownerKey,
                messageBox:=MessageBoxRequestFactory.Question(
                    title:=title,
                    message:=message,
                    detail:=detail),
                onClosed:=onClosed)

        End Function

        Friend Shared Function Confirm(rootHost As MASSkiaRootHost,
                                       ownerKey As String,
                                       title As String,
                                       message As String,
                                       Optional detail As String = "",
                                       Optional onClosed As Action(Of MASFloatClosedEventArgs) = Nothing) As MASFloatRequest

            Dim box As MessageBoxRequest =
                MessageBoxRequestBuilder.Create(MessageBoxKind.Question).
                    WithTitle(title).
                    WithMessage(message).
                    WithDetail(detail).
                    WithButtonsOKCancel().
                    WithCloseButton(True).
                    Build()

            Return CreateRequest(
                rootHost:=rootHost,
                ownerKey:=ownerKey,
                messageBox:=box,
                onClosed:=onClosed)

        End Function

#End Region

#Region "Helpers"

        Private Shared Function CreateDefaultCenteredBoundsPx(rootHost As MASSkiaRootHost,
                                                              availablePx As SKRect,
                                                              messageBox As MessageBoxRequest,
                                                              surface As IMASFloatSurfaceSizeContract) As SKRect

            If rootHost Is Nothing Then Return SKRect.Empty

            If Not IsUsableRect(availablePx) Then
                availablePx = rootHost.GetSurfaceViewportPx()
            End If

            If Not IsUsableRect(availablePx) Then
                Return SKRect.Empty
            End If

            Dim dpi As Single = Math.Max(0.01F, rootHost.GetDpi())

            Dim metrics As MessageBoxMetrics =
                If(messageBox IsNot Nothing AndAlso messageBox.MetricsOverride IsNot Nothing,
                   messageBox.MetricsOverride,
                   MessageBoxMetricsDefaults.Create())

            Dim ctx As MASThemeContext = ResolveContext(rootHost)
            Dim sizeContext As MASSizeContext = CreateSizeContext(availablePx, dpi, ctx)

            Dim sizeLayoutBounds As SKRect =
                MASFloatSurfaceSizeContractResolver.TryResolveCenteredBoundsPixel(
                    surface:=surface,
                    context:=sizeContext,
                    availableBoundsPx:=availablePx,
                    dpiScale:=dpi,
                    intent:=MASSize.Default)

            If Not IsUsableRect(sizeLayoutBounds) Then
                sizeLayoutBounds =
                    MASFloatSurfaceSizeResolver.ResolveCenteredBoundsPixel(
                        kind:=MASFloatSurfaceKind.MessageBox,
                        availableBoundsPx:=availablePx,
                        dpiScale:=dpi,
                        intent:=MASSize.Default)
            End If

            Dim widthPx As Single = If(IsUsableRect(sizeLayoutBounds),
                                       sizeLayoutBounds.Width,
                                       Math.Max(1.0F, metrics.FixedPanelWidth * dpi))
            Dim heightPx As Single = If(IsUsableRect(sizeLayoutBounds),
                                        sizeLayoutBounds.Height,
                                        Math.Max(1.0F, metrics.FixedPanelHeight * dpi))

            If ctx IsNot Nothing Then
                Dim measured As MessageBoxMeasureResult =
                    MessageBoxMeasureEngine.Measure(
                        dpi:=dpi,
                        request:=messageBox,
                        ctx:=ctx,
                        metrics:=metrics)

                If measured IsNot Nothing Then
                    If measured.PanelWidth > 1.0F Then
                        widthPx = measured.PanelWidth
                    End If

                    If measured.PanelHeight > 1.0F Then
                        heightPx = measured.PanelHeight
                    End If
                End If
            End If

            Dim edgeMarginPx As Single = Math.Max(Nexamas.UI.Values.MessageBoxTokens.ViewportEdgeMarginMinDip * dpi, metrics.ViewportEdgeMargin * dpi)
            Dim maxWidthPx As Single = Math.Max(1.0F, availablePx.Width - (edgeMarginPx * 2.0F))
            Dim maxHeightPx As Single = Math.Max(1.0F, availablePx.Height - (edgeMarginPx * 2.0F))

            widthPx = Math.Min(Math.Max(1.0F, widthPx), maxWidthPx)
            heightPx = Math.Min(Math.Max(1.0F, heightPx), maxHeightPx)

            Dim left As Single =
                availablePx.Left + ((availablePx.Width - widthPx) * 0.5F)

            Dim top As Single =
                availablePx.Top + ((availablePx.Height - heightPx) * 0.5F)

            Return New SKRect(left, top, left + widthPx, top + heightPx)

        End Function

        Private Shared Function CreateSizeContext(availablePx As SKRect,
                                                        dpi As Single,
                                                        themeContext As MASThemeContext) As MASSizeContext
            Dim safeDpi As Single = Math.Max(0.01F, dpi)
            Dim availableLogical As New SKSize(
                MASLayoutDpiResolver.PixelToLogical(Math.Max(1.0F, availablePx.Width), safeDpi),
                MASLayoutDpiResolver.PixelToLogical(Math.Max(1.0F, availablePx.Height), safeDpi))

            If themeContext Is Nothing Then
                Return New MASSizeContext(availableLogical, MASRuntimeSizeProfileService.Current, safeDpi)
            End If

            Return New MASSizeContext(
                availableLogical,
                MASSizeLayoutIntegrationGateway.Create(themeContext, MASRuntimeSizeProfileService.Current, safeDpi))
        End Function

        Private Shared Function ResolveContext(rootHost As MASSkiaRootHost) As MASThemeContext
            If rootHost Is Nothing Then Return Nothing

            Try
                Return rootHost.ThemeHostCore.TryGetContext()
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException1, "MessageBox.Float.ResolveContext")
            End Try

            Return Nothing
        End Function

        Private Shared Function IsUsableRect(rect As SKRect) As Boolean
            Return Not rect.IsEmpty AndAlso rect.Width > 1.0F AndAlso rect.Height > 1.0F
        End Function

#End Region

    End Class

End Namespace