package zntg

import "time"

// Animation is a struct that keeps track of time for animation.
type Animation struct {
	// The interval of the animation
	Interval time.Duration

	active     bool
	start      time.Time
	lastUpdate time.Duration
}

// NewAnimation creates an Animation given the specified interval. The animation is immediately started.
func NewAnimation(interval time.Duration) Animation {
	return Animation{
		Interval: interval,

		active: true,
		start:  time.Now(),
	}
}

// NewAnimationPtr creates an Animation given the specified interval and returns a pointer to it. The animation is immediately started.
func NewAnimationPtr(interval time.Duration) *Animation {
	ani := NewAnimation(interval)
	return &ani
}

// Animate checks if enough time as elapsed to animate a single interval and advances the single interval.
func (a *Animation) Animate() bool {
	since := time.Since(a.start)
	if !a.active || since < a.lastUpdate+a.Interval {
		return false
	}
	a.lastUpdate += a.Interval
	return true
}

// AnimateDelta checks how many natural intervals have elapsed and advances that many intervals. Returns the total of time that has been advanced and the number of intervals.
func (a *Animation) AnimateDelta() (time.Duration, int) {
	if !a.active {
		return 0, 0
	}
	since := time.Since(a.start)
	n := (since - a.lastUpdate) / a.Interval
	delta := n * a.Interval
	a.lastUpdate += delta
	return delta, int(n)
}

// AnimateFn calls fn for every interval and advances that interval for every interval that has elapsed until it caught up again.
func (a *Animation) AnimateFn(fn func()) {
	if !a.active {
		return
	}
	since := time.Since(a.start)
	for a.active && since > a.lastUpdate+a.Interval {
		fn()
		a.lastUpdate += a.Interval
	}
}

// Pause pauses the animation causing the Animate{,Delta,Fn} methods to do nothing.
func (a *Animation) Pause() {
	a.active = false
}

// Start starts the animation (when paused or not started yet).
func (a *Animation) Start() {
	if a.active {
		return
	}
	a.active = true
	a.start = time.Now()
	a.lastUpdate = 0
}