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

    Partial Public NotInheritable Class MASTransferList

#Region "Input"

        Protected Overrides Sub OnMouseMove(ctx As MASThemeContext, ptPx As SKPoint)
            Dim oldAction As TransferAction = _hoverAction
            Dim oldRow As RowHit = _hoverRow
            _hoverAction = HitTestAction(ptPx)
            _hoverRow = HitTestRow(ptPx)
            If oldAction = _hoverAction AndAlso oldRow.LeftList = _hoverRow.LeftList AndAlso oldRow.Index = _hoverRow.Index Then Return
            InvalidateVisual()
        End Sub

        Protected Overrides Function OnMouseDown(ctx As MASThemeContext,
                                                 ptPx As SKPoint,
                                                 button As Integer) As Boolean
            If button <> CInt(MouseButtons.Left) Then Return False
            If _availablePanelRect.Contains(ptPx.X, ptPx.Y) Then _activeAvailablePanel = True
            If _selectedPanelRect.Contains(ptPx.X, ptPx.Y) Then _activeAvailablePanel = False
            _pressedAction = HitTestAction(ptPx)
            _pressedRow = HitTestRow(ptPx)
            If _pressedAction <> TransferAction.None OrElse _pressedRow.Index >= 0 Then
                InvalidateVisual()
                Return True
            End If
            Return _availablePanelRect.Contains(ptPx.X, ptPx.Y) OrElse _selectedPanelRect.Contains(ptPx.X, ptPx.Y)
        End Function

        Protected Overrides Function OnMouseUp(ctx As MASThemeContext,
                                               ptPx As SKPoint,
                                               button As Integer) As Boolean
            If button <> CInt(MouseButtons.Left) Then Return False
            Dim action As TransferAction = _pressedAction
            Dim row As RowHit = _pressedRow
            _pressedAction = TransferAction.None
            _pressedRow = New RowHit(False, -1)

            If action <> TransferAction.None AndAlso action = HitTestAction(ptPx) Then
                ExecuteAction(action)
                InvalidateVisual()
                Return True
            End If

            Dim upRow As RowHit = HitTestRow(ptPx)
            If row.Index >= 0 AndAlso row.LeftList = upRow.LeftList AndAlso row.Index = upRow.Index Then
                If row.LeftList Then
                    SetAvailableSelection(row.Index, True)
                Else
                    SetSelectedSelection(row.Index, True)
                End If
                Return True
            End If

            If action <> TransferAction.None OrElse row.Index >= 0 Then
                InvalidateVisual()
                Return True
            End If
            Return False
        End Function

        Protected Overrides Sub OnMouseLeave(ctx As MASThemeContext)
            ResetPointerState()
        End Sub

        Protected Overrides Sub OnMouseCancel(ctx As MASThemeContext)
            ResetPointerState()
        End Sub

        Protected Overrides Sub OnHostLostFocus(ctx As MASThemeContext)
            _pressedAction = TransferAction.None
            _pressedRow = New RowHit(False, -1)
            InvalidateVisual()
        End Sub

        Protected Overrides Function OnKeyDown(ctx As MASThemeContext,
                                               keyCode As Keys) As Boolean
            If Not Enabled Then Return False
            If keyCode = Keys.Back Then
                If TrimActiveFilterBack() Then Return True
            ElseIf keyCode = Keys.Delete OrElse keyCode = Keys.Escape Then
                If ClearActiveFilter() Then Return True
            End If
            Select Case keyCode
                Case Keys.Up
                    If _selectedSelectedIndex >= 0 AndAlso _availableSelectedIndex < 0 Then
                        SetSelectedSelection(Math.Max(0, _selectedSelectedIndex - 1), True)
                    Else
                        SetAvailableSelection(Math.Max(0, _availableSelectedIndex - 1), True)
                    End If
                    Return True
                Case Keys.Down
                    If _selectedSelectedIndex >= 0 AndAlso _availableSelectedIndex < 0 Then
                        SetSelectedSelection(Math.Min(_selected.Count - 1, Math.Max(0, _selectedSelectedIndex + 1)), True)
                    Else
                        SetAvailableSelection(Math.Min(_available.Count - 1, Math.Max(0, _availableSelectedIndex + 1)), True)
                    End If
                    Return True
                Case Keys.Right
                    If MASTransferListPolicy.ResolveRightToLeft() Then
                        MoveTargetToAvailable()
                    Else
                        MoveSelectedToTarget()
                    End If
                    Return True
                Case Keys.Left
                    If MASTransferListPolicy.ResolveRightToLeft() Then
                        MoveSelectedToTarget()
                    Else
                        MoveTargetToAvailable()
                    End If
                    Return True
                Case Keys.Enter, Keys.Space
                    If _availableSelectedIndex >= 0 Then
                        MoveSelectedToTarget()
                    ElseIf _selectedSelectedIndex >= 0 Then
                        MoveTargetToAvailable()
                    End If
                    Return True
                Case Keys.Home
                    If _selectedSelectedIndex >= 0 AndAlso _availableSelectedIndex < 0 Then
                        SetSelectedSelection(0, True)
                    Else
                        SetAvailableSelection(0, True)
                    End If
                    Return True
                Case Keys.End
                    If _selectedSelectedIndex >= 0 AndAlso _availableSelectedIndex < 0 Then
                        SetSelectedSelection(_selected.Count - 1, True)
                    Else
                        SetAvailableSelection(_available.Count - 1, True)
                    End If
                    Return True
            End Select
            Return False
        End Function

        Protected Overrides Function OnTextInput(ctx As MASThemeContext, text As String) As Boolean
            If String.IsNullOrEmpty(text) Then Return False
            Dim appended As Boolean = False
            For Each ch As Char In text
                If Not Char.IsControl(ch) Then
                    If _activeAvailablePanel Then
                        _availableFilterText &= ch
                    Else
                        _selectedFilterText &= ch
                    End If
                    appended = True
                End If
            Next
            If appended Then
                _availableFilterText = MASTransferListPolicy.NormalizeItem(_availableFilterText)
                _selectedFilterText = MASTransferListPolicy.NormalizeItem(_selectedFilterText)
                If _activeAvailablePanel Then _availableTopIndex = 0 Else _selectedTopIndex = 0
                InvalidateVisual()
            End If
            Return appended
        End Function

        Protected Overrides Function WantsKeyboardFocus() As Boolean
            Return MASCompositeContentFocusPolicy.CanReceiveKeyboardFocus(_contract, Visible, Enabled)
        End Function

        Protected Overrides Function WantsPointerFocus() As Boolean
            Return MASCompositeContentFocusPolicy.AllowsPointerFocusForCompositeContent(_contract, Visible, Enabled)
        End Function

#End Region

    End Class

End Namespace
