Option Strict On
Option Explicit On

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports Nexamas.UI.Icons
Imports Nexamas.UI.Rendering
Imports SkiaSharp

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Public Application-level options for the startup layered shell.
    '''
    ''' This object configures the real MASPanelShell chrome used by startup/lightweight
    ''' layered dialogs without exposing internal renderer/runtime details to applications.
    ''' </summary>
    <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
    Friend NotInheritable Class MASApplicationStartupShellOptions

        Friend Property Title As String = "MAS"
        Friend Property Subtitle As String = String.Empty

        Friend Property ShowIcon As Boolean = False
        Friend Property IconKind As MASIconKind = MASIconKind.Info
        Friend Property ShowCloseButton As Boolean = False

        Friend Property HeaderSize As MASPanelShellHeaderSize = MASPanelShellHeaderSize.Compact
        Friend Property Density As MASPanelShellDensity = MASPanelShellDensity.Normal

        Friend Property BorderStrength As MASPanelShellStrength = MASPanelShellStrength.Level5
        Friend Property SurfaceStrength As MASPanelShellStrength = MASPanelShellStrength.Level5

        ''' <summary>
        ''' Complete reusable surface visual style used by the startup shell.
        ''' The default matches the platform dialog frame used by PanelShell-hosted surfaces.
        ''' </summary>
        Friend Property SurfaceVisualStyleKey As String = MASSurfaceVisualStyleIds.PlatformDialogFrame

        ''' <summary>
        ''' Optional material override. Leave Auto to use the material declared by SurfaceVisualStyleKey.
        ''' </summary>
        Friend Property SurfaceMaterialKey As String = MASSurfaceMaterialIds.Auto

        Friend Property PaddingLeftDip As Single = 0.0F
        Friend Property PaddingTopDip As Single = 0.0F
        Friend Property PaddingRightDip As Single = 0.0F
        Friend Property PaddingBottomDip As Single = 0.0F

        Friend Property ShellMarginLogical As Single = 18.0F

        Friend Property DrawSeparator As Boolean = True
        Friend Property DrawGloss As Boolean = True
        Friend Property DrawCrown As Boolean = True
        Friend Property DrawSeam As Boolean = True
        Friend Property DrawInnerRim As Boolean = True

        Friend Property AllowHeaderDrag As Boolean = True
        Friend Property ClearCanvasBeforeDraw As Boolean = True

        Friend Shared Function StartupDialog(Optional title As String = "MAS") As MASApplicationStartupShellOptions
            Return New MASApplicationStartupShellOptions() With {
                .Title = NormalizeTitle(title),
                .Subtitle = String.Empty,
                .ShowIcon = False,
                .ShowCloseButton = False,
                .HeaderSize = MASPanelShellHeaderSize.Compact,
                .Density = MASPanelShellDensity.Normal,
                .BorderStrength = MASPanelShellStrength.Level5,
                .SurfaceStrength = MASPanelShellStrength.Level5,
                .SurfaceVisualStyleKey = MASSurfaceVisualStyleIds.PlatformDialogFrame,
                .SurfaceMaterialKey = MASSurfaceMaterialIds.Auto,
                .PaddingLeftDip = 0.0F,
                .PaddingTopDip = 0.0F,
                .PaddingRightDip = 0.0F,
                .PaddingBottomDip = 0.0F,
                .ShellMarginLogical = 18.0F,
                .DrawSeparator = True,
                .DrawGloss = True,
                .DrawCrown = True,
                .DrawSeam = True,
                .DrawInnerRim = True,
                .AllowHeaderDrag = True,
                .ClearCanvasBeforeDraw = True
            }
        End Function

        Friend Function Clone() As MASApplicationStartupShellOptions
            Return New MASApplicationStartupShellOptions() With {
                .Title = NormalizeTitle(Me.Title),
                .Subtitle = NormalizeText(Me.Subtitle),
                .ShowIcon = Me.ShowIcon,
                .IconKind = Me.IconKind,
                .ShowCloseButton = Me.ShowCloseButton,
                .HeaderSize = MASPanelShellValueNormalizer.NormalizeHeaderSize(Me.HeaderSize),
                .Density = MASPanelShellValueNormalizer.NormalizeDensity(Me.Density),
                .BorderStrength = MASPanelShellValueNormalizer.NormalizeStrength(Me.BorderStrength),
                .SurfaceStrength = MASPanelShellValueNormalizer.NormalizeStrength(Me.SurfaceStrength),
                .SurfaceVisualStyleKey = MASPanelShellValueNormalizer.NormalizeKey(Me.SurfaceVisualStyleKey, MASSurfaceVisualStyleIds.PlatformDialogFrame),
                .SurfaceMaterialKey = MASPanelShellValueNormalizer.NormalizeKey(Me.SurfaceMaterialKey, MASSurfaceMaterialIds.Auto),
                .PaddingLeftDip = MASPanelShellValueNormalizer.NormalizeNonNegativeDip(Me.PaddingLeftDip, 0.0F),
                .PaddingTopDip = MASPanelShellValueNormalizer.NormalizeNonNegativeDip(Me.PaddingTopDip, 0.0F),
                .PaddingRightDip = MASPanelShellValueNormalizer.NormalizeNonNegativeDip(Me.PaddingRightDip, 0.0F),
                .PaddingBottomDip = MASPanelShellValueNormalizer.NormalizeNonNegativeDip(Me.PaddingBottomDip, 0.0F),
                .ShellMarginLogical = MASPanelShellValueNormalizer.NormalizeNonNegativeDip(Me.ShellMarginLogical, 18.0F),
                .DrawSeparator = Me.DrawSeparator,
                .DrawGloss = Me.DrawGloss,
                .DrawCrown = Me.DrawCrown,
                .DrawSeam = Me.DrawSeam,
                .DrawInnerRim = Me.DrawInnerRim,
                .AllowHeaderDrag = Me.AllowHeaderDrag,
                .ClearCanvasBeforeDraw = Me.ClearCanvasBeforeDraw
            }
        End Function

        Private Shared Function NormalizeTitle(value As String) As String
            Dim title As String = NormalizeText(value)
            If title.Length = 0 Then Return "MAS"
            Return title
        End Function

        Private Shared Function NormalizeText(value As String) As String
            Return If(value, String.Empty).Trim()
        End Function

    End Class

    ''' <summary>
    ''' Public Application-level startup shell controller.
    '''
    ''' It owns the official startup shell chrome path:
    ''' MASApplicationLayeredSceneContext -> MASApplicationPanelShellChrome -> MASPanelShell.
    '''
    ''' It also owns header drag behavior using the public PanelShell chrome result,
    ''' so consumer forms do not duplicate PanelShell hit-test logic.
    ''' </summary>
    <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
    Friend NotInheritable Class MASApplicationStartupShellChrome

        Private _options As MASApplicationStartupShellOptions
        Private _lastResult As MASApplicationPanelShellChromeResult =
            MASApplicationPanelShellChromeResult.Empty

        Private _isDragging As Boolean
        Private _dragOffsetScreen As System.Drawing.Point

        Friend Sub New(Optional options As MASApplicationStartupShellOptions = Nothing)
            _options = If(options, MASApplicationStartupShellOptions.StartupDialog()).Clone()
        End Sub

        Friend Shared Function StartupDialog(Optional title As String = "MAS") As MASApplicationStartupShellChrome
            Return New MASApplicationStartupShellChrome(
                MASApplicationStartupShellOptions.StartupDialog(title))
        End Function

        Friend Property Options As MASApplicationStartupShellOptions
            Get
                Return _options.Clone()
            End Get
            Set(value As MASApplicationStartupShellOptions)
                _options = If(value, MASApplicationStartupShellOptions.StartupDialog()).Clone()
                CancelDrag(Nothing)
            End Set
        End Property

        Friend ReadOnly Property LastResult As MASApplicationPanelShellChromeResult
            Get
                Return If(_lastResult, MASApplicationPanelShellChromeResult.Empty)
            End Get
        End Property

        Friend ReadOnly Property IsDragging As Boolean
            Get
                Return _isDragging
            End Get
        End Property

        Friend Function Draw(canvas As SKCanvas,
                             width As Integer,
                             height As Integer,
                             context As MASApplicationLayeredSceneContext) As MASApplicationPanelShellChromeResult

            If canvas Is Nothing Then Return MASApplicationPanelShellChromeResult.Empty
            If context Is Nothing Then Return MASApplicationPanelShellChromeResult.Empty
            If width <= 0 OrElse height <= 0 Then Return MASApplicationPanelShellChromeResult.Empty

            Dim opts As MASApplicationStartupShellOptions =
                If(_options, MASApplicationStartupShellOptions.StartupDialog())

            If opts.ClearCanvasBeforeDraw Then
                canvas.Clear(SKColors.Transparent)
            End If

            Dim dpi As Single =
                NormalizeDpi(context.Dpi)

            Dim shellBoundsPx As SKRect =
                ResolveShellBoundsPx(
                    width:=width,
                    height:=height,
                    dpi:=dpi,
                    opts:=opts)

            If Not HasArea(shellBoundsPx) Then
                _lastResult = MASApplicationPanelShellChromeResult.Empty
                Return _lastResult
            End If

            Dim spec As MASPanelShellSpec =
                CreatePanelShellSpec(
                    boundsPx:=shellBoundsPx,
                    opts:=opts)

            _lastResult =
                MASApplicationPanelShellChrome.Draw(
                    canvas:=canvas,
                    context:=context,
                    spec:=spec)

            Return _lastResult

        End Function

        Friend Function MouseDown(owner As Form,
                                  e As MouseEventArgs) As Boolean

            If owner Is Nothing Then Return False
            If e Is Nothing Then Return False
            If e.Button <> MouseButtons.Left Then Return False
            If _options Is Nothing OrElse Not _options.AllowHeaderDrag Then Return False
            If _lastResult Is Nothing OrElse Not _lastResult.HasHeader Then Return False

            Dim pointPx As New SKPoint(CSng(e.X), CSng(e.Y))

            If Not _lastResult.IsInsideHeader(pointPx, includeCloseButton:=False) Then
                Return False
            End If

            Dim screen As System.Drawing.Point =
                owner.PointToScreen(New System.Drawing.Point(e.X, e.Y))

            _dragOffsetScreen =
                New System.Drawing.Point(
                    screen.X - owner.Left,
                    screen.Y - owner.Top)

            _isDragging = True
            owner.Capture = True

            Return True

        End Function

        Friend Function MouseMove(owner As Form,
                                  e As MouseEventArgs) As Boolean

            If owner Is Nothing Then Return False
            If e Is Nothing Then Return False
            If Not _isDragging Then Return False

            Dim screen As System.Drawing.Point =
                owner.PointToScreen(New System.Drawing.Point(e.X, e.Y))

            owner.Location =
                New System.Drawing.Point(
                    screen.X - _dragOffsetScreen.X,
                    screen.Y - _dragOffsetScreen.Y)

            Return True

        End Function

        Friend Function MouseUp(owner As Form,
                                e As MouseEventArgs) As Boolean

            If Not _isDragging Then Return False

            _isDragging = False

            If owner IsNot Nothing Then
                owner.Capture = False
            End If

            Return True

        End Function

        Friend Function MouseLeave(owner As Form,
                                   e As EventArgs) As Boolean

            ' While dragging, the form may receive mouse leave events.
            ' The drag remains active until MouseUp, matching normal borderless-window behavior.
            Return _isDragging

        End Function

        Friend Sub CancelDrag(owner As Form)

            _isDragging = False

            If owner IsNot Nothing Then
                Try
                    owner.Capture = False
                Catch masCaughtException1 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(masCaughtException1, "MASApplicationStartupShellChrome.CancelDrag")
                End Try
            End If

        End Sub

        Friend Function GetContentRectLogical(dpi As Single) As SKRect

            Dim safeDpi As Single =
                NormalizeDpi(dpi)

            If _lastResult Is Nothing OrElse Not _lastResult.HasContent Then
                Return SKRect.Empty
            End If

            Return ToLogicalRect(_lastResult.ContentRectPx, safeDpi)

        End Function

        ''' <summary>
        ''' Measures the startup shell content rectangle without drawing and without relying on the previous
        ''' frame result. Startup hosts use this before applying composition layout so content controls are
        ''' arranged inside the real PanelShell content slot rather than duplicating chrome/header padding.
        ''' </summary>
        Friend Function MeasureContentRectLogical(width As Integer,
                                                  height As Integer,
                                                  dpi As Single) As SKRect

            If width <= 0 OrElse height <= 0 Then Return SKRect.Empty

            Dim safeDpi As Single = NormalizeDpi(dpi)
            Dim opts As MASApplicationStartupShellOptions =
                If(_options, MASApplicationStartupShellOptions.StartupDialog())

            Dim shellBoundsPx As SKRect =
                ResolveShellBoundsPx(
                    width:=width,
                    height:=height,
                    dpi:=safeDpi,
                    opts:=opts)

            If Not HasArea(shellBoundsPx) Then Return SKRect.Empty

            Dim spec As MASPanelShellSpec =
                CreatePanelShellSpec(
                    boundsPx:=shellBoundsPx,
                    opts:=opts)

            Dim measured As MASApplicationPanelShellChromeResult =
                MASApplicationPanelShellChrome.Measure(
                    dpi:=safeDpi,
                    spec:=spec)

            If measured Is Nothing OrElse Not measured.HasContent Then Return SKRect.Empty
            Return ToLogicalRect(measured.ContentRectPx, safeDpi)

        End Function

        Friend Function GetBaseRectLogical(dpi As Single) As SKRect

            Dim safeDpi As Single =
                NormalizeDpi(dpi)

            If _lastResult Is Nothing Then Return SKRect.Empty

            Return ToLogicalRect(_lastResult.BaseRectPx, safeDpi)

        End Function

        Private Shared Function CreatePanelShellSpec(boundsPx As SKRect,
                                                     opts As MASApplicationStartupShellOptions) As MASPanelShellSpec

            Dim safeTitle As String =
                If(opts.Title, String.Empty).Trim()

            If safeTitle.Length = 0 Then
                safeTitle = "MAS"
            End If

            Dim safeSubtitle As String =
                If(opts.Subtitle, String.Empty).Trim()

            Dim visualStyleKey As String =
                If(opts.SurfaceVisualStyleKey, String.Empty).Trim()

            If visualStyleKey.Length = 0 Then
                visualStyleKey = MASSurfaceVisualStyleIds.PlatformDialogFrame
            End If

            Dim materialKey As String =
                If(opts.SurfaceMaterialKey, String.Empty).Trim()

            If materialKey.Length = 0 Then
                materialKey = MASSurfaceMaterialIds.Auto
            End If

            Dim header As MASPanelShellHeaderSpec =
                MASPanelShellHeaderSpec.Create(
                    title:=safeTitle,
                    subtitle:=safeSubtitle,
                    iconKind:=opts.IconKind,
                    showIcon:=opts.ShowIcon,
                    showCloseButton:=opts.ShowCloseButton,
                    size:=opts.HeaderSize,
                    density:=opts.Density).
                Copy(
                    drawSeparator:=opts.DrawSeparator,
                    drawGloss:=opts.DrawGloss,
                    drawCrown:=opts.DrawCrown,
                    drawSeam:=opts.DrawSeam,
                    drawInnerRim:=opts.DrawInnerRim)

            Return MASPanelShellSpec.Create(boundsPx).
                WithDensity(opts.Density).
                WithHeader(header).
                WithPadding(
                    leftDip:=opts.PaddingLeftDip,
                    topDip:=opts.PaddingTopDip,
                    rightDip:=opts.PaddingRightDip,
                    bottomDip:=opts.PaddingBottomDip).
                WithStrengths(
                    border:=opts.BorderStrength,
                    surface:=opts.SurfaceStrength).
                WithSurfaceVisualStyleKey(visualStyleKey).
                WithSurfaceMaterialKey(materialKey).
                Build()

        End Function

        Private Shared Function ResolveShellBoundsPx(width As Integer,
                                                     height As Integer,
                                                     dpi As Single,
                                                     opts As MASApplicationStartupShellOptions) As SKRect

            Dim safeDpi As Single =
                NormalizeDpi(dpi)

            Dim margin As Single =
                Math.Max(0.0F, opts.ShellMarginLogical * safeDpi)

            Return New SKRect(
                margin,
                margin,
                Math.Max(margin, CSng(width) - margin),
                Math.Max(margin, CSng(height) - margin))

        End Function

        Private Shared Function NormalizeDpi(value As Single) As Single

            If value <= 0.0F OrElse
               Single.IsNaN(value) OrElse
               Single.IsInfinity(value) Then

                Return 1.0F
            End If

            Return Math.Max(0.75F, value)

        End Function

        Private Shared Function HasArea(rect As SKRect) As Boolean

            Return rect.Width > 1.0F AndAlso
                   rect.Height > 1.0F

        End Function

        Private Shared Function ToLogicalRect(rectPx As SKRect,
                                              dpi As Single) As SKRect

            Dim safeDpi As Single =
                NormalizeDpi(dpi)

            Return New SKRect(
                rectPx.Left / safeDpi,
                rectPx.Top / safeDpi,
                rectPx.Right / safeDpi,
                rectPx.Bottom / safeDpi)

        End Function

    End Class

End Namespace
