
from typing import Any
import numpy as np


class LoggerArray:
    """
    A LoggerArray is a time series array which can be gradually filled in the interval [k0, k0+N-1].
    n records an index showing the current filling status.
    """
    
    def __init__(self, N, k0 = 0, keep_order=True):
        """
        Initialize a time series of a given length with starting point k
        
                [v0, v1, v2, v3, v4, v5, v6, v7, v8]
                  |                               |
        index:   k0  k0+1, ......,              k0+8
        
        :param N: length 
        :param k0: starting point
        :param keep_order: if True (default), values can only be sequentially inserted, and no index can be skipped in between.
        """
        self.data = np.zeros(N)
        self.k0 = k0
        self.k = 0
        self.N = N 
        self.n = 0 # k0-1
        self.keep_order = keep_order

    @property
    def start(self):
        return self.k0
    
    @property
    def end(self):
        return self.k0 + self.N - 1
    
    @property
    def pointer(self):
        return self.n
    
    def __contains__(self, i):
        j = i - self.k0
        if j < 0 or j >= self.pointer:
            return False 
        return True 
    
    def __getitem__(self, i):
        j = i - self.k0
        # print("get item", i, "->", j, "pointer", self.pointer)
        if self.keep_order:
            if j < 0 or j >= self.pointer: # self.N-1:
                raise KeyError("Index %s out of bounds (%i, %i)." % (i, self.start, self.pointer+1))
        
        return self.data[j]
    
    def __setitem__(self, i, v):
        j = i - self.k0
        # print("set item", j, "pointer", self.pointer, v)
        if self.keep_order:
            if j < 0 or j > self.pointer:
                raise KeyError("Index %s out of bounds (%i, %i)." % (i, self.start, self.pointer+1))
        self.data[j] = v
        
        if j == self.pointer and j == self.n:
            self.n += 1

    def values(self):
        # TODO make iterable instead of list
        return self.data 

    def keys(self):
        # TODO make iterable instead of list
        return np.array(list(range(self.start, self.pointer+self.k0)))
    
    def items(self):
        out = [] # TODO make iterable instead of list
        for i, v in enumerate(self.data):
            out.append((i+self.k0, v))
            if i >= self.pointer-1:
                break 
        
        return out 

if __name__ == "__main__": 
    """
    test example
    """
    
    ts = LoggerArray(100, 2)
    print(ts.start)
    print(ts.end)

    ts[2] = 44 
    ts[3] = 55 
    ts[4] = 66
    ts[5] = 77

    ts[5] = 88
    ts[5] = 99
    ts[4] = 66
    ts[5] = 100

    print(ts.keys(), len(ts.keys()))
    print(ts.values(), len(ts.values()))
    print(ts.items())

    ts[7] = 404 # should raise an error because entry 6 has not yet been filled up