cimport numpy as cnp

from mm_toolbox.ringbuffer.numeric cimport NumericRingBuffer
from mm_toolbox.moving_average.base cimport MovingAverage

cdef class SimpleMovingAverage(MovingAverage):
    """
    The SMA uses equal weights 1/N, where N is the window size.
    """

    def __init__(self, int window, bint fast=False):
        super().__init__(window, fast)
        
        self._raw_values = NumericRingBuffer(window, dtype='float64') 
        self._rolling_sum = 0.0

    cpdef double initialize(self, cnp.ndarray values):
        cdef:
            int i
            int n = values.shape[0]
            double raw_value
            double[:] values_view = values
    
        if n != self._window:
            raise ValueError(
                f"Input array length must match window; expected {self._window} but got {n}"
            )

        self._values.fast_reset()
        self._rolling_sum = 0.0
        
        for i in range(n):
            raw_value = values_view[i]
            self._rolling_sum += raw_value
            self._raw_values.append(raw_value)
        
        self._value = self._rolling_sum / self._window
        self._is_warm = True
        self.push_to_ringbuffer()

        return self._value

    cpdef double next(self, double new_val):
        self.ensure_warm()

        cdef: 
            double new_rolling_sum
            double old_raw_value = self._raw_values[0]
        
        new_rolling_sum = self._rolling_sum + (new_val - old_raw_value)
        return new_rolling_sum / self._window
        
    cpdef double update(self, double new_val):
        self.ensure_warm()
        
        cdef double old_raw_value = self._raw_values.popleft()
        
        self._rolling_sum += (new_val - old_raw_value)
        self._raw_values.append(new_val)
        self._value = self._rolling_sum / self._window
        self.push_to_ringbuffer()
        return self._value
