Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Host
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Theming
Imports SkiaSharp

Namespace Nexamas.UI.Application


    ''' <summary>
    ''' Public beginner-facing page layout builder for direct MAS controls hosted by MASApplicationWindow.Controls.
    ''' It exposes intent-only page/section/grid/flow language and hides MASLayoutNode, measure passes,
    ''' arrange passes, slots, DPI conversion, and batch Apply details from consumers.
    ''' </summary>
    Public NotInheritable Class MASApplicationLayoutPageBuilder
        Private ReadOnly _entries As New List(Of Object)()
        Private _spacing As Single = MASApplicationLayoutSizeTokens.PageSpacing
        Private _padding As MASLayoutInset = MASApplicationLayoutSizeTokens.PagePadding()
        Private _maxContentWidth As Single = MASApplicationLayoutSizeTokens.PageMaxContentWidth
        Private _resolvedApplicationPageProfile As MASApplicationPageLayoutProfile
        Private _verticalScrollEnabled As Boolean
        Private _verticalScrollKey As String = String.Empty

        ''' <summary>
        ''' Applies an official MASApplication page preset below the standard MAS TopBar.
        ''' This is the preferred path for application pages that should not carry local
        ''' top-bar heights, page padding numbers, or content-width formulas.
        ''' </summary>
        Public Function ApplicationPage(kind As MASApplicationPageLayoutKind,
                                        shell As MASApplicationWindowShell) As MASApplicationLayoutPageBuilder
            _resolvedApplicationPageProfile = MASApplicationLayoutPolicy.ResolvePage(kind, shell)
            Return Me
        End Function

        Private Sub ClearResolvedApplicationPageProfile()
            _resolvedApplicationPageProfile = Nothing
        End Sub

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Function Spacing(value As Single) As MASApplicationLayoutPageBuilder
            ClearResolvedApplicationPageProfile()
            _spacing = SafeNonNegative(value)
            Return Me
        End Function

        ''' <summary>
        ''' Uses the official MASLayout semantic spacing vocabulary. This is the preferred SDK
        ''' path for normal consumers; it remains density-aware when the layout is built.
        ''' </summary>
        Public Function Spacing(value As MASLayoutSpacing) As MASApplicationLayoutPageBuilder
            ClearResolvedApplicationPageProfile()
            _spacing = MASLayoutSpacingResolver.ResolveBase(value)
            Return Me
        End Function

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Function Padding(all As Single) As MASApplicationLayoutPageBuilder
            ClearResolvedApplicationPageProfile()
            _padding = New MASLayoutInset(all)
            Return Me
        End Function

        ''' <summary>
        ''' Uses semantic padding from the MASLayout spacing vocabulary instead of raw logical numbers.
        ''' </summary>
        Public Function Padding(value As MASLayoutSpacing) As MASApplicationLayoutPageBuilder
            ClearResolvedApplicationPageProfile()
            _padding = MASLayoutSpacingResolver.ResolveInsetBase(value)
            Return Me
        End Function

        ''' <summary>
        ''' Internal Nexamas UI-owned page padding hook. It lets owned Application pages consume
        ''' centralized MASLayout policy tokens without exposing layout insets as beginner API.
        ''' </summary>
        Friend Function Padding(value As MASLayoutInset) As MASApplicationLayoutPageBuilder
            ClearResolvedApplicationPageProfile()
            _padding = value
            Return Me
        End Function

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Function Padding(left As Single,
                                top As Single,
                                right As Single,
                                bottom As Single) As MASApplicationLayoutPageBuilder
            ClearResolvedApplicationPageProfile()
            _padding = New MASLayoutInset(left, top, right, bottom)
            Return Me
        End Function

        <Global.System.ComponentModel.EditorBrowsable(Global.System.ComponentModel.EditorBrowsableState.Advanced)>
        Friend Function MaxContentWidth(value As Single) As MASApplicationLayoutPageBuilder
            ClearResolvedApplicationPageProfile()
            _maxContentWidth = SafeNonNegative(value)
            Return Me
        End Function

        ''' <summary>
        ''' ApplicationSystem-owned hook used by MASApplicationWindow.Pages to make the hosted workspace
        ''' vertically scrollable through the official MAS ScrollSystem. It is Friend so external demo code
        ''' cannot create a parallel scroll route or attach scrollbars locally.
        ''' </summary>
        Friend Function EnableVerticalScrollInternal(scrollKey As String) As MASApplicationLayoutPageBuilder
            _verticalScrollEnabled = True
            _verticalScrollKey = If(scrollKey, String.Empty).Trim()
            If _verticalScrollKey.Length = 0 Then _verticalScrollKey = "MASApplicationWindow.Pages.Workspace"
            Return Me
        End Function

        Friend ReadOnly Property IsVerticalScrollEnabledInternal As Boolean
            Get
                Return _verticalScrollEnabled
            End Get
        End Property

        Friend ReadOnly Property VerticalScrollKeyInternal As String
            Get
                If _verticalScrollKey.Length = 0 Then Return "MASApplicationWindow.Pages.Workspace"
                Return _verticalScrollKey
            End Get
        End Property

        Public Function Section(ParamArray controls As MASControlBase()) As MASApplicationLayoutPageBuilder
            Dim sectionEntries As New List(Of MASApplicationLayoutControlEntry)()
            If controls IsNot Nothing Then
                For Each control As MASControlBase In controls
                    If control IsNot Nothing Then sectionEntries.Add(New MASApplicationLayoutControlEntry(control, Nothing, MASLayoutCrossAlignment.Stretch))
                Next
            End If

            If sectionEntries.Count > 0 Then
                _entries.Add(New MASApplicationLayoutSectionEntry(sectionEntries, MASApplicationLayoutSizeTokens.PageSectionSpacing, MASLayoutInset.Empty))
            End If

            Return Me
        End Function

        Public Function FullWidth(control As MASControlBase,
                                  Optional sizeIntent As MASSize = Nothing) As MASApplicationLayoutPageBuilder
            If control IsNot Nothing Then
                _entries.Add(New MASApplicationLayoutControlEntry(control, If(sizeIntent, MASSize.FillWidth), MASLayoutCrossAlignment.Stretch))
            End If

            Return Me
        End Function

        ''' <summary>
        ''' Nexamas UI-owned full-width entry that lets the hosted control keep owning
        ''' its runtime MASSize intent. Use this for retained pages whose collection
        ''' surfaces need to shrink/grow from their current read-model facts without
        ''' rebuilding a page-local parallel sizing path.
        ''' </summary>
        Friend Function FullWidthUsingControlSize(control As MASControlBase) As MASApplicationLayoutPageBuilder
            If control IsNot Nothing Then
                _entries.Add(New MASApplicationLayoutControlEntry(control, Nothing, MASLayoutCrossAlignment.Stretch))
            End If

            Return Me
        End Function

        Public Function Add(control As MASControlBase,
                            Optional sizeIntent As MASSize = Nothing) As MASApplicationLayoutPageBuilder
            If control IsNot Nothing Then
                _entries.Add(New MASApplicationLayoutControlEntry(control, sizeIntent, MASLayoutCrossAlignment.Start))
            End If

            Return Me
        End Function

        Public Function Grid(columns As Integer) As MASApplicationLayoutGridBuilder
            Dim builder As New MASApplicationLayoutGridBuilder(columns)
            _entries.Add(builder)
            Return builder
        End Function

        Public Function Flow() As MASApplicationLayoutFlowBuilder
            Dim builder As New MASApplicationLayoutFlowBuilder()
            _entries.Add(builder)
            Return builder
        End Function

        Public Function StackVertical() As MASApplicationLayoutStackBuilder
            Dim builder As New MASApplicationLayoutStackBuilder(MASLayoutOrientation.Vertical)
            _entries.Add(builder)
            Return builder
        End Function

        Public Function StackHorizontal() As MASApplicationLayoutStackBuilder
            Dim builder As New MASApplicationLayoutStackBuilder(MASLayoutOrientation.Horizontal)
            _entries.Add(builder)
            Return builder
        End Function

        ''' <summary>
        ''' Creates an official semantic action group. Use it for OK/Cancel/Apply rows, command-bar
        ''' button groups, and any action row that needs equal item sizing or start/center/end placement
        ''' without manual button widths or coordinates.
        ''' </summary>
        Public Function ActionGroup() As MASApplicationActionGroupLayoutBuilder
            Dim builder As New MASApplicationActionGroupLayoutBuilder()
            _entries.Add(builder)
            Return builder
        End Function

        ''' <summary>
        ''' Builds an official semantic action group inline and returns the page builder for fluent page recipes.
        ''' </summary>
        Public Function ActionGroup(buildAction As Action(Of MASApplicationActionGroupLayoutBuilder)) As MASApplicationLayoutPageBuilder
            If buildAction Is Nothing Then Throw New ArgumentNullException(NameOf(buildAction))
            Dim builder As MASApplicationActionGroupLayoutBuilder = ActionGroup()
            buildAction.Invoke(builder)
            Return Me
        End Function

        ''' <summary>
        ''' Creates an official semantic filter bar for search fields, combo boxes, toggles,
        ''' checkboxes, and compact filter controls without manual positions or field widths.
        ''' </summary>
        Public Function FilterBar() As MASApplicationFilterBarLayoutBuilder
            Dim builder As New MASApplicationFilterBarLayoutBuilder()
            _entries.Add(builder)
            Return builder
        End Function

        ''' <summary>
        ''' Builds an official semantic filter bar inline and returns the page builder for fluent recipes.
        ''' </summary>
        Public Function FilterBar(buildAction As Action(Of MASApplicationFilterBarLayoutBuilder)) As MASApplicationLayoutPageBuilder
            If buildAction Is Nothing Then Throw New ArgumentNullException(NameOf(buildAction))
            Dim builder As MASApplicationFilterBarLayoutBuilder = FilterBar()
            buildAction.Invoke(builder)
            Return Me
        End Function

        ''' <summary>
        ''' Creates an official semantic form row for label/input pairs and compact settings rows.
        ''' </summary>
        Public Function FormRow() As MASApplicationFormRowLayoutBuilder
            Dim builder As New MASApplicationFormRowLayoutBuilder()
            _entries.Add(builder)
            Return builder
        End Function

        ''' <summary>
        ''' Builds an official semantic form row inline and returns the page builder for fluent recipes.
        ''' </summary>
        Public Function FormRow(buildAction As Action(Of MASApplicationFormRowLayoutBuilder)) As MASApplicationLayoutPageBuilder
            If buildAction Is Nothing Then Throw New ArgumentNullException(NameOf(buildAction))
            Dim builder As MASApplicationFormRowLayoutBuilder = FormRow()
            buildAction.Invoke(builder)
            Return Me
        End Function

        ''' <summary>
        ''' Creates an official semantic gallery for icon, tile, preview, and card collections.
        ''' Gallery uses MASLayout TileGrid internally, so consumers avoid x/y math, manual tile sizes,
        ''' and ad-hoc wrap calculations.
        ''' </summary>
        Public Function Gallery() As MASApplicationGalleryLayoutBuilder
            Dim builder As New MASApplicationGalleryLayoutBuilder()
            _entries.Add(builder)
            Return builder
        End Function

        ''' <summary>
        ''' Builds an official semantic gallery inline and returns the page builder for fluent recipes.
        ''' </summary>
        Public Function Gallery(buildAction As Action(Of MASApplicationGalleryLayoutBuilder)) As MASApplicationLayoutPageBuilder
            If buildAction Is Nothing Then Throw New ArgumentNullException(NameOf(buildAction))
            Dim builder As MASApplicationGalleryLayoutBuilder = Gallery()
            buildAction.Invoke(builder)
            Return Me
        End Function

        ''' <summary>
        ''' Creates an official narrative/surface split pane. Use it when a page needs
        ''' explanatory text in a left pane and a real MAS control surface in the right
        ''' pane, without local widths, manual coordinates, or demo-owned layout helpers.
        ''' </summary>
        Public Function SplitPane() As MASApplicationSplitPaneLayoutBuilder
            Dim builder As New MASApplicationSplitPaneLayoutBuilder()
            _entries.Add(builder)
            Return builder
        End Function

        ''' <summary>
        ''' Builds an official narrative/surface split pane inline and returns the page
        ''' builder for fluent page recipes.
        ''' </summary>
        Public Function SplitPane(buildAction As Action(Of MASApplicationSplitPaneLayoutBuilder)) As MASApplicationLayoutPageBuilder
            If buildAction Is Nothing Then Throw New ArgumentNullException(NameOf(buildAction))
            Dim builder As MASApplicationSplitPaneLayoutBuilder = SplitPane()
            buildAction.Invoke(builder)
            Return Me
        End Function


        ''' <summary>
        ''' Nexamas UI-owned composition hook used by MASApplicationWindow.Pages to include a cached
        ''' page recipe inside the current workspace without top-level-window navigation or duplicating controls
        ''' on every resize. It remains Friend so public consumers keep using the normal page builder verbs.
        ''' </summary>
        Friend Function IncludePage(page As MASApplicationLayoutPageBuilder) As MASApplicationLayoutPageBuilder
            If page IsNot Nothing AndAlso Not Object.ReferenceEquals(page, Me) Then
                _entries.Add(page)
            End If

            Return Me
        End Function

        Friend Function BuildNode(Optional sizeProfile As MASSizeProfile = Nothing) As MASLayoutNode
            Dim profile As MASSizeProfile = MASSizeProfile.Normalize(sizeProfile)
            Dim nodes As New List(Of MASLayoutNode)()

            For Each entry As Object In _entries
                Dim node As MASLayoutNode = BuildEntryNode(entry, profile)
                If node IsNot Nothing Then nodes.Add(node)
            Next

            If _resolvedApplicationPageProfile IsNot Nothing Then
                Return MASLayoutBuilder.Page(nodes,
                                             _resolvedApplicationPageProfile.Spacing,
                                             _resolvedApplicationPageProfile.Padding,
                                             If(_resolvedApplicationPageProfile.HasMaxContentWidth, _resolvedApplicationPageProfile.MaxContentWidth, 0.0F))
            End If

            Return MASLayoutBuilder.Page(nodes,
                                         MASLayoutSpacingResolver.ScaleLength(_spacing, profile),
                                         MASLayoutSpacingResolver.ScaleInset(_padding, profile),
                                         MASLayoutSpacingResolver.ScaleWidth(_maxContentWidth, profile))
        End Function

        Friend Sub CollectControls(target As IList(Of MASControlBase))
            If target Is Nothing Then Return

            For Each entry As Object In _entries
                CollectEntryControls(entry, target)
            Next
        End Sub

        Private Shared Function BuildEntryNode(entry As Object, profile As MASSizeProfile) As MASLayoutNode
            If TypeOf entry Is MASApplicationLayoutControlEntry Then
                Return DirectCast(entry, MASApplicationLayoutControlEntry).BuildNode()
            End If

            If TypeOf entry Is MASApplicationLayoutSectionEntry Then
                Return DirectCast(entry, MASApplicationLayoutSectionEntry).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationLayoutGridBuilder Then
                Return DirectCast(entry, MASApplicationLayoutGridBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationLayoutFlowBuilder Then
                Return DirectCast(entry, MASApplicationLayoutFlowBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationLayoutStackBuilder Then
                Return DirectCast(entry, MASApplicationLayoutStackBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationSplitPaneLayoutBuilder Then
                Return DirectCast(entry, MASApplicationSplitPaneLayoutBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationActionGroupLayoutBuilder Then
                Return DirectCast(entry, MASApplicationActionGroupLayoutBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationFilterBarLayoutBuilder Then
                Return DirectCast(entry, MASApplicationFilterBarLayoutBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationFormRowLayoutBuilder Then
                Return DirectCast(entry, MASApplicationFormRowLayoutBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationGalleryLayoutBuilder Then
                Return DirectCast(entry, MASApplicationGalleryLayoutBuilder).BuildNode(profile)
            End If

            If TypeOf entry Is MASApplicationLayoutPageBuilder Then
                Return DirectCast(entry, MASApplicationLayoutPageBuilder).BuildNode(profile)
            End If

            Return Nothing
        End Function

        Private Shared Sub CollectEntryControls(entry As Object, target As IList(Of MASControlBase))
            If TypeOf entry Is MASApplicationLayoutControlEntry Then
                DirectCast(entry, MASApplicationLayoutControlEntry).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationLayoutSectionEntry Then
                DirectCast(entry, MASApplicationLayoutSectionEntry).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationLayoutGridBuilder Then
                DirectCast(entry, MASApplicationLayoutGridBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationLayoutFlowBuilder Then
                DirectCast(entry, MASApplicationLayoutFlowBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationLayoutStackBuilder Then
                DirectCast(entry, MASApplicationLayoutStackBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationSplitPaneLayoutBuilder Then
                DirectCast(entry, MASApplicationSplitPaneLayoutBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationActionGroupLayoutBuilder Then
                DirectCast(entry, MASApplicationActionGroupLayoutBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationFilterBarLayoutBuilder Then
                DirectCast(entry, MASApplicationFilterBarLayoutBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationFormRowLayoutBuilder Then
                DirectCast(entry, MASApplicationFormRowLayoutBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationGalleryLayoutBuilder Then
                DirectCast(entry, MASApplicationGalleryLayoutBuilder).CollectControls(target)
            ElseIf TypeOf entry Is MASApplicationLayoutPageBuilder Then
                DirectCast(entry, MASApplicationLayoutPageBuilder).CollectControls(target)
            End If
        End Sub

        Friend Shared Function SafeNonNegative(value As Single) As Single
            If Single.IsNaN(value) OrElse Single.IsInfinity(value) OrElse value < 0.0F Then Return 0.0F
            Return value
        End Function
    End Class

End Namespace
