#/usr/bin/env python

# Jacob Joseph
# 16 Feb 2010

# A class to talk to the LED Control PWM board.
# See PIC16F887 firmware for the other side of these communications

import libftdi
#import libftdipy
#import ftdi
#import pyftdi
#from ctypes import c_int

class ledComm(object):
    ft = None

    def __init__(self):
        self.ft = libftdi.ftdi()
        #self.ft.write_data_set_chunksize(1)
        #self.ft.set_latency_timer(1)
        #self.ft = pyftdi.FTDI()

        #self.usb_open()

    #def usb_open(self):
    #    self.ft.usb_open( 0x0403, 0x6001)
        
    def close(self):
        self.ft.close()

    def senddata(self, command, bytearr=None):
        """Send a command and optional data to the board.
0x02 - raw grayscale, followed by 24 bytes
0x03 - raw dot correction, followed by 12 bytes
0x80 - reset data reception.  This might screw up the blank syncronization
0x81 - disable GIE (turn lights off)
0x82 - enable GIE (turn lights on)"""

        if bytearr is None: bytearr = []
        data = [command]+bytearr
        #print "sending", [hex(a) for a in data]

        self.ft.write_data( data)

        #for d in data:
        #    # FIXME: sending all at once doesn't seem to work right now.
        #    self.ft.write_data( [d])
        #Arr = c_int * len(data)
        #cbuf = Arr( *data)
        #self.ft.write_data( cbuf)
        
        return

    def toggle(self, on):
        if on:
            self.senddata(0x82)
        else:
            self.senddata(0x81)
        return
        

    def reset(self):
        self.ft.purge_buffers()
        for i in range(24):
            self.senddata(0x80)
        return

    def update_leds( self, *ledrgb):
        """From 5 tuples of (r,g,b) floats, send the color values to the board."""

        assert len(ledrgb) == 5, "update_leds() requires a list of five (r,g,b) float tuples"
        
        # convert color intensities to integer fractions of 4095, and
        # build a list of them
        out_values = [ int(f*4095) for tup in ledrgb for f in tup]

        # the PIC currently expects 16 values.  Pad this array
        out_values += (16 - len(out_values)) * [0]
        
        
        # ports should be ordered greatest to least
        out_values.reverse()

        # 16 12-bit values should be sent.  Build the 24-byte value
        rf = 24*[0x0]
        
        # algorithm from website ?
        for i,val in enumerate(out_values):
            bit_num = i * 12
            byte_num = bit_num / 8
            start_nibble = (bit_num & 7) > 0

            if not start_nibble:
                rf[byte_num] = val >> 4
                rf[byte_num+1] = (rf[byte_num+1] & 0x0F) | (( val & 0x0F) << 4)
            else:
                rf[byte_num] = (rf[byte_num] & 0xF0) | (val >> 8)
                rf[byte_num+1] = val & 0xFF

        self.senddata(0x02, rf)
        return out_values, rf

def binary( b, bits=8):
    s = ''
    for i in range(bits-1, -1, -1):
        s += str(b >> i & 1)
    return s

def print_vals( *ledrgb):
    out, rf = lc.update_leds( *ledrgb)
    vals = [0x00] * len(out)
    for i in range(len(vals)):

        bytei = int(i * 12 / 8)
        # lower nibble complete
        if i % 2 == 1:
            vals[i] = ((rf[bytei] & 0x0F) << 8 ) | (rf[bytei + 1] & 0xFF)
            
        # upper nibble complete
        else:
            vals[i] = ( (rf[ bytei ] & 0xFF) << 4) | ((rf[ bytei + 1] & 0xF0) >> 4)
            
    return out, [binary(b) for b in rf], vals, vals==out


if __name__ == "__main__":
    
    lc = ledComm()


    
    
