Option Strict On
Option Explicit On

Imports System.Windows.Forms
Imports SkiaSharp

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Shared internal resize interaction policy for Nexamas UI surfaces that expose column or pane resize affordances.
    ''' It keeps drag math deterministic, DPI-safe, bounded, and lightweight so controls do not duplicate fragile pointer logic.
    ''' </summary>
    Friend NotInheritable Class MASResizeInteractionPolicy
        Private Sub New()
        End Sub

        Friend Const DefaultMinimumDip As Single = 24.0F
        Friend Const DefaultMaximumDip As Single = 32768.0F
        Friend Const DefaultGripHalfWidthDip As Single = 4.0F
        Friend Const WidthChangeEpsilonDip As Single = 0.05F
        Friend Const RatioChangeEpsilon As Single = 0.0005F

        Private Const InvalidMagnitudeLimit As Single = 100000000.0F
        Private Shared _horizontalResizeCursorActive As Boolean

        Friend Shared Function SafeDpi(dpi As Single) As Single
            If IsInvalidSingle(dpi) OrElse dpi <= 0.0F Then Return 1.0F
            Return dpi
        End Function

        Friend Shared Function NormalizeMinimumDip(value As Single, Optional fallbackDip As Single = DefaultMinimumDip) As Single
            Dim fallback As Single = fallbackDip
            If IsInvalidSingle(fallback) OrElse fallback <= 0.0F Then
                fallback = DefaultMinimumDip
            End If

            If IsInvalidSingle(value) OrElse value <= 0.0F Then Return fallback
            If value < DefaultMinimumDip Then Return DefaultMinimumDip
            Return value
        End Function

        Friend Shared Function NormalizeMaximumDip(value As Single) As Single
            If IsInvalidSingle(value) OrElse value <= 0.0F Then Return DefaultMaximumDip
            If value < DefaultMinimumDip Then Return DefaultMinimumDip
            If value > DefaultMaximumDip Then Return DefaultMaximumDip
            Return value
        End Function

        Friend Shared Function NormalizeWidthDip(value As Single, minDip As Single, Optional fallbackDip As Single = 120.0F) As Single
            Dim safeMin As Single = NormalizeMinimumDip(minDip)
            Dim v As Single = value
            If IsInvalidSingle(v) OrElse v <= 0.0F Then v = fallbackDip
            If IsInvalidSingle(v) OrElse v <= 0.0F Then v = safeMin
            If v < safeMin Then Return safeMin
            If v > DefaultMaximumDip Then Return DefaultMaximumDip
            Return v
        End Function

        Friend Shared Function ClampDip(value As Single, minDip As Single, maxDip As Single) As Single
            Dim minValue As Single = NormalizeMinimumDip(minDip)
            Dim maxValue As Single = NormalizeMaximumDip(maxDip)
            If maxValue < minValue Then maxValue = minValue

            Dim v As Single = value
            If IsInvalidSingle(v) Then v = minValue
            If v < minValue Then v = minValue
            If v > maxValue Then v = maxValue
            Return v
        End Function

        Friend Shared Function ComputeHorizontalDip(startValueDip As Single,
                                                    startPointerXPx As Single,
                                                    currentPointerXPx As Single,
                                                    dpi As Single,
                                                    minValueDip As Single,
                                                    maxValueDip As Single,
                                                    Optional directionSign As Single = 1.0F) As Single
            Dim dpiScale As Single = MASResizeInteractionPolicy.SafeDpi(dpi)
            Dim sign As Single = 1.0F
            If directionSign < 0.0F Then sign = -1.0F

            Dim deltaDip As Single = ((currentPointerXPx - startPointerXPx) / dpiScale) * sign
            Return ClampDip(startValueDip + deltaDip, minValueDip, maxValueDip)
        End Function

        Friend Shared Function HasMeaningfulWidthChange(currentDip As Single, nextDip As Single) As Boolean
            Return AbsoluteDifference(currentDip, nextDip) >= WidthChangeEpsilonDip
        End Function

        Friend Shared Function HasMeaningfulRatioChange(currentRatio As Single, nextRatio As Single) As Boolean
            Return AbsoluteDifference(currentRatio, nextRatio) >= RatioChangeEpsilon
        End Function

        Friend Shared Function AbsoluteDifference(first As Single, second As Single) As Single
            Dim delta As Single = first - second
            If delta < 0.0F Then Return -delta
            Return delta
        End Function

        Friend Shared Function ResolveVerticalGripRect(edgeXPx As Single,
                                                       topPx As Single,
                                                       bottomPx As Single,
                                                       dpi As Single,
                                                       Optional gripHalfWidthDip As Single = DefaultGripHalfWidthDip) As SKRect
            Dim dpiScale As Single = MASResizeInteractionPolicy.SafeDpi(dpi)
            Dim safeGripHalfDip As Single = gripHalfWidthDip
            If IsInvalidSingle(safeGripHalfDip) OrElse safeGripHalfDip < 0.0F Then
                safeGripHalfDip = 0.0F
            End If

            Dim halfWidthPx As Single = safeGripHalfDip * dpiScale
            If halfWidthPx < 2.0F Then halfWidthPx = 2.0F
            Return New SKRect(edgeXPx - halfWidthPx, topPx, edgeXPx + halfWidthPx, bottomPx)
        End Function

        Friend Shared Function HitTestVerticalGrip(edgeXPx As Single,
                                                   topPx As Single,
                                                   bottomPx As Single,
                                                   dpi As Single,
                                                   ptPx As SKPoint,
                                                   Optional gripHalfWidthDip As Single = DefaultGripHalfWidthDip) As Boolean
            If bottomPx <= topPx Then Return False
            Dim rect As SKRect = ResolveVerticalGripRect(edgeXPx, topPx, bottomPx, dpi, gripHalfWidthDip)
            Return rect.Contains(ptPx.X, ptPx.Y)
        End Function

        Friend Shared ReadOnly Property IsHorizontalResizeCursorActive As Boolean
            Get
                Return _horizontalResizeCursorActive
            End Get
        End Property

        Friend Shared Sub ApplyHorizontalResizeCursor(active As Boolean)
            ' Cursor.Current is message-scoped in WinForms and can be overwritten by the native
            ' SKGLControl cursor between mouse messages. Controls therefore publish the resize
            ' intent here, and the root host synchronizes the native Cursor property after routing.
            Dim wasActive As Boolean = _horizontalResizeCursorActive
            _horizontalResizeCursorActive = active

            If active OrElse wasActive Then
                Cursor.Current = If(active, Cursors.SizeWE, Cursors.Default)
            End If
        End Sub

        Friend Shared Sub SyncHorizontalResizeCursor(hostControl As Control)
            If hostControl Is Nothing Then Return
            If hostControl.IsDisposed Then Return

            Dim desired As Cursor = If(_horizontalResizeCursorActive, Cursors.SizeWE, Cursors.Default)
            If _horizontalResizeCursorActive Then
                If Not Object.ReferenceEquals(hostControl.Cursor, desired) Then hostControl.Cursor = desired
                Cursor.Current = desired
                Return
            End If

            If Object.ReferenceEquals(hostControl.Cursor, Cursors.SizeWE) Then
                hostControl.Cursor = Cursors.Default
                Cursor.Current = Cursors.Default
            End If
        End Sub

        Private Shared Function IsInvalidSingle(value As Single) As Boolean
            If value <> value Then Return True
            If value > InvalidMagnitudeLimit OrElse value < -InvalidMagnitudeLimit Then Return True
            Return False
        End Function
    End Class

    ''' <summary>
    ''' Small reusable horizontal drag-resize state machine. It owns only pointer math and stability thresholds;
    ''' each control remains responsible for model updates, rendering, and public events.
    ''' </summary>
    Friend NotInheritable Class MASDragResizeController
        Private _isDragging As Boolean
        Private _targetIndex As Integer = -1
        Private _startPointerXPx As Single
        Private _startValueDip As Single
        Private _minValueDip As Single
        Private _maxValueDip As Single = MASResizeInteractionPolicy.DefaultMaximumDip
        Private _lastValueDip As Single
        Private _directionSign As Single = 1.0F

        Friend ReadOnly Property IsDragging As Boolean
            Get
                Return _isDragging
            End Get
        End Property

        Friend ReadOnly Property TargetIndex As Integer
            Get
                Return _targetIndex
            End Get
        End Property

        Friend ReadOnly Property LastValueDip As Single
            Get
                Return _lastValueDip
            End Get
        End Property

        Friend Function Begin(targetIndex As Integer,
                              startPointerXPx As Single,
                              startValueDip As Single,
                              minValueDip As Single,
                              Optional maxValueDip As Single = 32768.0F,
                              Optional directionSign As Single = 1.0F) As Boolean
            If targetIndex < 0 Then Return False

            _targetIndex = targetIndex
            _startPointerXPx = startPointerXPx
            _minValueDip = MASResizeInteractionPolicy.NormalizeMinimumDip(minValueDip)
            _maxValueDip = MASResizeInteractionPolicy.NormalizeMaximumDip(maxValueDip)
            _startValueDip = MASResizeInteractionPolicy.ClampDip(startValueDip, _minValueDip, _maxValueDip)
            _lastValueDip = _startValueDip
            _directionSign = 1.0F
            If directionSign < 0.0F Then _directionSign = -1.0F
            _isDragging = True
            Return True
        End Function

        Friend Function Update(currentPointerXPx As Single,
                               dpi As Single,
                               ByRef nextValueDip As Single) As Boolean
            If Not _isDragging Then Return False

            Dim computed As Single = MASResizeInteractionPolicy.ComputeHorizontalDip(
                _startValueDip,
                _startPointerXPx,
                currentPointerXPx,
                dpi,
                _minValueDip,
                _maxValueDip,
                _directionSign)

            If Not MASResizeInteractionPolicy.HasMeaningfulWidthChange(_lastValueDip, computed) Then Return False

            _lastValueDip = computed
            nextValueDip = computed
            Return True
        End Function

        Friend Function EndDrag() As Boolean
            If Not _isDragging Then Return False
            _isDragging = False
            _targetIndex = -1
            Return True
        End Function

        Friend Sub Cancel()
            _isDragging = False
            _targetIndex = -1
            _startPointerXPx = 0.0F
            _startValueDip = 0.0F
            _minValueDip = MASResizeInteractionPolicy.DefaultMinimumDip
            _maxValueDip = MASResizeInteractionPolicy.DefaultMaximumDip
            _lastValueDip = 0.0F
            _directionSign = 1.0F
        End Sub
    End Class

End Namespace
