Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks

Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Controls
Imports Nexamas.UI.FileSystem
Imports Nexamas.UI.General
Imports Nexamas.UI.Icons
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Platform
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values

Imports SkiaSharp

Imports Nexamas.UI.Performance

Namespace Nexamas.UI.Components


    Partial Friend NotInheritable Class MASFileBrowser
#Region "Navigation and asynchronous loading"

        Friend Sub LoadRoots()
            CancelPendingItemLoad()

            _currentPath = String.Empty
            _isLoadingItems = False
            ClearSelectionInternal(notifySelectionChanged:=False)

            _items.Clear()

            If _computerRootBuilder IsNot Nothing Then
                _items.AddRange(
                    _computerRootBuilder.BuildChildren(
                        includeSpecialFolders:=True,
                        includeDrives:=True
                    )
                )
            End If

            PopulateListFromItems()

            RaiseEvent CurrentPathChanged(Me, EventArgs.Empty)
            RaiseEvent ItemsChanged(Me, EventArgs.Empty)
            RaiseEvent SelectionChanged(Me, EventArgs.Empty)

            SafeInvalidate()
        End Sub


        Friend Sub RefreshItems()
            If _provider Is Nothing Then
                CancelPendingItemLoad()
                _isLoadingItems = False
                _items.Clear()

                ClearSelectionInternal(False)
                PopulateListFromItems()

                RaiseEvent ItemsChanged(Me, EventArgs.Empty)
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)

                SafeInvalidate()
                Return
            End If

            If String.IsNullOrWhiteSpace(_currentPath) Then
                CancelPendingItemLoad()
                _isLoadingItems = False
                _items.Clear()

                Dim cancellable As IFileSystemCancellableProvider = TryCast(_provider, IFileSystemCancellableProvider)

                If cancellable IsNot Nothing Then
                    _items.AddRange(cancellable.GetRoots(CancellationToken.None))
                Else
                    _items.AddRange(_provider.GetRoots())
                End If

                ReconcileSelectionWithItems(False)
                PopulateListFromItems()
                SyncListSelectionFromSelectionState()

                RaiseEvent ItemsChanged(Me, EventArgs.Empty)
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)

                SafeInvalidate()
                Return
            End If

            BeginAsyncItemLoad(_currentPath, _showHidden, _filter)
        End Sub


        Private Sub BeginAsyncItemLoad(path As String, showHidden As Boolean, filter As String)
            Dim normalizedPath As String = If(path, String.Empty)
            If String.IsNullOrWhiteSpace(normalizedPath) Then Return

            Dim generation As Integer
            Dim cts As CancellationTokenSource

            SyncLock _loadGate
                CancelPendingItemLoadNoLock()
                _loadGeneration += 1
                generation = _loadGeneration
                cts = New CancellationTokenSource()
                _loadCancellation = cts
                _isLoadingItems = True
            End SyncLock

            _items.Clear()
            PopulateListFromItems()
            SafeInvalidate()

            Dim token As CancellationToken = cts.Token
            MASPerformanceSystem.RecordAsyncLoadStarted()

            Task.Run(
                Sub()
                    Dim children As List(Of FileSystemEntry) = Nothing
                    Dim canceled As Boolean = False

                    Try
                        token.ThrowIfCancellationRequested()

                        Dim currentEntry As New FileSystemEntry With {
                            .Name = normalizedPath,
                            .FullPath = normalizedPath,
                            .EntryType = FileSystemEntryType.Folder
                        }

                        children = _childrenLoader.GetChildren(
                            entry:=currentEntry,
                            showHidden:=showHidden,
                            cancellationToken:=token
                        )

                        token.ThrowIfCancellationRequested()

                        children = ApplyFilter(children, filter)

                        children = children.
                            OrderBy(Function(x) If(x.IsFolderLike, 0, 1)).
                            ThenBy(Function(x) x.Name, StringComparer.CurrentCultureIgnoreCase).
                            ToList()

                        token.ThrowIfCancellationRequested()
                        MASPerformanceSystem.RecordAsyncLoadCompleted()
                    Catch masCanceled1 As OperationCanceledException
                        canceled = True
                        MASPerformanceSystem.RecordAsyncLoadCancelled()
                    Catch masCaughtException1 As Exception
                        MASPerformanceSystem.RecordAsyncLoadFailed()
                        Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowFileSystemBoundary(masCaughtException1)
                        children = New List(Of FileSystemEntry)()
                    End Try

                    PostToUi(
                        Sub()
                            CompleteAsyncItemLoad(generation, normalizedPath, children, canceled)
                        End Sub)
                End Sub,
                token)
        End Sub


        Private Sub CompleteAsyncItemLoad(generation As Integer,
                                          path As String,
                                          children As List(Of FileSystemEntry),
                                          canceled As Boolean)

            If _disposedLocal Then Return

            SyncLock _loadGate
                If generation <> _loadGeneration Then Return
                If canceled Then Return

                _isLoadingItems = False

                If _loadCancellation IsNot Nothing Then
                    _loadCancellation.Dispose()
                    _loadCancellation = Nothing
                End If
            End SyncLock

            If Not String.Equals(_currentPath, path, StringComparison.OrdinalIgnoreCase) Then Return

            _items.Clear()

            If children IsNot Nothing Then
                _items.AddRange(children)
            End If

            ReconcileSelectionWithItems(False)
            PopulateListFromItems()
            SyncListSelectionFromSelectionState()

            RaiseEvent ItemsChanged(Me, EventArgs.Empty)
            RaiseEvent SelectionChanged(Me, EventArgs.Empty)

            SafeInvalidate()
        End Sub


        Private Sub CancelPendingItemLoad()
            SyncLock _loadGate
                CancelPendingItemLoadNoLock()
                _loadGeneration += 1
                _isLoadingItems = False
            End SyncLock
        End Sub


        Private Sub CancelPendingItemLoadNoLock()
            Dim oldCts As CancellationTokenSource = _loadCancellation
            _loadCancellation = Nothing

            If oldCts Is Nothing Then Return

            Try
                oldCts.Cancel()
            Catch masCaughtException2 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowFileSystemBoundary(masCaughtException2)
            Finally
                Try
                    oldCts.Dispose()
                Catch masCaughtException3 As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException3)
                End Try
            End Try
        End Sub


        Private Sub PostToUi(action As Action)
            If action Is Nothing Then Return

            If _runtimeServices IsNot Nothing AndAlso
               _runtimeServices.PostToUiThread(action, "MASFileBrowser.AsyncCompletion") Then
                Return
            End If

            Nexamas.UI.Controls.MASControlRuntimeServices.ReportDroppedUiPostWithoutFallback(
                "MASFileBrowser.AsyncCompletion",
                "File browser async completion was dropped because the owning host UI dispatcher was unavailable; stale SynchronizationContext fallback is not permitted.")
        End Sub


        Friend Sub NavigateUp()
            If _provider Is Nothing Then Return
            If String.IsNullOrWhiteSpace(_currentPath) Then Return

            Dim parentPath As String = _provider.GetParentPath(_currentPath)

            If String.IsNullOrWhiteSpace(parentPath) Then
                LoadRoots()
            Else
                CurrentPath = parentPath
            End If
        End Sub



#End Region

    End Class

End Namespace
