package shroom_internals

import (
	"sync"
)

const MAX_CACHE_POINTS = 1000

type DataCache struct {
	sync.RWMutex
	data  []Datapoint
	start int
}

func (dc *DataCache) Add(data *Datapoint) {
	dc.Lock()
	defer dc.Unlock()

	if len(dc.data) < MAX_CACHE_POINTS {
		dc.data = append(dc.data, *data)
	} else {
		dc.data[dc.start] = *data
		dc.start += 1
		if dc.start >= len(dc.data) {
			dc.start = 0
		}
	}
}

func (dc *DataCache) binarySearch(s int, e int, target uint64) int {
	for s < e {
		mid := (s + e) / 2
		if dc.data[mid].Time < target {
			s = mid + 1
		} else {
			e = mid
		}
	}
	return s
}

func (dc *DataCache) ReadSince(start_time uint64) []Datapoint {
	dc.RLock()
	defer dc.RUnlock()

	if len(dc.data) == 0 {
		return nil
	}
	if dc.data[dc.start].Time <= start_time {
		// binary search for the point right at/after
		// start_time

		if len(dc.data) >= MAX_CACHE_POINTS {
			// if the data is full length we've been wrapping
			// around
			if dc.data[0].Time <= start_time {
				// it's between [0, start)
				idx := dc.binarySearch(0, dc.start, start_time)
				ret := []Datapoint{}
				ret = append(ret, dc.data[idx:dc.start]...)
				return ret
			} else {
				// it's between [start, len(dc.data))
				// and we can always include [0, start) afterwards
				idx := dc.binarySearch(dc.start, len(dc.data), start_time)
				ret := []Datapoint{}
				ret = append(ret, dc.data[idx:len(dc.data)]...)
				ret = append(ret, dc.data[0:dc.start]...)
				return ret
			}
		} else {
			// it's between [0, len(dc.data))
			idx := dc.binarySearch(0, len(dc.data), start_time)
			ret := []Datapoint{}
			ret = append(ret, dc.data[idx:len(dc.data)]...)
			return ret
		}
	} else {
		return nil
	}
}