Option Strict On
Option Explicit On

Imports System

Namespace Nexamas.UI.Motion

    ''' <summary>
    ''' Deterministic timeline for a single transition. It advances only when an official caller supplies elapsed time; it never creates timers.
    ''' </summary>
    Friend NotInheritable Class MASMotionTimeline

        Private ReadOnly _plan As MASMotionTransitionPlan
        Private _elapsedMs As Integer
        Private _state As MASMotionPlaybackState

        Friend Sub New(plan As MASMotionTransitionPlan)
            If plan Is Nothing Then Throw New ArgumentNullException(NameOf(plan))
            _plan = plan
            _elapsedMs = 0
            _state = If(plan.ShouldAnimate, MASMotionPlaybackState.Running, MASMotionPlaybackState.Completed)
        End Sub

        Friend ReadOnly Property Plan As MASMotionTransitionPlan
            Get
                Return _plan
            End Get
        End Property

        Friend ReadOnly Property State As MASMotionPlaybackState
            Get
                Return _state
            End Get
        End Property

        Friend Function CurrentFrame() As MASMotionFrame
            Return CreateFrame(_elapsedMs, _state)
        End Function

        Friend Function Advance(deltaMs As Integer) As MASMotionFrame
            If _state = MASMotionPlaybackState.Cancelled OrElse _state = MASMotionPlaybackState.Completed Then
                Return CurrentFrame()
            End If

            _elapsedMs += MASMotionClock.NormalizeDeltaMs(deltaMs)
            If _elapsedMs >= _plan.EffectiveDurationMs Then
                _elapsedMs = _plan.EffectiveDurationMs
                _state = MASMotionPlaybackState.Completed
            Else
                _state = MASMotionPlaybackState.Running
            End If

            Return CreateFrame(_elapsedMs, _state)
        End Function

        Friend Function Complete() As MASMotionFrame
            _elapsedMs = _plan.EffectiveDurationMs
            _state = MASMotionPlaybackState.Completed
            Return CurrentFrame()
        End Function

        Friend Function Cancel() As MASMotionFrame
            _state = MASMotionPlaybackState.Cancelled
            Return CurrentFrame()
        End Function

        Private Function CreateFrame(elapsedMs As Integer, state As MASMotionPlaybackState) As MASMotionFrame
            Dim duration As Integer = Math.Max(0, _plan.EffectiveDurationMs)
            Dim linear As Single
            If duration <= 0 Then
                linear = 1.0F
            Else
                linear = MASMotionEasingEvaluator.Clamp01(CSng(elapsedMs) / CSng(duration))
            End If

            Dim eased As Single = MASMotionEasingEvaluator.Evaluate(_plan.Token.Easing, linear)
            Dim value As Single = _plan.FromValue + ((_plan.ToValue - _plan.FromValue) * eased)
            Return New MASMotionFrame(elapsedMs, duration, linear, eased, value, state)
        End Function

    End Class

End Namespace
