from __future__ import with_statement
from pylibusb import lowlevel, highlevel
import sys

startreg_offset = 0x80

# XXX: according to the biopod kernel driver there's more registers, but they aren't described and I'd prefer only to set the registers that have atleast a bit of a description
default_registers = [
	(0x80, "Control Register 1", 0x00),
	(0x81, "Control Register 2", 0x00),
	(0x82, "Excitation Common Controls", 0x00),
	(0x83, "Detect Control", 0x13), # 0x10 in the kernel driver
	(0x84, "Reference Resistor", 0x07),
	(0x85, "Reference Capacitor", 0x7F),
	(0x86, "Detect Drive", 0x03),
	(0x87, "Detect Frequency", 0x01),
	(0x88, "Column Scan Rate", 0x02),
	(0x89, "Measure Drive", 0x03),
	(0x8A, "Measure Frequency", 0x05),
	(0x8B, "Z-Matrix Kernel Size", 0x2F),
	(0x8C, "Demod Phase 2", 0x7A),
	(0x8D, "Demod Phase 1", 0x00),
	(0x8E, "Channel Gain", 0x20),
	(0x8F, "Channel Bias", 0x22),
	(0x90, "Carrier Null", 0x00),
	(0x91, "A/D Reference High", 0x14),
	(0x92, "A/D Reference Low", 0x03),
	(0x93, "Start Row", 0x00),
	(0x94, "End Row", 0x0F),
	(0x95, "Start Column", 0x00),
	(0x96, "End Column", 0x7F),
	(0x97, "Data Format", 0x04),
	(0x98, "** Was 20 ** Image Data Control", 0x20),
	(0x99, "LED Control", 0x00),
	(0x9A, "Status", 0x00),
	(0x9B, "Challenge Word 1", 0x00),
	(0x9C, "Challenge Word 2", 0x00),
	(0x9D, "Challenge Word 3", 0x00),
	(0x9E, "Challenge Word 4", 0x00),
	(0x9F, "Challenge Word 5", 0x00),
	(0xA0, "Bias", 0x00),
	#(0xA1, None, 0x03),
	#(0xA2, None, 0x02),
	#(0xA3, None, 0x00),
	#(0xA4, None, 0x00),
	#(0xA5, None, 0x00),
	#(0xA6, None, 0x00),
	#(0xA7. None, 0x08),
	#(0xA8, None, 0x00),
	#(0xA9, None, 0x40),
	#(0xAA, None, 0x00),
	#(0xAB, None, 0x00),
	#(0xAC, None, 0x00),
	#(0xAD, None, 0x00),
	#(0xAE, None, 0x00),
	#(0xAF, None, 0x00),
	#(0xB0, None, 0x00),
	#(0xB1, None, 0x00),
	#(0xB2, None, 0x00),
	#(0xB3, None, 0x00),
	#(0xB4, None, 0x05),
	#(0xB5, None, 0x00),
	#(0xB6, None, 0x00),
	#(0xB7, None, 0x00),
	#(0xB8, None, 0x00),
	#(0xB9, None, 0x00),
	#(0xBA, None, 0x00),
	#(0xBB, None, 0x22),
]

class BioPod(object):
	def __init__(self, device):
		self.device = device
		self.ep_in = None
		self.ep_out = None
		self.closed = True
		self.iface_claimed = False
		assert device.descriptor.idVendor == 0x08ff # 08ff  AuthenTec, Inc.
		# XXX: handle other Product id's
		assert device.descriptor.idProduct == 0x5731 # 5731  AES3500 TruePrint Sensor
		self.handle = lowlevel.open(device)
		self.closed = False
		# device setup stuff
		iface = device.config[0].interface[0]
		self.iface = iface
		for lowlevel_ep in iface.altsetting[0].endpoint:
			ep = highlevel.get_endpoint(lowlevel_ep, self.handle)
			if ep.direction == "in":
				assert self.ep_in == None, "more than 1 'in' endpoints on the given device"
				self.ep_in = ep
			elif ep.direction == "out":
				assert self.ep_out == None, "more than 1 'out' endpoints on the given device"
				self.ep_out = ep
		assert self.ep_in and self.ep_out
		lowlevel.claim_interface(self.handle, iface.altsetting[0].bInterfaceNumber)
		self.iface_claimed = True
		# set the default registers
		self.ep_out.write("\x87\x01", 1000)
		#self.ep_out.write("\x89\x03", 1000)
		#self.ep_out.write("\x8A\x05", 1000)
		#self.ep_out.write("\x8B\x2F", 1000)
		#self.ep_out.write("\x8C\x7A", 1000)
		#self.ep_out.write("\x8E\x20", 1000)
		#self.ep_out.write("\x8F\x22", 1000)
		#self.ep_out.write("\x80\x01", 1000)
		send = []
		for reg, desc, value in default_registers:
			send.append("%s%s" % (chr(reg), chr(value)))
		print repr("".join(send))
		self.ep_out.write("".join(send), 1000)

	def try_getting_image(self):
		# XXX: 0x81 0x01 seems to enable continuious scanning where
		# 0x04 automatically goes back to 0x00 when done scanning once
		# (0x02 is for showing the registers, and I think it's bit based
		# to a degree)
		self.ep_out.write("\x81\x04", 1000)
		try:
			res = self.ep_in.read(128*128, 10000)
		finally:
			self.ep_out.write("\x81\x00", 1000)
		return res

	def __del__(self):
		self.close()

	def close(self):
		if self.iface_claimed:
			lowlevel.release_interface(self.handle, self.iface.altsetting[0].bInterfaceNumber)
			self.iface_claimed = False
		if self.closed == False:
			lowlevel.close(self.handle)
			self.closed = True

import Image
from cStringIO import StringIO

colours = (0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00)

def get_image(f, sections=8, columns=128):
	img = Image.new("L", (sections*16, columns), 255)
	pdata = img.load()
	# XXX: after this there's 129 bytes that I'm not using, starting with a 0xDF byte (seems last 120 bytes is the register info)
	for section in range(sections):
		header = f.read(1)
		assert header == chr(0xE0 + section), (section, header, f.tell())
		for column in range(columns):
			for index, c in enumerate(f.read(8)):
				pdata[127-(section*16)-(index*2), column] = colours[ord(c) & 0x0F]
				pdata[126-(section*16)-(index*2), column] = colours[(ord(c) >> 4) & 0x0F]
	return img

import psyco
psyco.bind(get_image)

if __name__ == "__main__":
	lowlevel.init()
	lowlevel.find_busses()
	lowlevel.find_devices()
	dev = list(lowlevel.get_busses())[-2].devices
	bp = BioPod(dev)
	res = bp.try_getting_image()
	print res[0]
	img = get_image(StringIO(res[1]))
	img.show()
	img.save("fp.png")
	img.save("fp.jpg")

