Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports Nexamas.UI.Values

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Public immutable Kanban board snapshot. It captures visual board order,
    ''' card payloads, selected keys, and viewport state only; it deliberately does
    ''' not own task persistence, workflow engines, remote providers, background
    ''' synchronization, or OS drag/drop runtime state.
    ''' </summary>
    Public NotInheritable Class MASKanbanBoardSnapshot
        Public Sub New(version As Integer,
                       title As String,
                       selectedColumnKey As String,
                       selectedCardKey As String,
                       leftColumnIndex As Integer,
                       columns As IEnumerable(Of MASKanbanColumnSnapshot))
            Me.Version = Math.Max(1, version)
            Me.Title = MASKanbanBoardPolicy.NormalizeTitle(title)
            Me.SelectedColumnKey = MASKanbanBoardPolicy.NormalizeText(selectedColumnKey, String.Empty, 96)
            Me.SelectedCardKey = MASKanbanBoardPolicy.NormalizeText(selectedCardKey, String.Empty, 96)
            Me.LeftColumnIndex = Math.Max(0, leftColumnIndex)
            Dim copy As New List(Of MASKanbanColumnSnapshot)()
            If columns IsNot Nothing Then
                For Each column As MASKanbanColumnSnapshot In columns
                    If column IsNot Nothing Then copy.Add(column)
                Next
            End If
            Me.Columns = New ReadOnlyCollection(Of MASKanbanColumnSnapshot)(copy)
        End Sub

        Public ReadOnly Property Version As Integer
        Public ReadOnly Property Title As String
        Public ReadOnly Property SelectedColumnKey As String
        Public ReadOnly Property SelectedCardKey As String
        Public ReadOnly Property LeftColumnIndex As Integer
        Public ReadOnly Property Columns As IReadOnlyList(Of MASKanbanColumnSnapshot)
    End Class

    ''' <summary>
    ''' Public immutable column entry used by MASKanbanBoardSnapshot.
    ''' </summary>
    Public NotInheritable Class MASKanbanColumnSnapshot
        Public Sub New(key As String,
                       title As String,
                       topCardIndex As Integer,
                       cards As IEnumerable(Of MASKanbanCardSnapshot))
            Me.Key = MASKanbanBoardPolicy.NormalizeKey(key, "column", 1)
            Me.Title = MASKanbanBoardPolicy.NormalizeColumnTitle(title)
            Me.TopCardIndex = Math.Max(0, topCardIndex)
            Dim copy As New List(Of MASKanbanCardSnapshot)()
            If cards IsNot Nothing Then
                For Each card As MASKanbanCardSnapshot In cards
                    If card IsNot Nothing Then copy.Add(card)
                Next
            End If
            Me.Cards = New ReadOnlyCollection(Of MASKanbanCardSnapshot)(copy)
        End Sub

        Public ReadOnly Property Key As String
        Public ReadOnly Property Title As String
        Public ReadOnly Property TopCardIndex As Integer
        Public ReadOnly Property Cards As IReadOnlyList(Of MASKanbanCardSnapshot)
    End Class

    ''' <summary>
    ''' Public immutable card entry used by MASKanbanBoardSnapshot.
    ''' </summary>
    Public NotInheritable Class MASKanbanCardSnapshot
        Public Sub New(key As String,
                       title As String,
                       detail As String,
                       badgeText As String)
            Me.Key = MASKanbanBoardPolicy.NormalizeKey(key, "card", 1)
            Me.Title = MASKanbanBoardPolicy.NormalizeCardTitle(title)
            Me.Detail = MASKanbanBoardPolicy.NormalizeCardDetail(detail)
            Me.BadgeText = MASKanbanBoardPolicy.NormalizeBadgeText(badgeText)
        End Sub

        Public ReadOnly Property Key As String
        Public ReadOnly Property Title As String
        Public ReadOnly Property Detail As String
        Public ReadOnly Property BadgeText As String
    End Class

    Partial Public NotInheritable Class MASKanbanBoard

        Public Function CreateBoardSnapshot() As MASKanbanBoardSnapshot
            Dim columns As New List(Of MASKanbanColumnSnapshot)()
            For Each column As KanbanColumnState In _columns
                If column Is Nothing Then Continue For
                Dim cards As New List(Of MASKanbanCardSnapshot)()
                For Each card As KanbanCardState In column.Cards
                    If card Is Nothing Then Continue For
                    cards.Add(New MASKanbanCardSnapshot(card.Key, card.Title, card.Detail, card.BadgeText))
                Next
                columns.Add(New MASKanbanColumnSnapshot(column.Key, column.Title, column.TopCardIndex, cards))
            Next
            Return New MASKanbanBoardSnapshot(1, _title, SelectedColumnKey, SelectedCardKey, _leftColumnIndex, columns)
        End Function

        Public Function RestoreBoardSnapshot(snapshot As MASKanbanBoardSnapshot) As Boolean
            If snapshot Is Nothing Then Return False
            ClearKanbanSourceLinkForManualMutation()
            _columns.Clear()
            _columnLookup.Clear()
            _columnIndexByKey.Clear()
            _cardLocationByKey.Clear()
            _columnRects.Clear()
            _cardHitRects.Clear()
            _cardCount = 0
            _selectedColumnIndex = -1
            _selectedCardIndex = -1
            _hoverColumnIndex = -1
            _hoverCardIndex = -1
            _pressedColumnIndex = -1
            _pressedCardIndex = -1
            _dragTargetColumnIndex = -1
            _draggingCard = False
            _hasPressedPoint = False

            Dim usedColumnKeys As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
            Dim usedCardKeys As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)

            For Each columnSnapshot As MASKanbanColumnSnapshot In snapshot.Columns
                If columnSnapshot Is Nothing Then Continue For
                If _columns.Count >= KanbanBoardTokens.MaxColumns Then Exit For
                Dim columnKey As String = ResolveUniqueKanbanKey(columnSnapshot.Key, "column", _columns.Count + 1, usedColumnKeys)
                Dim column As New KanbanColumnState(columnKey, columnSnapshot.Title)
                For Each cardSnapshot As MASKanbanCardSnapshot In columnSnapshot.Cards
                    If cardSnapshot Is Nothing Then Continue For
                    If _cardCount >= KanbanBoardTokens.MaxCards Then Exit For
                    Dim cardKey As String = ResolveUniqueKanbanKey(cardSnapshot.Key, "card", _cardCount + 1, usedCardKeys)
                    column.Cards.Add(New KanbanCardState(cardKey, cardSnapshot.Title, cardSnapshot.Detail, cardSnapshot.BadgeText))
                    _cardCount += 1
                Next
                column.TopCardIndex = Math.Max(0, Math.Min(columnSnapshot.TopCardIndex, ResolveColumnMaxTop(column)))
                _columnIndexByKey(column.Key) = _columns.Count
                _columns.Add(column)
                _columnLookup.Add(column.Key, column)
            Next

            For columnIndex As Integer = 0 To _columns.Count - 1
                ReindexCardLocationsForColumn(columnIndex)
            Next

            _title = MASKanbanBoardPolicy.NormalizeTitle(snapshot.Title)
            _leftColumnIndex = Math.Max(0, Math.Min(snapshot.LeftColumnIndex, Math.Max(0, _columns.Count - 1)))

            If snapshot.SelectedCardKey.Length > 0 Then
                Dim location As Tuple(Of Integer, Integer) = FindCardLocation(snapshot.SelectedCardKey)
                If location IsNot Nothing Then
                    _selectedColumnIndex = location.Item1
                    _selectedCardIndex = location.Item2
                End If
            End If
            If _selectedColumnIndex < 0 AndAlso snapshot.SelectedColumnKey.Length > 0 Then
                Dim selectedColumn As KanbanColumnState = FindColumn(snapshot.SelectedColumnKey)
                Dim selectedColumnIndex As Integer = ResolveColumnIndex(selectedColumn)
                If selectedColumnIndex >= 0 Then
                    _selectedColumnIndex = selectedColumnIndex
                    _selectedCardIndex = If(_columns(selectedColumnIndex).Cards.Count > 0, 0, -1)
                End If
            End If
            If _selectedColumnIndex < 0 AndAlso _columns.Count > 0 Then
                _selectedColumnIndex = 0
                _selectedCardIndex = If(_columns(0).Cards.Count > 0, 0, -1)
            End If

            NormalizeSelection()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASKanbanBoard.RestoreBoardSnapshot")
            InvalidateVisual()
            RaiseBoardChangedSafe()
            RaiseSelectedCardChangedSafe()
            Return True
        End Function

        Public Function ResetBoardViewport() As MASKanbanBoard
            _leftColumnIndex = 0
            For Each column As KanbanColumnState In _columns
                If column IsNot Nothing Then column.TopCardIndex = 0
            Next
            InvalidateVisual()
            Return Me
        End Function

        Friend Function HasBoardSnapshotForTests() As Boolean
            Dim snapshot As MASKanbanBoardSnapshot = CreateBoardSnapshot()
            Return snapshot IsNot Nothing AndAlso snapshot.Columns.Count = _columns.Count
        End Function

    End Class

End Namespace
