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

Namespace Nexamas.UI.Components


    Partial Friend NotInheritable Class MASFileBrowser
#Region "Selection and inline rename"

        Friend Sub SelectIndex(index As Integer)
            If index < -1 OrElse index >= _items.Count Then Return

            If index = -1 Then
                ClearSelection()
                Return
            End If

            SelectOnly(index)
        End Sub


        Friend Sub SelectOnly(index As Integer)
            If index < 0 OrElse index >= _items.Count Then Return

            Dim item As FileSystemEntry = _items(index)
            If item Is Nothing OrElse Not CanSelect(item) Then Return

            Dim path As String = If(item.FullPath, String.Empty)
            If String.IsNullOrWhiteSpace(path) Then Return

            Dim changed As Boolean = False

            If _selectedPaths.Count <> 1 OrElse Not _selectedPaths.Contains(path) Then
                _selectedPaths.Clear()
                _selectedPaths.Add(path)
                changed = True
            End If

            If _selectedIndex <> index Then
                _selectedIndex = index
                changed = True
            End If

            If changed Then MarkFileBrowserSelectionSnapshotDirty()

            SyncListSelectionFromSelectionState()

            If changed Then
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)
                SafeInvalidate()
            End If
        End Sub


        Friend Sub ToggleSelection(index As Integer)
            If index < 0 OrElse index >= _items.Count Then Return

            Dim item As FileSystemEntry = _items(index)
            If item Is Nothing OrElse Not CanSelect(item) Then Return

            Dim path As String = If(item.FullPath, String.Empty)
            If String.IsNullOrWhiteSpace(path) Then Return

            Dim changed As Boolean = False

            If _selectionPolicy Is Nothing OrElse Not _selectionPolicy.AllowMultiSelect Then
                If _selectedPaths.Count <> 1 OrElse Not _selectedPaths.Contains(path) Then
                    _selectedPaths.Clear()
                    _selectedPaths.Add(path)
                    changed = True
                End If
            Else
                If _selectedPaths.Contains(path) Then
                    _selectedPaths.Remove(path)
                Else
                    _selectedPaths.Add(path)
                End If

                changed = True
            End If

            If changed Then MarkFileBrowserSelectionSnapshotDirty()

            ReconcileSelectionWithItems(False)
            SyncListSelectionFromSelectionState()

            If changed Then
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)
                SafeInvalidate()
            End If
        End Sub


        Friend Sub ClearSelection()
            If ClearSelectionInternal(True) Then
                SyncListSelectionFromSelectionState()
                SafeInvalidate()
            End If
        End Sub


        Friend Function IsSelected(index As Integer) As Boolean
            If index < 0 OrElse index >= _items.Count Then Return False

            Dim it As FileSystemEntry = _items(index)
            If it Is Nothing Then Return False

            Return _selectedPaths.Contains(If(it.FullPath, String.Empty))
        End Function


        Friend Sub EnsureVisible(index As Integer)
            If index < 0 OrElse index >= _items.Count Then Return
            If _list Is Nothing Then Return

            If _list.SelectedIndex <> index Then
                _syncingListSelection = True

                Try
                    _list.SelectedIndex = index
                Finally
                    _syncingListSelection = False
                End Try
            End If
        End Sub


        Friend Function BeginInlineRenameEntry(entry As FileSystemEntry) As Boolean
            If entry Is Nothing Then Return False
            If _list Is Nothing Then Return False
            If Not CanInlineRename(entry) Then Return False

            For Each listItem As MASListViewItem In _list.Items
                If listItem Is Nothing Then Continue For

                Dim model As FileSystemEntry = TryCast(listItem.Tag, FileSystemEntry)
                If Object.ReferenceEquals(model, entry) Then
                    RequestFocus(_list)
                    Return _list.BeginInlineRename(listItem)
                End If
            Next

            Return False
        End Function


        Private Sub RebuildSelectionFromList()
            If _list Is Nothing Then Return

            Dim oldSignature As String = BuildSelectionSignature()

            _selectedPaths.Clear()
            _selectedIndex = -1
            MarkFileBrowserSelectionSnapshotDirty()

            Dim selectedListItems As IReadOnlyList(Of MASListViewItem) = _list.SelectedItems

            If _selectionPolicy Is Nothing OrElse Not _selectionPolicy.AllowMultiSelect Then
                Dim primary As MASListViewItem = _list.SelectedItem

                If primary IsNot Nothing Then
                    Dim entry As FileSystemEntry = TryCast(primary.Tag, FileSystemEntry)
                    Dim idx As Integer = _items.IndexOf(entry)

                    If entry IsNot Nothing AndAlso idx >= 0 Then
                        _selectedIndex = idx

                        If CanSelect(entry) AndAlso Not String.IsNullOrWhiteSpace(entry.FullPath) Then
                            _selectedPaths.Add(entry.FullPath)
                        End If
                    End If
                End If

                If selectedListItems IsNot Nothing AndAlso selectedListItems.Count > 1 Then
                    SyncListSelectionFromSelectionState()
                End If
            Else
                If selectedListItems IsNot Nothing Then
                    For Each listItem As MASListViewItem In selectedListItems
                        Dim entry As FileSystemEntry = TryCast(listItem.Tag, FileSystemEntry)
                        Dim idx As Integer = _items.IndexOf(entry)

                        If entry Is Nothing OrElse idx < 0 Then Continue For

                        If _selectedIndex < 0 Then
                            _selectedIndex = idx
                        End If

                        If CanSelect(entry) AndAlso Not String.IsNullOrWhiteSpace(entry.FullPath) Then
                            _selectedPaths.Add(entry.FullPath)
                        End If
                    Next
                End If
            End If

            MarkFileBrowserSelectionSnapshotDirty()

            Dim newSignature As String = BuildSelectionSignature()

            If Not String.Equals(oldSignature, newSignature, StringComparison.Ordinal) Then
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)
                SafeInvalidate()
            End If
        End Sub


        Private Sub SyncListSelectionFromSelectionState()
            If _list Is Nothing Then Return

            _syncingListSelection = True

            Try
                SyncListSelectionFromSelectionStateCore()
            Finally
                _syncingListSelection = False
            End Try
        End Sub


        Private Sub SyncListSelectionFromSelectionStateCore()
            If _list Is Nothing Then Return

            If _selectedIndex >= 0 AndAlso _selectedIndex < _items.Count Then
                _list.SelectedIndex = _selectedIndex
            Else
                _list.SelectedIndex = -1
            End If
        End Sub


        Private Shared Function CanInlineRename(item As FileSystemEntry) As Boolean
            If item Is Nothing Then Return False

            Select Case item.EntryType
                Case FileSystemEntryType.FileItem,
                     FileSystemEntryType.Folder
                    Return Not String.IsNullOrWhiteSpace(item.FullPath)
            End Select

            Return False
        End Function


        Private Function CanSelect(item As FileSystemEntry) As Boolean
            If item Is Nothing Then Return False
            If _selectionPolicy Is Nothing Then Return False

            Return _selectionPolicy.CanSelect(item)
        End Function


        Private Function ClearSelectionInternal(notifySelectionChanged As Boolean) As Boolean
            Dim changed As Boolean = (_selectedPaths.Count > 0 OrElse _selectedIndex <> -1)

            _selectedPaths.Clear()
            _selectedIndex = -1

            If changed Then MarkFileBrowserSelectionSnapshotDirty()

            If changed AndAlso notifySelectionChanged Then
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)
            End If

            Return changed
        End Function


        Private Sub ReconcileSelectionWithItems(notifySelectionChanged As Boolean)
            Dim changed As Boolean = False

            If _selectedPaths.Count > 0 Then
                Dim validPaths As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)

                For Each it As FileSystemEntry In _items
                    If it Is Nothing Then Continue For

                    Dim p As String = If(it.FullPath, String.Empty)
                    If String.IsNullOrWhiteSpace(p) Then Continue For

                    If _selectedPaths.Contains(p) AndAlso CanSelect(it) Then
                        validPaths.Add(p)
                    End If
                Next

                If validPaths.Count <> _selectedPaths.Count OrElse Not _selectedPaths.SetEquals(validPaths) Then
                    _selectedPaths.Clear()

                    For Each p As String In validPaths
                        _selectedPaths.Add(p)
                    Next

                    changed = True
                    MarkFileBrowserSelectionSnapshotDirty()
                End If
            End If

            If _selectionPolicy Is Nothing OrElse Not _selectionPolicy.AllowMultiSelect Then
                If _selectedPaths.Count > 1 Then
                    Dim firstPath As String = GetPrimarySelectedPath()
                    _selectedPaths.Clear()

                    If Not String.IsNullOrWhiteSpace(firstPath) Then
                        _selectedPaths.Add(firstPath)
                    End If

                    changed = True
                    MarkFileBrowserSelectionSnapshotDirty()
                End If
            End If

            Dim newPrimaryIndex As Integer = -1

            If _selectedPaths.Count > 0 Then
                For i As Integer = 0 To _items.Count - 1
                    Dim it As FileSystemEntry = _items(i)
                    If it Is Nothing Then Continue For

                    If _selectedPaths.Contains(If(it.FullPath, String.Empty)) Then
                        newPrimaryIndex = i
                        Exit For
                    End If
                Next
            End If

            If _selectedIndex <> newPrimaryIndex Then
                _selectedIndex = newPrimaryIndex
                changed = True
                MarkFileBrowserSelectionSnapshotDirty()
            End If

            If notifySelectionChanged AndAlso changed Then
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)
            End If
        End Sub


        Private Function GetPrimarySelectedPath() As String
            If _selectedIndex >= 0 AndAlso _selectedIndex < _items.Count Then
                Dim selected As FileSystemEntry = _items(_selectedIndex)

                If selected IsNot Nothing AndAlso _selectedPaths.Contains(If(selected.FullPath, String.Empty)) Then
                    Return selected.FullPath
                End If
            End If

            For Each it As FileSystemEntry In _items
                If it Is Nothing Then Continue For

                If _selectedPaths.Contains(If(it.FullPath, String.Empty)) Then
                    Return it.FullPath
                End If
            Next

            Return String.Empty
        End Function


        Private Function EnsureSelectedPathSnapshot() As IReadOnlyList(Of String)
            If Not _selectedPathsSnapshotDirty Then Return _selectedPathsSnapshot

            Dim paths As New List(Of String)(_selectedPaths.Count)

            For Each path As String In _selectedPaths
                If Not String.IsNullOrWhiteSpace(path) Then
                    paths.Add(path)
                End If
            Next

            paths.Sort(StringComparer.OrdinalIgnoreCase)
            _selectedPathsSnapshot = paths.AsReadOnly()
            _selectedPathsSnapshotDirty = False

            Return _selectedPathsSnapshot
        End Function


        Private Sub MarkFileBrowserSelectionSnapshotDirty()
            _selectedPathsSnapshotDirty = True
            _selectionSignatureDirty = True
        End Sub


        Private Function BuildSelectionSignature() As String
            If _selectionSignatureDirty Then
                _selectionSignatureSnapshot = _selectedIndex.ToString() & "|" & String.Join(";", EnsureSelectedPathSnapshot())
                _selectionSignatureDirty = False
            End If

            Return _selectionSignatureSnapshot
        End Function



#End Region

    End Class

End Namespace
