Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Reflection
Imports System.Windows.Forms
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Friend-owned policy for MASTreeGrid. It centralizes text and key hygiene,
    ''' row projection limits, visible-row windowing, RTL detection, theme colors,
    ''' and architectural boundary facts so the public control remains a visual
    ''' hierarchy-with-columns surface rather than a data/storage/binding service.
    ''' </summary>
    Friend NotInheritable Class MASTreeGridPolicy
        Private Sub New()
        End Sub

        Friend Shared Function NormalizeTitle(value As String) As String
            Dim normalized As String = NormalizeText(value, TreeGridTokens.DefaultTitle, 96)
            If normalized.Length = 0 Then Return TreeGridTokens.DefaultTitle
            Return normalized
        End Function

        Friend Shared Function NormalizeColumnHeader(value As String) As String
            Return NormalizeText(value, TreeGridTokens.DefaultColumnHeader, 72)
        End Function

        Friend Shared Function NormalizeRowLabel(value As String) As String
            Return NormalizeText(value, TreeGridTokens.DefaultRowLabel, 96)
        End Function

        Friend Shared Function NormalizeCellText(value As String) As String
            Return NormalizeText(value, String.Empty, 96)
        End Function

        Friend Shared Function NormalizeFilterText(value As String) As String
            Return NormalizeText(value, String.Empty, 96)
        End Function

        Friend Shared Function NormalizeKey(value As String,
                                            fallbackPrefix As String,
                                            fallbackIndex As Integer) As String
            Dim normalized As String = If(value, String.Empty).Trim()
            If normalized.Length = 0 Then normalized = fallbackPrefix & Math.Max(1, fallbackIndex).ToString(CultureInfo.InvariantCulture)
            normalized = normalized.Replace(ControlChars.Cr, " "c).Replace(ControlChars.Lf, " "c).Replace(ControlChars.Tab, " "c)
            If normalized.Length > 64 Then normalized = normalized.Substring(0, 64)
            Return normalized
        End Function

        Friend Shared Function NormalizeText(value As String,
                                             fallback As String,
                                             maxLength As Integer) As String
            Dim normalized As String = If(value, String.Empty).Trim()
            If normalized.Length = 0 Then normalized = If(fallback, String.Empty)
            normalized = normalized.Replace(ControlChars.Cr, " "c).Replace(ControlChars.Lf, " "c).Replace(ControlChars.Tab, " "c)
            While normalized.Contains("  ")
                normalized = normalized.Replace("  ", " ")
            End While
            If maxLength > 0 AndAlso normalized.Length > maxLength Then normalized = normalized.Substring(0, maxLength)
            Return normalized
        End Function

        Friend Shared Function NormalizeWidthWeight(value As Single) As Single
            If Single.IsNaN(value) OrElse Single.IsInfinity(value) Then Return 1.0F
            If value < 0.25F Then Return 0.25F
            If value > 4.0F Then Return 4.0F
            Return value
        End Function

        Friend Shared Function ClampIndex(index As Integer,
                                          count As Integer) As Integer
            If count <= 0 Then Return -1
            If index < 0 Then Return 0
            If index >= count Then Return count - 1
            Return index
        End Function

        Friend Shared Function EnsureVisibleRowIndex(selectedIndex As Integer,
                                                     currentTopIndex As Integer,
                                                     visibleRowCount As Integer) As Integer
            If visibleRowCount <= 0 Then Return 0
            Dim maxTop As Integer = Math.Max(0, visibleRowCount - TreeGridTokens.MaxVisibleRows)
            Dim top As Integer = Math.Max(0, Math.Min(currentTopIndex, maxTop))
            If selectedIndex < 0 Then Return top
            If selectedIndex < top Then Return Math.Max(0, selectedIndex)
            If selectedIndex >= top + TreeGridTokens.MaxVisibleRows Then
                Return Math.Max(0, Math.Min(maxTop, selectedIndex - TreeGridTokens.MaxVisibleRows + 1))
            End If
            Return top
        End Function

        Friend Shared Function ResolveRightToLeft() As Boolean
            Try
                Return Nexamas.UI.Localization.MASLocalization.IsCurrentRightToLeft()
            Catch ex As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowInputBoundary(ex, "MASTreeGridPolicy.ResolveRightToLeft")
                Return False
            End Try
        End Function

        Friend Shared Function BuildSummary(rowCount As Integer,
                                            columnCount As Integer,
                                            visibleRowCount As Integer) As String
            Dim rowsText As String = If(rowCount = 1, "1 row", rowCount.ToString(CultureInfo.CurrentCulture) & " rows")
            Dim columnsText As String = If(columnCount = 1, "1 column", columnCount.ToString(CultureInfo.CurrentCulture) & " columns")
            Dim visibleText As String = visibleRowCount.ToString(CultureInfo.CurrentCulture) & " visible"
            Return rowsText & " • " & columnsText & " • " & visibleText
        End Function

        Friend Shared Function ResolveAccentColor(ctx As MASThemeContext) As SKColor
            If ctx IsNot Nothing AndAlso ctx.BrandTheme IsNot Nothing Then Return ctx.BrandTheme.BrandPrimary
            If ctx IsNot Nothing AndAlso ctx.SemanticTheme IsNot Nothing Then Return ctx.SemanticTheme.Info
            Return New SKColor(64, 132, 246)
        End Function

        Friend Shared Function ResolvePrimaryTextColor(ctx As MASThemeContext) As SKColor
            If ctx IsNot Nothing AndAlso ctx.TextColorTheme IsNot Nothing Then Return ctx.TextColorTheme.PrimaryText.WithAlpha(TreeGridTokens.TextAlpha)
            Return Nexamas.UI.Values.Color.Text.Primary.WithAlpha(TreeGridTokens.TextAlpha)
        End Function

        Friend Shared Function ResolveMutedTextColor(ctx As MASThemeContext) As SKColor
            If ctx IsNot Nothing AndAlso ctx.TextColorTheme IsNot Nothing Then Return ctx.TextColorTheme.SecondaryText.WithAlpha(TreeGridTokens.MutedTextAlpha)
            Return Nexamas.UI.Values.Color.Text.Secondary.WithAlpha(TreeGridTokens.MutedTextAlpha)
        End Function

        Friend Shared Function HasOfficialVisualSurface() As Boolean
            Dim type As Type = GetType(MASTreeGrid)
            Return type.IsPublic AndAlso
                   type.IsClass AndAlso
                   type.IsSealed AndAlso
                   GetType(Nexamas.UI.Controls.MASControlBase).IsAssignableFrom(type) AndAlso
                   GetType(Nexamas.UI.Composition.IMASLayoutParticipant).IsAssignableFrom(type) AndAlso
                   GetType(Nexamas.UI.Layout.IMASIntrinsicSizeContract).IsAssignableFrom(type)
        End Function

        Friend Shared Function HasCachedProjectionPath() As Boolean
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
            Dim type As Type = GetType(MASTreeGrid)
            Return type.GetField("_visibleRowsCache", flags) IsNot Nothing AndAlso
                   type.GetField("_treeRevision", flags) IsNot Nothing AndAlso
                   type.GetField("_visibleRowsCacheRevision", flags) IsNot Nothing AndAlso
                   type.GetMethod("InvalidateTreeProjection", flags) IsNot Nothing
        End Function

        Friend Shared Function HasNoStoragePath() As Boolean
            Return Not HasForbiddenMember(GetType(MASTreeGrid), "Storage", "Repository", "Database", "Directory", "FileStream")
        End Function

        Friend Shared Function HasNoExternalProviderPath() As Boolean
            Return Not HasForbiddenMember(GetType(MASTreeGrid), "ExternalProvider", "RemoteProvider", "DataProvider", "Client")
        End Function

        Friend Shared Function HasNoBindingEnginePath() As Boolean
            Return Not HasForbiddenMember(GetType(MASTreeGrid), "BindingEngine", "Binder", "ViewModel")
        End Function

        Friend Shared Function HasItemsSourceProjectionPath() As Boolean
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
            Dim type As Type = GetType(MASTreeGrid)
            Return type.GetField("_itemsSource", flags) IsNot Nothing AndAlso
                   type.GetField("_sourceRevision", flags) IsNot Nothing AndAlso
                   type.GetMethod("SetItemsSource", flags) IsNot Nothing AndAlso
                   type.GetMethod("RebuildRowsFromItemsSource", flags) IsNot Nothing AndAlso
                   type.GetMethod("ReplaceRowsFromSource", flags) IsNot Nothing AndAlso
                   GetType(MASTreeGridSourceRow) IsNot Nothing
        End Function

        Friend Shared Function HasViewportWindowingPath() As Boolean
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
            Dim type As Type = GetType(MASTreeGrid)
            Return type.GetField("_topRowIndex", flags) IsNot Nothing AndAlso
                   type.GetField("_rowViewportCapacity", flags) IsNot Nothing AndAlso
                   type.GetMethod("ResolveRowViewportMaxTop", flags) IsNot Nothing AndAlso
                   type.GetMethod("GetVisibleSourceKeys", flags) IsNot Nothing
        End Function

        Friend Shared Function HasSortFilterProjectionPath() As Boolean
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public
            Dim type As Type = GetType(MASTreeGrid)
            Return type.GetField("_rowFilterText", flags) IsNot Nothing AndAlso
                   type.GetField("_sortColumnKey", flags) IsNot Nothing AndAlso
                   type.GetMethod("FilterRows", flags) IsNot Nothing AndAlso
                   type.GetMethod("ClearRowFilter", flags) IsNot Nothing AndAlso
                   type.GetMethod("SortRowsByLabel", flags) IsNot Nothing AndAlso
                   type.GetMethod("SortRowsByColumn", flags) IsNot Nothing AndAlso
                   type.GetMethod("AppendVisibleRowIfAllowed", flags) IsNot Nothing AndAlso
                   type.GetMethod("ApplyActiveSortToTree", flags) IsNot Nothing
        End Function

        Friend Shared Function HasEditingContractPath() As Boolean
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public
            Dim type As Type = GetType(MASTreeGrid)
            Return type.GetField("_editingRowKey", flags) IsNot Nothing AndAlso
                   type.GetField("_editingDraftText", flags) IsNot Nothing AndAlso
                   type.GetMethod("BeginEdit", flags) IsNot Nothing AndAlso
                   type.GetMethod("UpdateEditDraft", flags) IsNot Nothing AndAlso
                   type.GetMethod("CommitEdit", flags) IsNot Nothing AndAlso
                   type.GetMethod("CancelEdit", flags) IsNot Nothing AndAlso
                   type.GetMethod("TrySetRowLabel", flags) IsNot Nothing AndAlso
                   type.GetMethod("TrySetRowValue", flags) IsNot Nothing
        End Function

        Friend Shared Function HasSourceWriteBackAdapterPath() As Boolean
            Const controlFlags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
            Const sourceFlags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public
            Dim type As Type = GetType(MASTreeGrid)
            Dim adapterType As Type = GetType(MASTreeGridSourceWriteBackAdapter)
            Return adapterType IsNot Nothing AndAlso
                   adapterType.GetMethod("CommitLabel", sourceFlags) IsNot Nothing AndAlso
                   adapterType.GetMethod("CommitValue", sourceFlags) IsNot Nothing AndAlso
                   type.GetMethod("TryWriteSourceRowLabel", controlFlags) IsNot Nothing AndAlso
                   type.GetMethod("TryWriteSourceRowValue", controlFlags) IsNot Nothing
        End Function

        Friend Shared Function HasIncrementalSourceUpdatePath() As Boolean
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
            Dim type As Type = GetType(MASTreeGrid)
            Return type.GetMethod("TryApplyIncrementalItemsSourceChange", flags) IsNot Nothing AndAlso
                   type.GetMethod("TryApplyIncrementalSourceAdd", flags) IsNot Nothing AndAlso
                   type.GetMethod("TryApplyIncrementalSourceRemove", flags) IsNot Nothing AndAlso
                   type.GetMethod("TryApplyIncrementalSourceReplace", flags) IsNot Nothing AndAlso
                   GetType(MASTreeGridIncrementalSourceUpdateGate) IsNot Nothing
        End Function

        Friend Shared Function HasLargeDataWindowingProofPath() As Boolean
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public
            Dim type As Type = GetType(MASTreeGrid)
            Return type.GetMethod("CreateRowWindowSnapshot", flags) IsNot Nothing AndAlso
                   type.GetMethod("GetRealizedRowKeys", flags) IsNot Nothing AndAlso
                   type.GetMethod("HasBoundedRowWindow", flags) IsNot Nothing AndAlso
                   type.GetMethod("SetDiagnosticRowViewportCapacity", flags) IsNot Nothing AndAlso
                   GetType(MASTreeGridRowWindowSnapshot) IsNot Nothing AndAlso
                   TreeGridTokens.MaxProjectedSourceRows >= TreeGridTokens.LargeDataProofRows AndAlso
                   TreeGridTokens.LargeDataProofRows > TreeGridTokens.MaxRows
        End Function

        Friend Shared Function HasNoTreeViewDataGridWrapperPath() As Boolean
            Return Not HasForbiddenMember(GetType(MASTreeGrid), "TreeView", "DataGridView", "DataGrid")
        End Function

        Friend Shared Function HasNoVirtualizationEnginePath() As Boolean
            Return Not HasForbiddenMember(GetType(MASTreeGrid), "Virtualization", "Virtualizer", "RealizationRange", "RecyclePool")
        End Function

        Friend Shared Function HasMouseFocusRingSuppressed() As Boolean
            Return OverridesPointerFocusPolicy(GetType(MASTreeGrid))
        End Function

        Private Shared Function HasForbiddenMember(type As Type,
                                                   ParamArray tokens() As String) As Boolean
            If type Is Nothing OrElse tokens Is Nothing Then Return False
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.Static Or BindingFlags.Public Or BindingFlags.NonPublic
            For Each field As FieldInfo In type.GetFields(flags)
                If ContainsAny(field.Name, tokens) OrElse ContainsAny(field.FieldType.FullName, tokens) Then Return True
            Next
            For Each prop As PropertyInfo In type.GetProperties(flags)
                If ContainsAny(prop.Name, tokens) OrElse ContainsAny(prop.PropertyType.FullName, tokens) Then Return True
            Next
            For Each method As MethodInfo In type.GetMethods(flags)
                If method.IsSpecialName Then Continue For
                If ContainsAny(method.Name, tokens) OrElse ContainsAny(method.ReturnType.FullName, tokens) Then Return True
            Next
            Return False
        End Function

        Private Shared Function OverridesPointerFocusPolicy(type As Type) As Boolean
            If type Is Nothing Then Return False
            Const flags As BindingFlags = BindingFlags.Instance Or BindingFlags.NonPublic
            Dim method As MethodInfo = type.GetMethod("WantsPointerFocus", flags)
            Return method IsNot Nothing AndAlso method.DeclaringType Is type AndAlso method.ReturnType Is GetType(Boolean)
        End Function

        Private Shared Function ContainsAny(value As String,
                                            tokens As IEnumerable(Of String)) As Boolean
            Dim text As String = If(value, String.Empty)
            For Each token As String In tokens
                If token IsNot Nothing AndAlso token.Length > 0 AndAlso text.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0 Then Return True
            Next
            Return False
        End Function
    End Class

End Namespace
