"""
NiFpga, a thin wrapper around the FPGA Interface C API

Copyright (c) 2015 National Instruments
"""
from .statuscheckedlibrary import (NamedArgtype,
                                   LibraryFunctionInfo,
                                   StatusCheckedLibrary,
                                   LibraryNotFoundError)
import ctypes
from enum import Enum


class DataType(Enum):
    """ DataType is an enumerator, with the intention of abstracting the
    association between datatypes and ctypes within the Python API.
    """
    Bool = 1
    I8 = 2
    U8 = 3
    I16 = 4
    U16 = 5
    I32 = 6
    U32 = 7
    I64 = 8
    U64 = 9
    Sgl = 10
    Dbl = 11
    Fxp = 12
    Cluster = 13

    def __str__(self):
        return self.name

    def _return_ctype(self):
        """ Returns the associated ctype of a given datatype. """
        _datatype_ctype = {
            DataType.Bool: ctypes.c_uint8,
            DataType.I8: ctypes.c_int8,
            DataType.U8: ctypes.c_uint8,
            DataType.I16: ctypes.c_int16,
            DataType.U16: ctypes.c_uint16,
            DataType.I32: ctypes.c_int32,
            DataType.U32: ctypes.c_uint32,
            DataType.I64: ctypes.c_int64,
            DataType.U64: ctypes.c_uint64,
            DataType.Sgl: ctypes.c_float,
            DataType.Dbl: ctypes.c_double,
            DataType.Fxp: ctypes.c_uint32,
            DataType.Cluster: ctypes.c_uint32,
        }
        return _datatype_ctype[self]

    def isSigned(self):
        if self == DataType.I8 \
           or self == DataType.I16 \
           or self == DataType.I32 \
           or self == DataType.I64 \
           or self == DataType.Sgl \
           or self == DataType.Dbl:
            return True
        return False


class FifoPropertyType(Enum):
    """ Types of FIFO Properties, intended to abstract away the C Type. """
    I32 = 1
    U32 = 2
    I64 = 3
    U64 = 4
    Ptr = 5

    def __str__(self):
        return self.name

    def _return_ctype(self):
        """ Returns the associated ctype of a given property type. """
        _propertyType_ctype = {
            FifoPropertyType.I32: ctypes.c_int32,
            FifoPropertyType.U32: ctypes.c_uint32,
            FifoPropertyType.I64: ctypes.c_int64,
            FifoPropertyType.U64: ctypes.c_uint64,
            FifoPropertyType.Ptr: ctypes.c_void_p
        }
        return _propertyType_ctype[self]


class FifoProperty(Enum):
    BytesPerElement = 1  # U32
    BufferAllocationGranularityElements = 2  # U32
    BufferSizeElements = 3  # U64
    MirroredElements = 4  # U64
    DmaBufferType = 5  # I32
    DmaBuffer = 6  # Ptr
    FlowControl = 7  # I32
    ElementsCurrentlyAcquired = 8  # U64
    PreferredNumaNode = 9  # I32

    def __str__(self):
        return self.name


class FlowControl(Enum):
    """ When flow control is disabled, the FIFO no longer acts like a FIFO.
    The FIFO will overwrite data in this mode. The FPGA fully controls when
    data transfers. This can be useful when regenerating a waveform or when
    you only care about the most recent data.
    For Host to Target FIFOs, this only disables flow control when the entire FIFO
    has been written once.
    For Target to Host FIFOs, flow control is disabled on start and the FPGA can
    begin writing then.
    """
    DisableFlowControl = 1
    """ Default FIFO behavior. No data is lost, data only moves when there is
    room for it.
    """
    EnableFlowControl = 2


class DmaBufferType(Enum):
    """ Allocated by RIO means the driver take the other properties and create
    a buffer that meets their requirements.
    """
    AllocatedByRIO = 1
    """ Allocated by User means you will allocate a buffer and set the DMA Buffer
    property with your buffer. The driver will then use this buffer as the
    underlying host memory in the FIFO.
    """
    AllocatedByUser = 2


_fifo_properties_to_types = {
    FifoProperty.BytesPerElement: FifoPropertyType.U32,
    FifoProperty.BufferAllocationGranularityElements: FifoPropertyType.U32,
    FifoProperty.BufferSizeElements: FifoPropertyType.U64,
    FifoProperty.MirroredElements: FifoPropertyType.U64,
    FifoProperty.DmaBufferType: FifoPropertyType.I32,
    FifoProperty.DmaBuffer: FifoPropertyType.Ptr,
    FifoProperty.FlowControl: FifoPropertyType.I32,
    FifoProperty.ElementsCurrentlyAcquired: FifoPropertyType.U64,
    FifoProperty.PreferredNumaNode: FifoPropertyType.I32,
}


class FpgaViState(Enum):
    """ The FPGA VI has either been downloaded and not run, or the VI was aborted
    or reset. """
    NotRunning = 0
    """ An error has occurred. """
    Invalid = 1
    """ The FPGA VI is currently executing. """
    Running = 2
    """ The FPGA VI stopped normally.  This indicates it was not aborted or reset,
    but instead reached the end of any loops it was executing and ended. """
    NaturallyStopped = 3


_SessionType = ctypes.c_uint32
_IrqContextType = ctypes.c_void_p

OPEN_ATTRIBUTE_NO_RUN = 1
OPEN_ATTRIBUTE_BITFILE_PATH_IS_UTF8 = 2
RUN_ATTRIBUTE_WAIT_UNTIL_DONE = 1
CLOSE_ATTRIBUTE_NO_RESET_IF_LAST_SESSION = 1
INFINITE_TIMEOUT = 0xffffffff


class _NiFpga(StatusCheckedLibrary):
    """
    _NiFpga, a thin wrapper around the FPGA Interface C API

    Defines FPGA Interface C API types, and provides the _NiFpga class
    which loads C API symbols and allows them to be called, e.g.
    nifpga.Open(<args>) or nifpga["ReadU32](<args>). If any NiFpga function
    return status is non-zero, the appropriate exception derived from either
    WarningStatus or ErrorStatus is raised.

    While _NiFpga can be used directly, Session provides a higher-level and
    more convenient API that is better-suited for most users.
    """

    def __init__(self):
        library_function_infos = [
            LibraryFunctionInfo(
                pretty_name="Open",
                name_in_library="NiFpgaDll_Open",
                named_argtypes=[
                    NamedArgtype("bitfile path", ctypes.c_char_p),
                    NamedArgtype("signature", ctypes.c_char_p),
                    NamedArgtype("resource", ctypes.c_char_p),
                    NamedArgtype("attribute", ctypes.c_uint32),
                    NamedArgtype("session", ctypes.POINTER(_SessionType)),
                ]),
            LibraryFunctionInfo(
                pretty_name="Run",
                name_in_library="NiFpgaDll_Run",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("attribute", ctypes.c_uint32),
                ]),
            LibraryFunctionInfo(
                pretty_name="Close",
                name_in_library="NiFpgaDll_Close",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("attribute", ctypes.c_uint32),
                ]),
            LibraryFunctionInfo(
                pretty_name="OpenResource",
                name_in_library="NiFpgaDll_OpenResource",
                named_argtypes=[
                    NamedArgtype("parentSession", _SessionType),
                    NamedArgtype("parentIndex", ctypes.c_uint32),
                    NamedArgtype("globalIndex", ctypes.c_uint32),
                    NamedArgtype("childSession", ctypes.POINTER(_SessionType)),
                ]),
            LibraryFunctionInfo(
                pretty_name="AddResources",
                name_in_library="NiFpgaDll_AddResources",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("resourceNames", ctypes.POINTER(ctypes.c_char_p)),
                    NamedArgtype("resourceValues", ctypes.POINTER(ctypes.c_uint32)),
                    NamedArgtype("externalRegisters", ctypes.POINTER(ctypes.c_uint32)),
                    NamedArgtype("numberOfResources", ctypes.c_size_t),
                ]),
            LibraryFunctionInfo(
                pretty_name="GetResourceIndex",
                name_in_library="NiFpgaDll_GetResourceIndex",
                named_argtypes=[
                    NamedArgtype("resourceName", ctypes.c_char_p),
                    NamedArgtype("resourceIndex", ctypes.POINTER(ctypes.c_uint32)),
                ]),
            LibraryFunctionInfo(
                pretty_name="ReleaseResourceIndex",
                name_in_library="NiFpgaDll_ReleaseResourceIndex",
                named_argtypes=[
                    NamedArgtype("resourceName", ctypes.c_char_p),
                ]),
            LibraryFunctionInfo(
                pretty_name="GetResourceName",
                name_in_library="NiFpgaDll_GetResourceName",
                named_argtypes=[
                    NamedArgtype("resourceIndex", ctypes.c_uint32),
                    NamedArgtype("resourceName", ctypes.POINTER(ctypes.c_char_p)),
                ]),
            LibraryFunctionInfo(
                pretty_name="Reset",
                name_in_library="NiFpgaDll_Reset",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                ]),
            LibraryFunctionInfo(
                pretty_name="Abort",
                name_in_library="NiFpgaDll_Abort",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                ]),
            LibraryFunctionInfo(
                pretty_name="Download",
                name_in_library="NiFpgaDll_Download",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                ]),
            LibraryFunctionInfo(
                pretty_name="ReserveIrqContext",
                name_in_library="NiFpgaDll_ReserveIrqContext",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("context", ctypes.POINTER(_IrqContextType)),
                ]),
            LibraryFunctionInfo(
                pretty_name="UnreserveIrqContext",
                name_in_library="NiFpgaDll_UnreserveIrqContext",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("context", ctypes.POINTER(_IrqContextType)),
                ]),
            LibraryFunctionInfo(
                pretty_name="WaitOnIrqs",
                name_in_library="NiFpgaDll_WaitOnIrqs",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("context", ctypes.POINTER(_IrqContextType)),
                    NamedArgtype("irqs", ctypes.c_uint32),
                    NamedArgtype("timeout ms", ctypes.c_uint32),
                    NamedArgtype("irqs asserted", ctypes.POINTER(ctypes.c_uint32)),
                    NamedArgtype("timed out", ctypes.POINTER(DataType.Bool._return_ctype())),
                ]),
            LibraryFunctionInfo(
                pretty_name="AcknowledgeIrqs",
                name_in_library="NiFpgaDll_AcknowledgeIrqs",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("irqs", ctypes.c_uint32),
                ]),
            LibraryFunctionInfo(
                pretty_name="ConfigureFifo",
                name_in_library="NiFpgaDll_ConfigureFifo",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("depth", ctypes.c_size_t),
                ]),
            LibraryFunctionInfo(
                pretty_name="ConfigureFifo2",
                name_in_library="NiFpgaDll_ConfigureFifo2",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("requested depth", ctypes.c_size_t),
                    NamedArgtype("actual depth", ctypes.POINTER(ctypes.c_size_t))
                ]),
            LibraryFunctionInfo(
                pretty_name="StartFifo",
                name_in_library="NiFpgaDll_StartFifo",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                ]),
            LibraryFunctionInfo(
                pretty_name="StopFifo",
                name_in_library="NiFpgaDll_StopFifo",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                ]),
            LibraryFunctionInfo(
                pretty_name="UnreserveFifo",
                name_in_library="NiFpgaDll_UnreserveFifo",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                ]),
            LibraryFunctionInfo(
                pretty_name="ReleaseFifoElements",
                name_in_library="NiFpgaDll_ReleaseFifoElements",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("elements", ctypes.c_size_t),
                ]),
            LibraryFunctionInfo(
                pretty_name="GetPeerToPeerFifoEndpoint",
                name_in_library="NiFpgaDll_GetPeerToPeerFifoEndpoint",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("endpoint", ctypes.POINTER(ctypes.c_uint32)),
                ]),
            LibraryFunctionInfo(
                pretty_name="ClientFunctionCall",
                name_in_library="NiFpgaDll_ClientFunctionCall",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("group", ctypes.c_uint32),
                    NamedArgtype("functionId", ctypes.c_uint32),
                    NamedArgtype("inBuffer", ctypes.c_void_p),
                    NamedArgtype("inBufferSize", ctypes.c_size_t),
                    NamedArgtype("outBuffer", ctypes.c_void_p),
                    NamedArgtype("outBufferSize", ctypes.c_size_t),
                ]),
            LibraryFunctionInfo(
                pretty_name="FindRegisterPrivate",
                name_in_library="NiFpgaDll_FindRegisterPrivate",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("name", ctypes.c_char_p),
                    NamedArgtype("expectedType", ctypes.c_uint32),
                    NamedArgtype("offset", ctypes.POINTER(ctypes.c_uint32)),
                ]),
            LibraryFunctionInfo(
                pretty_name="FindFifoPrivate",
                name_in_library="NiFpgaDll_FindFifoPrivate",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("name", ctypes.c_char_p),
                    NamedArgtype("expectedType", ctypes.c_uint32),
                    NamedArgtype("fifoNumber", ctypes.POINTER(ctypes.c_uint32)),
                ]),
            LibraryFunctionInfo(
                pretty_name="GetFpgaViState",
                name_in_library="NiFpgaDll_GetFpgaViState",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("state", ctypes.POINTER(ctypes.c_uint32)),
                ]),
            LibraryFunctionInfo(
                pretty_name="CommitFifoConfiguration",
                name_in_library="NiFpgaDll_CommitFifoConfiguration",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                ])
        ]  # list of function_infos

        for datatype in DataType:
            if datatype == DataType.Fxp or datatype == DataType.Cluster:
                continue  # Fxp and Cluster do not have named read write entry points.
            type_ctype = datatype._return_ctype()
            library_function_infos.extend([
                LibraryFunctionInfo(
                    pretty_name="Read%s" % datatype,
                    name_in_library="NiFpgaDll_Read%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("indicator", ctypes.c_uint32),
                        NamedArgtype("value", ctypes.POINTER(type_ctype)),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="Write%s" % datatype,
                    name_in_library="NiFpgaDll_Write%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("control", ctypes.c_uint32),
                        NamedArgtype("value", type_ctype),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="ReadArray%s" % datatype,
                    name_in_library="NiFpgaDll_ReadArray%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("indicator", ctypes.c_uint32),
                        NamedArgtype("array", ctypes.POINTER(type_ctype)),
                        NamedArgtype("size", ctypes.c_size_t),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="WriteArray%s" % datatype,
                    name_in_library="NiFpgaDll_WriteArray%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("control", ctypes.c_uint32),
                        NamedArgtype("array", ctypes.POINTER(type_ctype)),
                        NamedArgtype("size", ctypes.c_size_t),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="ReadFifo%s" % datatype,
                    name_in_library="NiFpgaDll_ReadFifo%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("fifo", ctypes.c_uint32),
                        NamedArgtype("data", ctypes.POINTER(type_ctype)),
                        NamedArgtype("number of elements", ctypes.c_size_t),
                        NamedArgtype("timeout ms", ctypes.c_uint32),
                        NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="WriteFifo%s" % datatype,
                    name_in_library="NiFpgaDll_WriteFifo%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("fifo", ctypes.c_uint32),
                        NamedArgtype("data", ctypes.POINTER(type_ctype)),
                        NamedArgtype("number of elements", ctypes.c_size_t),
                        NamedArgtype("timeout ms", ctypes.c_uint32),
                        NamedArgtype("empty elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="AcquireFifoReadElements%s" % datatype,
                    name_in_library="NiFpgaDll_AcquireFifoReadElements%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("fifo", ctypes.c_uint32),
                        NamedArgtype("elements", ctypes.POINTER(ctypes.POINTER(type_ctype))),
                        NamedArgtype("elements requested ", ctypes.c_size_t),
                        NamedArgtype("timeout ms", ctypes.c_uint32),
                        NamedArgtype("elements acquired", ctypes.POINTER(ctypes.c_size_t)),
                        NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="AcquireFifoWriteElements%s" % datatype,
                    name_in_library="NiFpgaDll_AcquireFifoWriteElements%s" % datatype,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("fifo", ctypes.c_uint32),
                        NamedArgtype("elements", ctypes.POINTER(ctypes.POINTER(type_ctype))),
                        NamedArgtype("elements requested ", ctypes.c_size_t),
                        NamedArgtype("timeout ms", ctypes.c_uint32),
                        NamedArgtype("elements acquired", ctypes.POINTER(ctypes.c_size_t)),
                        NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                    ]),
            ])  # end of library_function_infos.extend() call
        for fifoPropertyType in FifoPropertyType:
            type_ctype = fifoPropertyType._return_ctype()
            library_function_infos.extend([
                LibraryFunctionInfo(
                    pretty_name="GetFifoProperty%s" % fifoPropertyType,
                    name_in_library="NiFpgaDll_GetFifoProperty%s" % fifoPropertyType,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("fifo", ctypes.c_uint32),
                        NamedArgtype("property", ctypes.c_uint32),
                        NamedArgtype("value", ctypes.POINTER(type_ctype)),
                    ]),
                LibraryFunctionInfo(
                    pretty_name="SetFifoProperty%s" % fifoPropertyType,
                    name_in_library="NiFpgaDll_SetFifoProperty%s" % fifoPropertyType,
                    named_argtypes=[
                        NamedArgtype("session", _SessionType),
                        NamedArgtype("fifo", ctypes.c_uint32),
                        NamedArgtype("property", ctypes.c_uint32),
                        NamedArgtype("value", type_ctype),
                    ]),
            ])
        # Add Composite FIFO Functions
        library_function_infos.extend([
            LibraryFunctionInfo(
                pretty_name="ReadFifoComposite",
                name_in_library="NiFpgaDll_ReadFifoComposite",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("data", ctypes.POINTER(ctypes.c_uint8)),
                    NamedArgtype("bytes per element", ctypes.c_uint32),
                    NamedArgtype("number of elements", ctypes.c_size_t),
                    NamedArgtype("timeout ms", ctypes.c_uint32),
                    NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                ]),
            LibraryFunctionInfo(
                pretty_name="WriteFifoComposite",
                name_in_library="NiFpgaDll_WriteFifoComposite",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("data", ctypes.POINTER(ctypes.c_uint8)),
                    NamedArgtype("bytes per element", ctypes.c_uint32),
                    NamedArgtype("number of elements", ctypes.c_size_t),
                    NamedArgtype("timeout ms", ctypes.c_uint32),
                    NamedArgtype("empty elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                ]),
            LibraryFunctionInfo(
                pretty_name="AcquireFifoReadElementsComposite",
                name_in_library="NiFpgaDll_AcquireFifoReadElementsComposite",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("elements", ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8))),
                    NamedArgtype("bytes per element", ctypes.c_uint32),
                    NamedArgtype("elements requested ", ctypes.c_size_t),
                    NamedArgtype("timeout ms", ctypes.c_uint32),
                    NamedArgtype("elements acquired", ctypes.POINTER(ctypes.c_size_t)),
                    NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                ]),
            LibraryFunctionInfo(
                pretty_name="AcquireFifoWriteElementsComposite",
                name_in_library="NiFpgaDll_AcquireFifoWriteElementsComposite",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("elements", ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8))),
                    NamedArgtype("bytes per element", ctypes.c_uint32),
                    NamedArgtype("elements requested ", ctypes.c_size_t),
                    NamedArgtype("timeout ms", ctypes.c_uint32),
                    NamedArgtype("elements acquired", ctypes.POINTER(ctypes.c_size_t)),
                    NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                ]),
        ])
        # Add Acquire FIFO Region functions
        library_function_infos.extend([
            LibraryFunctionInfo(
                pretty_name="AcquireFifoReadRegion",
                name_in_library="NiFpgaDll_AcquireFifoReadRegion",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("region", ctypes.POINTER(ctypes.c_void_p)),
                    NamedArgtype("elements", ctypes.POINTER(ctypes.c_void_p)),
                    NamedArgtype("is signed", ctypes.c_bool),
                    NamedArgtype("bytes per element", ctypes.c_uint32),
                    NamedArgtype("elements requested ", ctypes.c_size_t),
                    NamedArgtype("timeout ms", ctypes.c_uint32),
                    NamedArgtype("elements acquired", ctypes.POINTER(ctypes.c_size_t)),
                    NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                ]),
            LibraryFunctionInfo(
                pretty_name="AcquireFifoWriteRegion",
                name_in_library="NiFpgaDll_AcquireFifoWriteRegion",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("region", ctypes.POINTER(ctypes.c_void_p)),
                    NamedArgtype("elements", ctypes.POINTER(ctypes.c_void_p)),
                    NamedArgtype("is signed", ctypes.c_bool),
                    NamedArgtype("bytes per element", ctypes.c_uint32),
                    NamedArgtype("elements requested ", ctypes.c_size_t),
                    NamedArgtype("timeout ms", ctypes.c_uint32),
                    NamedArgtype("elements acquired", ctypes.POINTER(ctypes.c_size_t)),
                    NamedArgtype("elements remaining", ctypes.POINTER(ctypes.c_size_t)),
                ]),
            LibraryFunctionInfo(
                pretty_name="ReleaseFifoRegion",
                name_in_library="NiFpgaDll_ReleaseFifoRegion",
                named_argtypes=[
                    NamedArgtype("session", _SessionType),
                    NamedArgtype("fifo", ctypes.c_uint32),
                    NamedArgtype("region", ctypes.c_void_p)
                ]),
        ])
        try:
            super(_NiFpga, self).__init__(library_name="NiFpga",
                                          library_function_infos=library_function_infos)
        except LibraryNotFoundError as e:
            import platform
            system = platform.system().lower()
            if system == 'windows':
                raise LibraryNotFoundError(
                    "Unable to find NiFpga.dll on your system, "
                    "ensure you have installed the relevent RIO distribution for your device. "
                    "Search for your product here: http://www.ni.com/downloads/ni-drivers/ "
                    "Original Exception: " + str(e))
            if system == 'linux':
                raise LibraryNotFoundError(
                    "Unable to find libNiFpga.so on your system, "
                    "If you are on desktop linux, ensure you have installed the latest "
                    "RIO Linux distribution for your product, such as https://www.ni.com/en-us/support/downloads/drivers/download.ni-linux-device-drivers.html "
                    "If you are on a Linux RT embedded target (cRIO, sbRIO, FlexRIO, Industrial Controller, etc) install NI-RIO to your target "
                    "though MAX following these instructions: https://www.ni.com/getting-started/set-up-hardware/compactrio/controller-software "
                    "Original Exception: " + str(e))
            if system == 'darwin':
                raise LibraryNotFoundError(
                    "Unable to find NiFpga.Framework on your system, "
                    "Sorry we don't yet support using RIO Devices on OSX, contact your sales person "
                    "for the latest information on OSX support. "
                    "Original Exception: " + str(e))
            raise