package hh

import "time"

type limiter struct {
	count int64
	limit int64
	start time.Time
	delay float64
}

// NewRateLimiter returns a new limiter configured to restrict a process to the limit per second.
// limit is the maximum amount that can be used per second.  The limit should be > 0.  A limit
// <= 0, will not limit the processes.
func NewRateLimiter(limit int64) *limiter {
	return &limiter{
		start: time.Now(),
		limit: limit,
		delay: 0.5,
	}
}

// Update updates the amount used
func (t *limiter) Update(count int) {
	t.count += int64(count)
}

// Delay returns the amount of time, up to 1 second, that caller should wait
// to maintain the configured rate
func (t *limiter) Delay() time.Duration {
	if t.limit > 0 {
		delta := time.Now().Sub(t.start).Seconds()
		rate := int64(float64(t.count) / delta)

		// Determine how far off from the max rate we are
		delayAdj := float64(t.limit-rate) / float64(t.limit)

		// Don't adjust by more than 1 second at a time
		delayAdj = t.clamp(delayAdj, -1, 1)

		t.delay -= delayAdj
		if t.delay < 0 {
			t.delay = 0
		}

		return time.Duration(t.delay) * time.Second
	}
	return time.Duration(0)
}

func (t *limiter) clamp(value, min, max float64) float64 {
	if value < min {
		return min
	}

	if value > max {
		return max
	}
	return value
}
