package wshub

import "sync"

// sigRing is a fixed-capacity circular buffer storing (time, value) pairs.
// Writes come from the Hub.Run() goroutine; reads come from HTTP handler goroutines.
// The embedded RWMutex protects concurrent access.
type sigRing struct {
	mu         sync.RWMutex
	t, v       []float64
	cap        int
	head, size int // next write position; current fill
}

func newSigRing(capacity int) *sigRing {
	return &sigRing{
		t:   make([]float64, capacity),
		v:   make([]float64, capacity),
		cap: capacity,
	}
}

// write appends (tArr[i], vArr[i]) pairs, overwriting oldest entries when full.
func (rb *sigRing) write(tArr, vArr []float64) {
	rb.mu.Lock()
	defer rb.mu.Unlock()
	for i := 0; i < len(tArr); i++ {
		rb.t[rb.head] = tArr[i]
		rb.v[rb.head] = vArr[i]
		rb.head = (rb.head + 1) % rb.cap
		if rb.size < rb.cap {
			rb.size++
		}
	}
}

// slice returns copies of all (t, v) pairs whose timestamp falls in [t0, t1].
// The returned slices are safe to use after the call without holding any lock.
func (rb *sigRing) slice(t0, t1 float64) ([]float64, []float64) {
	rb.mu.RLock()
	defer rb.mu.RUnlock()

	if rb.size == 0 {
		return nil, nil
	}
	start := 0
	if rb.size == rb.cap {
		start = rb.head
	}
	physAt := func(k int) int { return (start + k) % rb.cap }

	// Binary search for t0
	lo, hi := 0, rb.size
	for lo < hi {
		m := (lo + hi) >> 1
		if rb.t[physAt(m)] < t0 {
			lo = m + 1
		} else {
			hi = m
		}
	}
	kStart := lo

	// Binary search for t1
	lo, hi = kStart, rb.size
	for lo < hi {
		m := (lo + hi) >> 1
		if rb.t[physAt(m)] <= t1 {
			lo = m + 1
		} else {
			hi = m
		}
	}
	kEnd := lo

	n := kEnd - kStart
	if n <= 0 {
		return nil, nil
	}
	outT := make([]float64, n)
	outV := make([]float64, n)
	for i := 0; i < n; i++ {
		p := physAt(kStart + i)
		outT[i] = rb.t[p]
		outV[i] = rb.v[p]
	}
	return outT, outV
}
