// Package player provides the audio engine for MP3 playback with
// a 10-band parametric EQ, volume control, and sample capture for visualization.
package player

import (
	"sync/atomic"

	"github.com/gopxl/beep/v2"
)

// tap is a streamer wrapper that copies samples into a ring buffer
// for real-time FFT visualization. It sits in the audio pipeline
// before the volume control so the visualizer sees pre-volume amplitude.
//
// The write position is updated atomically, allowing the audio thread
// (sole writer) and the UI thread (infrequent reader at 50ms intervals)
// to operate without mutex contention. Minor sample tearing at the
// read boundary is invisible in FFT-based spectrum visualization.
type tap struct {
	s    beep.Streamer
	buf  []float64
	pos  atomic.Int64
	size int
}

// newTap wraps a streamer with a ring buffer of the given size.
func newTap(s beep.Streamer, bufSize int) *tap {
	return &tap{
		s:    s,
		buf:  make([]float64, bufSize),
		size: bufSize,
	}
}

// Stream passes audio through while capturing a mono mix into the ring buffer.
func (t *tap) Stream(samples [][2]float64) (int, bool) {
	n, ok := t.s.Stream(samples)
	p := int(t.pos.Load())
	for i := range n {
		t.buf[p] = (samples[i][0] + samples[i][1]) / 2
		p = (p + 1) % t.size
	}
	t.pos.Store(int64(p))
	return n, ok
}

// Err returns the underlying streamer's error.
func (t *tap) Err() error {
	return t.s.Err()
}

// SamplesInto copies the last len(dst) samples into dst, avoiding allocation.
// Uses two copy() calls for the ring buffer wraparound instead of per-element
// modulo, which is significantly faster for large buffers (e.g. FFT size 2048).
func (t *tap) SamplesInto(dst []float64) int {
	n := min(len(dst), t.size)
	p := int(t.pos.Load())
	start := (p - n + t.size) % t.size
	if start+n <= t.size {
		copy(dst, t.buf[start:start+n])
	} else {
		first := t.size - start
		copy(dst[:first], t.buf[start:])
		copy(dst[first:], t.buf[:n-first])
	}
	return n
}
