import ustruct as us
import uctypes as uc
import micropython
from vts import Chrono, delay_ms as sleep
from array import array
from ucollections import OrderedDict
from gc import collect

t = Chrono()
ITERATIONS = micropython.const(50)
DATA_STORE = micropython.const(8 * ITERATIONS)
store = bytearray()
pos = 0
ar_1 = array('B')
m_list = []
payload = [x for x in range(1,9)]

#! ONLY UNCOMMENT THIS IF STORE IS FULL, OTHERWISE YOU CAN OVERWRITE OTHER PARTS OF MEMORY
#! AS UCTYPES MODULE GIVES ACCESS TO C-LEVEL MEMORY ALLOCATION
# dec = {"M{}".format(num): (8*num | uc.ARRAY, 8 | uc.UINT8) for num in range(ITERATIONS)}
# print(dec)
# can_map = uc.struct(uc.addressof(store), dec)

def reset(empty=False):
    global store, ar_1, pos, m_list
    store = bytearray() if empty else bytearray(DATA_STORE)
    ar_1 = array('B')
    pos = 0
    m_list.clear()
    collect()
    print(" -------------------------")


def byte_array_extend(l):
    store.extend(bytearray(l))

def pre_allocated_bytearray(l):
    global pos
    for i in l:
        store[pos] = i
        pos += 1

def slicer(l): # Should work but doesn't, get 'bytearray assignment not supported' error
    global pos
    store[pos:pos+8] = i
    pos += 8

def mem_view(l):
    global pos
    for i in l:
        window[pos] = i
        pos += 1

def ustruct_pack_tuple(l):
    global pos
    us.pack_into('B'*8, store, pos, *l)
    pos += 8

def array_extend(l):
    ar_1.extend(bytearray(l))

def basic_list(l):
    # global m_list
    # m_list += l
    m_list.extend(l)

@micropython.native
def native_pack(l):
    # store.extend(bytearray(l))
    m_list.extend(l)

# @micropython.viper
# def viper_write_8_Bit(l, pos: int):
#     global store
#     for i in l:
#         ptr8(store)[pos] = int(i)
#         pos += 1

@micropython.viper
def viper_write(l):
    # store.extend(bytearray(l))
    m_list.extend(l)

if __name__ == "__main__":
    reset()
    t.restart()
    for i in range(ITERATIONS): pre_allocated_bytearray(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('pre_allocated_bytearray', t.read()/1000))
    print(store[int((len(store)/2) + 1)])

    reset()
    t.restart()
    for i in range(ITERATIONS): byte_array_extend(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('Byte_array_extend', t.read()/1000))
    print(store[int((len(store)/2) + 1)])

    reset()
    window = memoryview(store)
    t.restart()
    for i in range(ITERATIONS): mem_view(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('Mem_View', t.read()/1000))
    print(store[int((len(store)/2) + 1)])

    reset()
    t.restart()
    for i in range(ITERATIONS): ustruct_pack_tuple(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('ustruct_pack', t.read()/1000))
    print(store[int((len(store)/2) + 1)])

    reset()
    t.restart()
    for i in range(ITERATIONS): array_extend(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('array_extend', t.read()/1000))
    print(ar_1[int((len(ar_1)/2) + 1)])

    reset()
    t.restart()
    for i in range(ITERATIONS): basic_list(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('basic_List', t.read()/1000))
    print(m_list[int((len(m_list)/2) + 1)])

    reset()
    t.restart()
    for i in range(ITERATIONS): native_pack(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('Native_Pack', t.read()/1000))
    print(m_list[int((len(m_list)/2) + 1)])
    # print(store[int((len(store)/2) + 1)])

    reset()
    t.restart()
    for i in range(ITERATIONS): viper_write(payload)
    print('Function \'{}\' Time = {:6.3f}ms'.format('viper_write', t.read()/1000))
    # print(m_list[int((len(m_list)/2) + 1)])
    print(m_list[int((len(m_list)/2) + 1)])
    print(" ------------------------- \n")

''' Conclusions:
    >50 Iterations:
        # Extending list is quicker than converting to bytearray or array
        # Native Emitter and Viper Emitter provide minimal benifit in this instance
        # Pre-allocating list or byte-array is slower than extending

    >100 Iterations:
        # Extending list is quicker than converting to bytearray or array
        # Native Emitter and Viper Emitter provide minimal benifit in this instance
        # Pre-allocating list or byte-array is slower than extending

    >500 Iterations:
        # Extending list is quicker than converting to bytearray
        # array offers significant benifit over bytearray and similar performance to list
        # Native Emitter does not speed anything up in this case
        # Viper Emitter provides a small benifit
        # Pre-allocating list or byte-array is slower than extending

    >1000 Iterations:
        # array-extend now the fastest method
        # converting to bytearray or array quicker than list
        # Native Emitter does not speed anything up in this case
        # Viper Emitter provides small benifit
        # Pre-allocating list or byte-array is slower than extending

    >5000 Iterations:
        # array-extend still the fastest method
        # converting to bytearray or array quicker than list
        # ustruct.pack_into now offers similar performance to array
        # Native Emitter does not speed anything up in this case (quicker than viper though)
        # Viper Emitter does not speed anything up
        # Pre-allocating list or byte-array is faster than extending

'''
