'========================================================
' File: MASTileBox.vb  (SSOT + Fixes + Fluent SDK API)
'========================================================
Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Windows.Forms
Imports Nexamas.UI.Controls
Imports Nexamas.UI.Composition
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Motion
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Scroll
Imports Nexamas.UI.SkiaScroll
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Values
Imports Nexamas.UI.Virtualization
Imports SkiaSharp


Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASTileBox

        Public Property SelectedId As Guid
            Get
                Return _selectedId
            End Get
            Set(value As Guid)
                If value = _selectedId AndAlso (value <> Guid.Empty OrElse _selectedRef Is Nothing) Then Return
                SetSelectionInternal(value, Nothing)
            End Set
        End Property

        Public Property SelectedIndex As Integer
            Get
                If _selectedId <> Guid.Empty Then
                    Return FindIndexById(_selectedId)
                End If

                If _selectedRef Is Nothing Then Return -1
                Return _items.IndexOf(_selectedRef)
            End Get
            Set(value As Integer)
                If value < 0 OrElse value >= _items.Count Then
                    SetSelectionInternal(Guid.Empty, Nothing)
                    Return
                End If

                Dim it As MASTileItem = _items(value)

                If it Is Nothing Then
                    SetSelectionInternal(Guid.Empty, Nothing)
                    Return
                End If

                If it.IconId <> Guid.Empty Then
                    SetSelectionInternal(it.IconId, Nothing)
                Else
                    SetSelectionInternal(Guid.Empty, it)
                End If
            End Set
        End Property

        Public ReadOnly Property SelectedItem As MASTileItem
            Get
                If _selectedId <> Guid.Empty Then
                    Dim idx As Integer = FindIndexById(_selectedId)
                    If idx >= 0 AndAlso idx < _items.Count Then Return _items(idx)
                    Return Nothing
                End If

                Return _selectedRef
            End Get
        End Property

        Public Function WithSelectedIndex(index As Integer) As MASTileBox
            Me.SelectedIndex = index
            Return Me
        End Function

        Public Function WithSelectedId(id As Guid) As MASTileBox
            Me.SelectedId = id
            Return Me
        End Function

        Public Function ClearSelection() As MASTileBox
            Me.SelectedIndex = -1
            Return Me
        End Function

        Private Sub SetSelectionInternal(id As Guid, refItem As MASTileItem, Optional raiseChanged As Boolean = True, Optional invalidate As Boolean = True)

            Dim previousIndex As Integer = SelectedIndex
            Dim changed As Boolean = False

            If id <> _selectedId Then
                _selectedId = id
                changed = True
            End If

            If Not Object.ReferenceEquals(refItem, _selectedRef) Then
                _selectedRef = refItem
                changed = True
            End If

            If Not changed Then Return

            StartSelectionMotion(previousIndex, SelectedIndex)
            EnsureSelectionVisible()
            MarkFrameCacheDirty()

            If raiseChanged Then
                RaiseEvent SelectionChanged(Me, EventArgs.Empty)
            End If

            If invalidate Then
                InvalidateVisual()
            End If

        End Sub

        Private Sub StartSelectionMotion(previousIndex As Integer,
                                         nextIndex As Integer)
            StopSelectionMotion()

            If previousIndex < 0 OrElse nextIndex < 0 OrElse previousIndex = nextIndex Then
                _hasSelectionMotion = False
                _selectionMotionProgress = 1.0F
                Return
            End If

            _selectionMotionFromIndex = previousIndex
            _selectionMotionToIndex = nextIndex
            _selectionMotionProgress = 0.0F
            _hasSelectionMotion = True

            Dim plan As MASMotionTransitionPlan = MASMotionSystem.CreateTransitionPlan(MASMotionTokenId.SelectionMove, 0.0F, 1.0F)
            If Not plan.ShouldAnimate Then
                CompleteSelectionMotionFrame(Nothing)
                Return
            End If

            _selectionMotionRunner = MASMotionSystem.CreateTimelineRunner(
                owner:=Me,
                consumerName:="MASTileBox.TileSelection",
                plan:=plan,
                requestFrame:=AddressOf SafeInvalidate,
                applyFrame:=AddressOf ApplySelectionMotionFrame,
                completed:=AddressOf CompleteSelectionMotionFrame)
            _selectionMotionRunner.Start()
        End Sub

        Private Sub ApplySelectionMotionFrame(frame As MASMotionFrame)
            If frame Is Nothing Then Return
            _selectionMotionProgress = MASMotionSystem.ClampProgress(frame.Value)
            MarkFrameCacheDirty()
            InvalidateVisual()
        End Sub

        Private Sub CompleteSelectionMotionFrame(frame As MASMotionFrame)
            _selectionMotionProgress = 1.0F
            _hasSelectionMotion = False
            _selectionMotionRunner = Nothing
            MarkFrameCacheDirty()
            InvalidateVisual()
        End Sub

        Private Sub StopSelectionMotion()
            If _selectionMotionRunner IsNot Nothing Then
                _selectionMotionRunner.Dispose()
                _selectionMotionRunner = Nothing
            End If

            _hasSelectionMotion = False
            _selectionMotionProgress = 1.0F
            _selectionMotionFromIndex = -1
            _selectionMotionToIndex = -1
        End Sub

        Private Function IsSelectionMotionActive() As Boolean
            Return _hasSelectionMotion AndAlso _selectionMotionRunner IsNot Nothing AndAlso _selectionMotionRunner.IsRunning
        End Function

        Private Function IsTileSelected(item As MASTileItem) As Boolean

            If item Is Nothing Then Return False
            If IsSelectionMotionActive() AndAlso SelectedIndex >= 0 AndAlso SelectedIndex = _selectionMotionToIndex Then Return False

            If item.IconId <> Guid.Empty AndAlso _selectedId <> Guid.Empty Then
                Return item.IconId = _selectedId
            End If

            If item.IconId = Guid.Empty AndAlso _selectedRef IsNot Nothing Then
                Return Object.ReferenceEquals(item, _selectedRef)
            End If

            Return False

        End Function

        Private Function FindIndexById(id As Guid) As Integer

            If id = Guid.Empty Then Return -1

            For i As Integer = 0 To _items.Count - 1
                Dim it As MASTileItem = _items(i)

                If it IsNot Nothing AndAlso it.IconId = id Then
                    Return i
                End If
            Next

            Return -1

        End Function

        Private Sub EnsureSelectionVisible()

            If _scroll Is Nothing Then Return
            If _lastCols <= 0 OrElse _rowStepPx <= 0 Then Return

            Dim idx As Integer = -1

            If _selectedId <> Guid.Empty Then
                idx = FindIndexById(_selectedId)
            ElseIf _selectedRef IsNot Nothing Then
                idx = _items.IndexOf(_selectedRef)
            End If

            If idx < 0 Then Return

            Dim request As MASVirtualizationScrollRequest =
                _virtualization.CreateScrollToItemRequest(
                    itemIndex:=idx,
                    columnCount:=_lastCols,
                    viewportOffsetPx:=_scroll.Offset,
                    viewportExtentPx:=Math.Max(0.0F, _scrollHost.Height),
                    alignment:=MASVirtualizationAlignment.Nearest)

            _scroll.ScrollToOffset(request.TargetOffsetPx)

        End Sub

    End Class

End Namespace
