Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Collections.ObjectModel
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Controls
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Official Nexamas UI two-list transfer surface. It owns visual list-pair
    ''' composition, source/target item movement, selection, keyboard transfer, RTL
    ''' visual order, and readiness evidence. It intentionally does not own data
    ''' binding, permissions, persistence, remote providers, drag/drop runtimes, or
    ''' virtualization; those remain with the host or existing Nexamas UI systems.
    ''' </summary>
    Partial Public NotInheritable Class MASTransferList
        Inherits MASControlBase
        Implements IMASLayoutParticipant
        Implements IMASIntrinsicSizeContract

#Region "Fields"

        Private Shared ReadOnly _contract As MASResolvedComponentContract =
            MASArchitectureRuntime.ResolveContractOrThrow(GetType(MASTransferList))

        Private Enum TransferAction
            None = 0
            AddOne = 1
            AddAll = 2
            RemoveOne = 3
            RemoveAll = 4
        End Enum

        Private Structure RowHit
            Friend Sub New(leftList As Boolean, index As Integer)
                Me.LeftList = leftList
                Me.Index = index
            End Sub

            Friend LeftList As Boolean
            Friend Index As Integer
        End Structure

        Private ReadOnly _primitives As New MASVisualPrimitivesPainter()
        Private ReadOnly _available As New List(Of String)()
        Private ReadOnly _selected As New List(Of String)()
        Private _title As String = TransferListTokens.DefaultTitle
        Private _availableTitle As String = TransferListTokens.DefaultAvailableTitle
        Private _selectedTitle As String = TransferListTokens.DefaultSelectedTitle
        Private _availableFilterText As String = String.Empty
        Private _selectedFilterText As String = String.Empty
        Private _activeAvailablePanel As Boolean = True
        Private _availableSelectedIndex As Integer = -1
        Private _selectedSelectedIndex As Integer = -1
        Private _availableTopIndex As Integer
        Private _selectedTopIndex As Integer
        Private _hoverRow As RowHit = New RowHit(False, -1)
        Private _pressedRow As RowHit = New RowHit(False, -1)
        Private _hoverAction As TransferAction = TransferAction.None
        Private _pressedAction As TransferAction = TransferAction.None
        Private _leftPanelRect As SKRect = SKRect.Empty
        Private _rightPanelRect As SKRect = SKRect.Empty
        Private _railRect As SKRect = SKRect.Empty
        Private _lastLayoutPlan As MASTransferListLayoutPlan = Nothing
        Private _availablePanelRect As SKRect = SKRect.Empty
        Private _selectedPanelRect As SKRect = SKRect.Empty
        Private _availableSearchRect As SKRect = SKRect.Empty
        Private _selectedSearchRect As SKRect = SKRect.Empty
        Private _addOneRect As SKRect = SKRect.Empty
        Private _addAllRect As SKRect = SKRect.Empty
        Private _removeOneRect As SKRect = SKRect.Empty
        Private _removeAllRect As SKRect = SKRect.Empty
        Private _lastDpi As Single = 1.0F

#End Region

#Region "Constructor / Factory"

        Public Sub New()
            MyBase.New()
        End Sub

        Public Shared Function Create(Optional availableItems As IEnumerable(Of String) = Nothing,
                                      Optional selectedItems As IEnumerable(Of String) = Nothing) As MASTransferList
            Dim control As New MASTransferList()
            control.SetItemsInternal(availableItems, selectedItems, False)
            Return control
        End Function

#End Region

#Region "Public API"

        Public Event SelectionChanged As EventHandler
        Public Event ItemsTransferred As EventHandler

        Public Property Title As String
            Get
                Return _title
            End Get
            Set(value As String)
                Dim normalized As String = MASTransferListPolicy.NormalizeTitle(value, TransferListTokens.DefaultTitle)
                If String.Equals(_title, normalized, StringComparison.Ordinal) Then Return
                _title = normalized
                RequestSizeLayoutRefreshForSizeAffectingChange("MASTransferList.Title")
                InvalidateVisual()
            End Set
        End Property

        Public Property AvailableTitle As String
            Get
                Return _availableTitle
            End Get
            Set(value As String)
                Dim normalized As String = MASTransferListPolicy.NormalizeTitle(value, TransferListTokens.DefaultAvailableTitle)
                If String.Equals(_availableTitle, normalized, StringComparison.Ordinal) Then Return
                _availableTitle = normalized
                RequestSizeLayoutRefreshForSizeAffectingChange("MASTransferList.AvailableTitle")
                InvalidateVisual()
            End Set
        End Property

        Public Property SelectedTitle As String
            Get
                Return _selectedTitle
            End Get
            Set(value As String)
                Dim normalized As String = MASTransferListPolicy.NormalizeTitle(value, TransferListTokens.DefaultSelectedTitle)
                If String.Equals(_selectedTitle, normalized, StringComparison.Ordinal) Then Return
                _selectedTitle = normalized
                RequestSizeLayoutRefreshForSizeAffectingChange("MASTransferList.SelectedTitle")
                InvalidateVisual()
            End Set
        End Property

        Public Property AvailableFilterText As String
            Get
                Return _availableFilterText
            End Get
            Set(value As String)
                Dim normalized As String = MASTransferListPolicy.NormalizeItem(value)
                If String.Equals(_availableFilterText, normalized, StringComparison.Ordinal) Then Return
                _availableFilterText = normalized
                _availableTopIndex = 0
                InvalidateVisual()
            End Set
        End Property

        Public Property SelectedFilterText As String
            Get
                Return _selectedFilterText
            End Get
            Set(value As String)
                Dim normalized As String = MASTransferListPolicy.NormalizeItem(value)
                If String.Equals(_selectedFilterText, normalized, StringComparison.Ordinal) Then Return
                _selectedFilterText = normalized
                _selectedTopIndex = 0
                InvalidateVisual()
            End Set
        End Property

        Public ReadOnly Property AvailableItems As IReadOnlyList(Of String)
            Get
                Return New ReadOnlyCollection(Of String)(_available)
            End Get
        End Property

        Public ReadOnly Property SelectedItems As IReadOnlyList(Of String)
            Get
                Return New ReadOnlyCollection(Of String)(_selected)
            End Get
        End Property

        Public ReadOnly Property AvailableCount As Integer
            Get
                Return _available.Count
            End Get
        End Property

        Public ReadOnly Property SelectedCount As Integer
            Get
                Return _selected.Count
            End Get
        End Property

        Public Property AvailableSelectedIndex As Integer
            Get
                Return _availableSelectedIndex
            End Get
            Set(value As Integer)
                SetAvailableSelection(value, True)
            End Set
        End Property

        Public Property SelectedSelectedIndex As Integer
            Get
                Return _selectedSelectedIndex
            End Get
            Set(value As Integer)
                SetSelectedSelection(value, True)
            End Set
        End Property

        Public Function SetAvailableItems(items As IEnumerable(Of String)) As MASTransferList
            _available.Clear()
            _available.AddRange(MASTransferListPolicy.NormalizeItems(items))
            NormalizeSelectionAfterCollectionChange()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTransferList.SetAvailableItems")
            InvalidateVisual()
            RaiseSelectionChangedSafe()
            Return Me
        End Function

        Public Function SetSelectedItems(items As IEnumerable(Of String)) As MASTransferList
            _selected.Clear()
            _selected.AddRange(MASTransferListPolicy.NormalizeItems(items))
            NormalizeSelectionAfterCollectionChange()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTransferList.SetSelectedItems")
            InvalidateVisual()
            RaiseSelectionChangedSafe()
            Return Me
        End Function

        Public Function SetItems(availableItems As IEnumerable(Of String),
                                 selectedItems As IEnumerable(Of String)) As MASTransferList
            SetItemsInternal(availableItems, selectedItems, True)
            Return Me
        End Function

        Public Function AddAvailableItem(text As String) As MASTransferList
            Dim normalized As String = MASTransferListPolicy.NormalizeItem(text)
            If normalized.Length = 0 OrElse _available.Count >= TransferListTokens.MaxStoredItems Then Return Me
            _available.Add(normalized)
            NormalizeSelectionAfterCollectionChange()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTransferList.AddAvailableItem")
            InvalidateVisual()
            Return Me
        End Function

        Public Function AddAvailableItems(items As IEnumerable(Of String)) As MASTransferList
            If items Is Nothing Then Return Me
            For Each item As String In items
                AddAvailableItem(item)
            Next
            Return Me
        End Function

        Public Function MoveSelectedToTarget() As MASTransferList
            MoveOne(_available, _selected, _availableSelectedIndex, True)
            Return Me
        End Function

        Public Function MoveTargetToAvailable() As MASTransferList
            MoveOne(_selected, _available, _selectedSelectedIndex, False)
            Return Me
        End Function

        Public Function MoveAllToTarget() As MASTransferList
            MoveAll(_available, _selected, True)
            Return Me
        End Function

        Public Function MoveAllToAvailable() As MASTransferList
            MoveAll(_selected, _available, False)
            Return Me
        End Function

        Public Function ClearItems() As MASTransferList
            If _available.Count = 0 AndAlso _selected.Count = 0 Then Return Me
            _available.Clear()
            _selected.Clear()
            NormalizeSelectionAfterCollectionChange()
            RequestSizeLayoutRefreshForSizeAffectingChange("MASTransferList.ClearItems")
            InvalidateVisual()
            RaiseSelectionChangedSafe()
            RaiseItemsTransferredSafe()
            Return Me
        End Function

        Public Function WithTitle(value As String) As MASTransferList
            Title = value
            Return Me
        End Function

        Public Function WithPanelTitles(availableTitle As String,
                                        selectedTitle As String) As MASTransferList
            Me.AvailableTitle = availableTitle
            Me.SelectedTitle = selectedTitle
            Return Me
        End Function

        Public Function WithAvailableItems(items As IEnumerable(Of String)) As MASTransferList
            Return SetAvailableItems(items)
        End Function

        Public Function WithSelectedItems(items As IEnumerable(Of String)) As MASTransferList
            Return SetSelectedItems(items)
        End Function

        Public Function WithAvailableFilter(value As String) As MASTransferList
            AvailableFilterText = value
            Return Me
        End Function

        Public Function WithSelectedFilter(value As String) As MASTransferList
            SelectedFilterText = value
            Return Me
        End Function

        Public Function ClearFilters() As MASTransferList
            AvailableFilterText = String.Empty
            SelectedFilterText = String.Empty
            Return Me
        End Function

        Public Function SelectAvailableIndex(index As Integer) As MASTransferList
            AvailableSelectedIndex = index
            Return Me
        End Function

        Public Function SelectSelectedIndex(index As Integer) As MASTransferList
            SelectedSelectedIndex = index
            Return Me
        End Function

        Public Shadows Function WithSize(sizeIntent As MASSize) As MASTransferList
            MyBase.SetSize(sizeIntent)
            Return Me
        End Function

#End Region

    End Class

End Namespace
