Option Strict On
Option Explicit On

Imports System
Imports SkiaSharp
Imports Nexamas.UI.Motion

Namespace Nexamas.UI.Controls

    Friend NotInheritable Class MASTooltipTimeline

        Friend Const HoverDelayMs As Integer = 300
        Friend Const HideGraceMs As Integer = 110
        Friend Const StickyMoveToleranceClientPx As Single = 10.0F
        Friend Const HoverRectInflatePx As Single = 2.0F
        Friend Const AnchorSnapEpsilonPx As Single = 0.04F

        Friend Function Advance(s As MASTooltipState, deltaMs As Integer) As Boolean
            If s Is Nothing Then Return False

            Dim changed As Boolean = False
            Dim nowUtc As DateTime = Nexamas.UI.Timing.MASVisualClock.UtcNow()
            Dim safeDeltaMs As Integer = MASMotionSystem.NormalizeFrameDeltaMs(deltaMs)

            If s.HasHoverRect AndAlso Not IsPointerInsideHoverRect(s, s.AnchorClientPx) Then
                s.PendingShow = False

                If s.IsOpen OrElse s.TargetOpacity > 0.0F OrElse s.Opacity > 0.01F Then
                    s.PendingHide = False
                    s.IsOpen = False
                    s.TargetOpacity = 0.0F
                    changed = True
                End If
            End If

            If s.PendingShow Then
                Dim elapsedShowMs As Double = (nowUtc - s.PendingShowStartedUtc).TotalMilliseconds

                If elapsedShowMs >= MASTooltipVisualPolicy.NormalizeHoverDelayMs(HoverDelayMs) Then
                    s.PendingShow = False
                    s.PendingHide = False
                    s.IsOpen = True
                    s.TargetOpacity = 1.0F
                    s.VisibleText = s.PendingShowText
                    changed = True
                End If
            End If

            If s.PendingHide Then
                Dim elapsedHideMs As Double = (nowUtc - s.PendingHideStartedUtc).TotalMilliseconds

                If elapsedHideMs >= MASTooltipVisualPolicy.NormalizeHideGraceMs(HideGraceMs) Then
                    s.PendingHide = False
                    s.IsOpen = False
                    s.TargetOpacity = 0.0F
                    changed = True
                End If
            End If

            If s.HasAnchor Then
                Dim dx As Single = s.AnchorClientPx.X - s.SmoothAnchorClientPx.X
                Dim dy As Single = s.AnchorClientPx.Y - s.SmoothAnchorClientPx.Y

                If Math.Abs(dx) > AnchorSnapEpsilonPx OrElse Math.Abs(dy) > AnchorSnapEpsilonPx Then
                    Dim anchorProgress As Single = MASMotionSystem.ResolveFrameProgress(MASMotionTokenId.ValueChange, safeDeltaMs)
                    s.SmoothAnchorClientPx.X += dx * anchorProgress
                    s.SmoothAnchorClientPx.Y += dy * anchorProgress
                    changed = True
                Else
                    If s.SmoothAnchorClientPx.X <> s.AnchorClientPx.X OrElse s.SmoothAnchorClientPx.Y <> s.AnchorClientPx.Y Then
                        s.SmoothAnchorClientPx = s.AnchorClientPx
                        changed = True
                    End If
                End If
            End If

            Dim da As Single = s.TargetOpacity - s.Opacity

            If Math.Abs(da) > 0.01F Then
                Dim tokenId As MASMotionTokenId = If(s.TargetOpacity >= s.Opacity, MASMotionTokenId.HoverEnter, MASMotionTokenId.HoverExit)
                Dim opacityProgress As Single = MASMotionSystem.ResolveFrameProgress(tokenId, safeDeltaMs)
                s.Opacity += da * opacityProgress
                changed = True
            Else
                If s.Opacity <> s.TargetOpacity Then
                    s.Opacity = s.TargetOpacity
                    changed = True
                End If
            End If

            If s.Opacity <= 0.001F AndAlso s.TargetOpacity <= 0.0F Then
                If s.Opacity <> 0.0F Then
                    s.Opacity = 0.0F
                    changed = True
                End If

                If Not String.IsNullOrEmpty(s.VisibleText) Then
                    s.VisibleText = String.Empty
                    changed = True
                End If
            End If

            Return changed
        End Function


        Friend Shared Function IsPointerInsideHoverRect(s As MASTooltipState, pt As SKPoint) As Boolean
            If s Is Nothing Then Return False
            If Not s.HasHoverRect Then Return True
            If s.HoverRectClientPx.IsEmpty Then Return False

            Dim r As New SKRect(
                s.HoverRectClientPx.Left - MASTooltipVisualPolicy.ResolveHoverRectInflatePx(HoverRectInflatePx),
                s.HoverRectClientPx.Top - MASTooltipVisualPolicy.ResolveHoverRectInflatePx(HoverRectInflatePx),
                s.HoverRectClientPx.Right + MASTooltipVisualPolicy.ResolveHoverRectInflatePx(HoverRectInflatePx),
                s.HoverRectClientPx.Bottom + MASTooltipVisualPolicy.ResolveHoverRectInflatePx(HoverRectInflatePx)
            )

            Return r.Contains(pt)
        End Function

        Friend Shared Function Distance(a As SKPoint, b As SKPoint) As Single
            Dim dx As Single = a.X - b.X
            Dim dy As Single = a.Y - b.Y
            Return CSng(Math.Sqrt((dx * dx) + (dy * dy)))
        End Function
    End Class

End Namespace