#include "time_series_buffer.h"

#include <algorithm>
#include <cassert>
#include <cstring>
#include <limits>
#include <numeric>
#include <utility>

TimeSeriesBuffer::TimeSeriesBuffer(size_t cap)
    : data(new float[cap]), capacity(cap), count(0), offset(0) {}

TimeSeriesBuffer::~TimeSeriesBuffer() {
    delete[] data;
}

TimeSeriesBuffer::TimeSeriesBuffer(TimeSeriesBuffer&& other) noexcept
    : data(other.data), capacity(other.capacity), count(other.count), offset(other.offset) {
    other.data     = nullptr;
    other.capacity = 0;
    other.count    = 0;
    other.offset   = 0;
}

TimeSeriesBuffer& TimeSeriesBuffer::operator=(TimeSeriesBuffer&& other) noexcept {
    if (this != &other) {
        delete[] data;
        data           = other.data;
        capacity       = other.capacity;
        count          = other.count;
        offset         = other.offset;
        other.data     = nullptr;
        other.capacity = 0;
        other.count    = 0;
        other.offset   = 0;
    }
    return *this;
}

void TimeSeriesBuffer::push(float value) {
    data[offset % capacity] = value;
    offset++;
    if (count < capacity) count++;
}

float TimeSeriesBuffer::get(size_t index) const {
    assert(index < count);
    // oldest element is at (offset - count) % capacity
    size_t real = (offset - count + index) % capacity;
    return data[real];
}

float TimeSeriesBuffer::latest() const {
    assert(count > 0);
    return data[(offset - 1) % capacity];
}

float TimeSeriesBuffer::min() const {
    float m = std::numeric_limits<float>::max();
    for (size_t i = 0; i < count; ++i) m = std::min(m, get(i));
    return m;
}

float TimeSeriesBuffer::max() const {
    float m = std::numeric_limits<float>::lowest();
    for (size_t i = 0; i < count; ++i) m = std::max(m, get(i));
    return m;
}

float TimeSeriesBuffer::average() const {
    if (count == 0) return 0.0f;
    float sum = 0.0f;
    for (size_t i = 0; i < count; ++i) sum += get(i);
    return sum / static_cast<float>(count);
}

size_t TimeSeriesBuffer::size() const {
    return count;
}

bool TimeSeriesBuffer::full() const {
    return count == capacity;
}

size_t TimeSeriesBuffer::copy_ordered(float* out, size_t out_capacity) const {
    size_t n = std::min(count, out_capacity);
    for (size_t i = 0; i < n; ++i)
        out[i] = get(i);
    return n;
}
