// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package vnet

import (
	
	
	

	
)

const (
	// Bit is a single bit
	Bit = 1
	// KBit is a kilobit
	KBit = 1000 * Bit
	// MBit is a Megabit
	MBit = 1000 * KBit
)

// TokenBucketFilter implements a token bucket rate limit algorithm.
type TokenBucketFilter struct {
	NIC
	currentTokensInBucket float64
	c                     chan Chunk
	queue                 *chunkQueue
	queueSize             int // in bytes

	mutex             sync.Mutex
	rate              int
	maxBurst          int
	minRefillDuration time.Duration

	wg   sync.WaitGroup
	done chan struct{}

	log logging.LeveledLogger
}

// TBFOption is the option type to configure a TokenBucketFilter
type TBFOption func(*TokenBucketFilter) TBFOption

// TBFQueueSizeInBytes sets the max number of bytes waiting in the queue. Can
// only be set in constructor before using the TBF.
func ( int) TBFOption {
	return func( *TokenBucketFilter) TBFOption {
		 := .queueSize
		.queueSize = 
		return ()
	}
}

// TBFRate sets the bit rate of a TokenBucketFilter
func ( int) TBFOption {
	return func( *TokenBucketFilter) TBFOption {
		.mutex.Lock()
		defer .mutex.Unlock()
		 := .rate
		.rate = 
		return ()
	}
}

// TBFMaxBurst sets the bucket size of the token bucket filter. This is the
// maximum size that can instantly leave the filter, if the bucket is full.
func ( int) TBFOption {
	return func( *TokenBucketFilter) TBFOption {
		.mutex.Lock()
		defer .mutex.Unlock()
		 := .maxBurst
		.maxBurst = 
		return ()
	}
}

// Set updates a setting on the token bucket filter
func ( *TokenBucketFilter) ( ...TBFOption) ( TBFOption) {
	for ,  := range  {
		 = ()
	}
	return 
}

// NewTokenBucketFilter creates and starts a new TokenBucketFilter
func ( NIC,  ...TBFOption) (*TokenBucketFilter, error) {
	 := &TokenBucketFilter{
		NIC:                   ,
		currentTokensInBucket: 0,
		c:                     make(chan Chunk),
		queue:                 nil,
		queueSize:             50000,
		mutex:                 sync.Mutex{},
		rate:                  1 * MBit,
		maxBurst:              8 * KBit,
		minRefillDuration:     100 * time.Millisecond,
		wg:                    sync.WaitGroup{},
		done:                  make(chan struct{}),
		log:                   logging.NewDefaultLoggerFactory().NewLogger("tbf"),
	}
	.Set(...)
	.queue = newChunkQueue(0, .queueSize)
	.wg.Add(1)
	go .run()
	return , nil
}

func ( *TokenBucketFilter) ( Chunk) {
	.c <- 
}

func ( *TokenBucketFilter) () {
	defer .wg.Done()

	.refillTokens(.minRefillDuration)
	 := time.Now()

	for {
		select {
		case <-.done:
			.drainQueue()
			return
		case  := <-.c:
			if time.Since() > .minRefillDuration {
				.refillTokens(time.Since())
				 = time.Now()
			}
			.queue.push()
			.drainQueue()
		}
	}
}

func ( *TokenBucketFilter) ( time.Duration) {
	 := 1000.0 / float64(.Milliseconds())
	 := (float64(.rate) / ) / 8.0
	.mutex.Lock()
	defer .mutex.Unlock()
	.currentTokensInBucket = math.Min(float64(.maxBurst), .currentTokensInBucket+)
	.log.Tracef("add=(%v / %v) / 8 = %v, currentTokensInBucket=%v, maxBurst=%v", .rate, , , .currentTokensInBucket, .maxBurst)
}

func ( *TokenBucketFilter) () {
	for {
		 := .queue.peek()
		if  == nil {
			break
		}
		 := float64(len(.UserData()))
		if .currentTokensInBucket <  {
			.log.Tracef("currentTokensInBucket=%v, tokens=%v, stop drain", .currentTokensInBucket, )
			break
		}
		.log.Tracef("currentTokensInBucket=%v, tokens=%v, pop chunk", .currentTokensInBucket, )
		.queue.pop()
		.NIC.onInboundChunk()
		.currentTokensInBucket -= 
	}
}

// Close closes and stops the token bucket filter queue
func ( *TokenBucketFilter) () error {
	close(.done)
	.wg.Wait()
	return nil
}