# cython: profile=True
import cython
import time
import logging

cdef extern from 'adxdma_dmadump.h':
    cpdef enum DmaMethod:
        DmaMethodNormal,
        DmaMethodLocked,
        DmaMethodNative

cdef extern from "stdint.h":
    #foo
    ctypedef int uint64_t
    ctypedef int uint32_t

cdef extern from "cmdline.cpp":
    #Ensures cmdline.c gets compiled
    # C is include here so that it doesn't need to be compiled externally
    pass



cdef extern from "cmdline.h":
     #"""Perform H5conversion
     #
     #Notes
     #-----
     #This definition esures that C_func_file.h gets included in the c code generated by Cython
     #and provides a function definition callable from cython code. Note that since C_func_file.h
     #already contains a decleration of multiply_by_10_in_C Cython won't actually genearte a decleration
     #corresponding to the one declared in this function in the generated c code.
     #"""
     #here we provide a definition of the function we want to call from python
     int dmadump(uint64_t *, unsigned int, unsigned int, int, size_t, unsigned int, unsigned int,
      unsigned int,
      uint64_t,
      #uint32_t,
      DmaMethod,
      unsigned int)

import numpy as np

def dma_dump(writeNotRead, nWriteData, wordSize, data, index, engineIndex, address, byteCount, dmaMethod, readCount): # 'arr' is a two-dimensional numpy array
    """Perform h5 conversion of the data
    Converts the raw data read from the opal kelly and returns the timeseries values recorded
    by the NISoC
    Parameters
    ----------
    write: bool
        if true write, if false read
    nWriteData : int
        number of elements to write
    wordSize : int
        size of each element
    engineIndex : int
        dma engine to use
    address : int
        starting index for transaction
    byteCount :
    DmaMethod :
    readCount : int
        number of bytes to read
    data : numpy array
        the data to stick in the buffer
    -----
    This function wraps the c function multiply_by_10_in_C used to perform h5 conversion
    """
    if (wordSize not in [1,2,4,8]):
        logging.error("invalid word size")

    #Cast as needed
    writeNotRead = int(writeNotRead)
    data = data.astype(np.uint64)

    if not data.flags['C_CONTIGUOUS']:
        data = np.ascontiguousarray(data) # Makes a contiguous copy of the numpy array.
    cdef uint64_t[:] data_memview = data

#data can just be a list of 64bit integers, however i'm not sure how to read data out yet
    exitCode = dmadump(&data_memview[0], data_memview.shape[0], data_memview.strides[0]/data_memview.itemsize, writeNotRead, nWriteData, wordSize, index, engineIndex, address,
                dmaMethod, readCount)
    #arr_memview.strides returns stride values in number of bytes, so we must divide by the number of bytes in our item (in this case a double) and arr_memview.itemsize gives us this value.
    return exitCode, data

def dma_dump_read(wordSize, index, engineIndex, address, dmaMethod, readCount): # 'arr' is a two-dimensional numpy array
    """Perform h5 conversion of the data
    Converts the raw data read from the opal kelly and returns the timeseries values recorded
    by the NISoC
    Parameters
    ----------
    write: bool
        if true write, if false read
    nWriteData : int
        number of elements to write
    wordSize : int
        size of each element
    engineIndex : int
        dma engine to use
    address : int
        starting index for transaction
    byteCount :
    DmaMethod :
    readCount : int
        number of bytes to read
    data : numpy array
        the data to stick in the buffer
    -----
    This function wraps the c function multiply_by_10_in_C used to perform h5 conversion
    """ 
    writeNotRead = 0
    nWriteData = 0
    data = np.zeros(readCount)
    
    if (wordSize not in [1,2,4,8]):
        logging.error("invalid word size")

    #Cast as needed
    data = data.astype(np.uint64)

    if not data.flags['C_CONTIGUOUS']:
        data = np.ascontiguousarray(data) # Makes a contiguous copy of the numpy array.
    cdef uint64_t[:] data_memview = data

#data can just be a list of 64bit integers, however i'm not sure how to read data out yet
    exitCode = dmadump(&data_memview[0], data_memview.shape[0], data_memview.strides[0]/data_memview.itemsize, writeNotRead, nWriteData, wordSize, index, engineIndex, address,
                dmaMethod, readCount)
    #arr_memview.strides returns stride values in number of bytes, so we must divide by the number of bytes in our item (in this case a double) and arr_memview.itemsize gives us this value.
    return exitCode, data

def dma_dump_write(data, nWriteData, wordSize, index, engineIndex, address, dmaMethod): # 'arr' is a two-dimensional numpy array
    """Perform h5 conversion of the data
    Converts the raw data read from the opal kelly and returns the timeseries values recorded
    by the NISoC
    Parameters
    ----------
    write: bool
        if true write, if false read
    nWriteData : int
        number of elements to write
    wordSize : int
        size of each element
    engineIndex : int
        dma engine to use
    address : int
        starting index for transaction
    byteCount :
    DmaMethod :
    readCount : int
        number of bytes to read
    data : numpy array
        the data to stick in the buffer
    -----
    This function wraps the c function multiply_by_10_in_C used to perform h5 conversion
    """
    if (wordSize not in [1,2,4,8]):
        logging.error("invalid word size")

    readCount = 0
    writeNotRead = 1
    #Cast as needed
    data = data.astype(np.uint64)

    if not data.flags['C_CONTIGUOUS']:
        data = np.ascontiguousarray(data) # Makes a contiguous copy of the numpy array.
    cdef uint64_t[:] data_memview = data

#data can just be a list of 64bit integers, however i'm not sure how to read data out yet
    exitCode = dmadump(&data_memview[0], data_memview.shape[0], data_memview.strides[0]/data_memview.itemsize, writeNotRead, nWriteData, wordSize, index, engineIndex, address,
                dmaMethod, readCount)
    #arr_memview.strides returns stride values in number of bytes, so we must divide by the number of bytes in our item (in this case a double) and arr_memview.itemsize gives us this value.
    return exitCode
