package audio

import (
	"math"
	"time"
)

// Take returns a Streamer which streams s for at most d duration.
func Take(d time.Duration, s Streamer) Streamer {
	currSample := 0
	numSamples := int(math.Ceil(d.Seconds() * SampleRate))
	return StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
		if currSample >= numSamples {
			return 0, false
		}
		toStream := numSamples - currSample
		if len(samples) < toStream {
			toStream = len(samples)
		}
		sn, sok := s.Stream(samples[:toStream])
		currSample += sn
		return sn, sok
	})
}

// Seq takes zero or more Streamers and returns a Streamer which streams them one by one without pauses.
func Seq(s ...Streamer) Streamer {
	i := 0
	return StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
		for i < len(s) && len(samples) > 0 {
			sn, sok := s[i].Stream(samples)
			samples = samples[sn:]
			n, ok = n+sn, ok || sok
			if !sok {
				i++
			}
		}
		return n, ok
	})
}

// Mix takes zero or more Streamers and returns a Streamer which streames them mixed together.
func Mix(s ...Streamer) Streamer {
	return StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
		var tmp, mix [512][2]float64

		for len(samples) > 0 {
			toStream := len(mix)
			if toStream > len(samples) {
				toStream = len(samples)
			}

			// clear the mix buffer
			for i := range mix[:toStream] {
				mix[i] = [2]float64{}
			}

			snMax := 0 // max number of streamed samples in this iteration
			for _, st := range s {
				// mix the stream
				sn, sok := st.Stream(tmp[:toStream])
				if sn > snMax {
					snMax = sn
				}
				ok = ok || sok

				for i := range tmp[:sn] {
					mix[i][0] += tmp[i][0]
					mix[i][1] += tmp[i][1]
				}
			}

			// copy mix buffer into samples
			for i := range mix[:snMax] {
				samples[i] = mix[i]
			}

			n += snMax
			if snMax < len(mix) {
				break
			}
			samples = samples[snMax:]
		}

		return n, ok
	})
}