# A ctypes wrapper around libfprint, a C lib for fingerprint scanners.
import ctypes

lib = ctypes.cdll.LoadLibrary("libfprint.so.0")

#
# Enums...
#

# enum fp_driver_type
(
    DRIVER_PRIMITIVE,
    DRIVER_IMAGING,
) = map(ctypes.c_int, xrange(0, 2))

# enum fp_scan_type
(
    FP_SCAN_TYPE_PRESS,  # press
    FP_SCAN_TYPE_SWIPE,  # swipe
) = map(ctypes.c_int, xrange(0, 2))


class usb_id(ctypes.Structure):
    _fields_ = [
        ('vendor', ctypes.c_uint16),
        ('product', ctypes.c_uint16),
        ('driver_data', ctypes.c_ulong),
        ]

class libusb_device_descriptor(ctypes.Structure):
    # from libusb.h
    _fields_ = [
        ('bLength', ctypes.c_uint8),
        ('bDescriptorType', ctypes.c_uint8),
        ('bcdUSB', ctypes.c_uint16),
        ('bDeviceClass', ctypes.c_uint8),
        ('bDeviceSubClass', ctypes.c_uint8),
        ('bDeviceProtocol', ctypes.c_uint8),
        ('bMaxPacketSize0', ctypes.c_uint8),
        ('idVendor', ctypes.c_uint16),
        ('idProduct', ctypes.c_uint16),
        ('bcdDevice', ctypes.c_uint16),
        ('iManufacturer', ctypes.c_uint8),
        ('iProduct', ctypes.c_uint8),
        ('iSerialNumber', ctypes.c_uint8),
        ('bNumConfigurations', ctypes.c_uint8),
        ]


#
# 'Hidden' types, i.e. types that should not be accessed internally by apps
# (defined in fprint_internal.h)
#
fp_dscv_dev = None
fp_dscv_print = None
fp_dev = None
class fp_driver(ctypes.Structure):
    _fields_ = [
        ('id', ctypes.c_uint16),
        ('name', ctypes.c_char_p),
        ('full_name', ctypes.c_char_p),
        ('id_table', usb_id),
        ('type', ctypes.c_int),       # fp_driver_type
        ('scan_type', ctypes.c_int),  # fp_scan_type
        ('priv', ctypes.c_void_p),
        #  Device operations
        # int (*discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
        ('discover',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(libusb_device_descriptor),
                ctypes.POINTER(ctypes.c_uint32)),
         ),
        # int (*open)(struct fp_dev *dev, unsigned long driver_data);
        ('open',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(fp_dev),       # dev
                ctypes.c_ulong),              # driver_data
         ),
        # void (*close)(struct fp_dev *dev);
        ('close',
         ctypes.CFUNCTYPE(
                ctypes.c_void_p,  # Returntype?
                ctypes.POINTER(fp_dev),       # dev
                ),
         ),
        # int (*enroll_start)(struct fp_dev *dev);
        ('enroll_start',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(fp_dev)        # dev
                ),
         ),
        # int (*enroll_stop)(struct fp_dev *dev);
        ('enroll_stop',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(fp_dev),       # dev
                ),
         ),
        # int (*verify_start)(struct fp_dev *dev);
        ('verify_start',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(fp_dev),       # dev
                ),
         ),
        # int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
        ('verify_stop',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(fp_dev),  # dev
                ctypes.c_int,            # iterating (glib.h gboolean=gint=int)
                ),
         ),
        # int (*identify_start)(struct fp_dev *dev);
        ('identify_start',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(fp_dev),  # dev
                ),
         ),
        # int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
        ('identify_stop',
         ctypes.CFUNCTYPE(
                ctypes.c_int,  # Returntype?
                ctypes.POINTER(fp_dev),  # dev
                ctypes.c_int,            # iterating (glib.h gboolean=gint=int)
                ),
         ),
        ]

fp_print_data = None
fp_img = None

# enum fp_finger   Some enum...
(
    LEFT_THUMB, # thumb (left hand)
    LEFT_INDEX,  # index finger (left hand)
    LEFT_MIDDLE,  # middle finger (left hand)
    LEFT_RING,  # ring finger (left hand)
    LEFT_LITTLE,  # little finger (left hand)
    RIGHT_THUMB,  # thumb (right hand)
    RIGHT_INDEX,  # index finger (right hand)
    RIGHT_MIDDLE,  # middle finger (right hand)
    RIGHT_RING,  # ring finger (right hand)
    RIGHT_LITTLE,  # little finger (right hand)
) = map(ctypes.c_int, xrange(1, 11))



# Drivers

lib.fp_driver_get_name.restype = ctypes.POINTER(ctypes.c_char)
lib.fp_driver_get_name.argtypes = [ctypes.POINTER(fp_driver)]
driver_get_name = lib.fp_driver_get_name


discover_devs = lib.fp_discover_devs
discover_devs.restype = ctypes.POINTER(
    ctypes.POINTER(fp_dscv_dev))

# Library
fp_init = lib.fp_init
fp_exit = lib.fp_exit

lib.fp_set_debug.argtypes = [ctypes.c_int,]
fp_set_debug = lib.fp_set_debug


if __name__ == '__main__':
    fp_init()
    devs = discover_devs()

    if isinstance(devs, ctypes.POINTER(ctypes.c_void_p)):
        print "No device detected!"
    else:
        print "Something found: "
        print devs
    import pdb; pdb.set_trace()
    fp_exit()
