#!/usr/bin/env python

#   (C) 2008 Jacob Joseph <jacob@jjoseph.org>
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.

import libftdi, time, kdeui

# commands:
#   0x02 - raw grayscale, followed by 72 bytes
#   0x03 - raw dot correction, followed by 36 bytes
#   0x80 - reset data reception. this might screw up the blank syncronization
#   0x81 - disable GIE (turn lights off)
#   0x82 - enable GIE (turn lights on)

#   LED TI5940 ports
#   ------------------------------------
#   led0_r   TI0 OUT0   TI0 OUT1   TI0 OUT2
#   led0_g   TI0 OUT3   TI0 OUT4   TI0 OUT5
#   led0_b   TI0 OUT6   TI0 OUT7   TI0 OUT8
#   led1_r   TI0 OUT9   TI0 OUT10  TI0 OUT11
#   led1_g   TI0 OUT12  TI0 OUT13  TI0 OUT14
#   led1_b   TI0 OUT15  TI1 OUT0   TI1 OUT1
#   led2_r   TI1 OUT2   TI1 OUT3   TI1 OUT4
#   led2_g   TI1 OUT5   TI1 OUT6   TI1 OUT7
#   led2_b   TI1 OUT8   TI1 OUT9   TI1 OUT10
#   led3_r   TI1 OUT11  TI1 OUT12  TI1 OUT13
#   led3_g   TI1 OUT14  TI1 OUT15  TI2 OUT0
#   led3_b   TI2 OUT1   TI2 OUT2   TI2 OUT3
#   led4_r   TI2 OUT4   TI2 OUT5   TI2 OUT6
#   led4_g   TI2 OUT7   TI2 OUT8   TI2 OUT9
#   led4_b   TI2 OUT10  TI2 OUT11  TI2 OUT12


class tlc5940_fifo(object):
    ft = None

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

    def close(self):
        self.ft.close()

    def senddata(self, command, bytearr=None):
        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:
            # sending all at once doesn't seem to work right now.
            self.ft.write_data( [d])
        return

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

    # Data ready to be clocked out MSB first.
    def setcolors( self, *ledrgb):
        #print ledrgb
        #assert len(ledrgb) == 5, "set5colors() requires a list of five tuples"
        
        # convert color intensities to fractions of 4095.
        #ledrgb = [ (int(r*4095), int(g*4095), int(b*4095))
        #           for (r, g, b) in ledrgb]

        # build output list of intensities
        out_values = []
        for (i,rgb) in enumerate(ledrgb):
            for col in rgb:
                # 3 outputs are used for each channel
                out_values += 3*[int(col*4095)]

        # pad to 48 outputs 
        out_values += (48-len(out_values)) * [0]

        # ports should be ordered greatest to least
        out_values.reverse()

        # 72-byte array
        rf = 72*[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

        # test with 24 bytes
        #self.senddata(0x02, rf[-24:])
        self.senddata(0x02, rf)
        return

    def set4colors(self, rgb_fl, rgb_fr, rgb_rr, rgb_rl):
        """RGB values, as tuples of floats, to control light intensity"""
        # lights connected to ports OUT15-OUT4, in order of R,G,B

        port_hex_values = []
        # 9 ports
        for i in rgb_fl + rgb_fr + rgb_rr + rgb_rl:
            port_hex_values.append( int(i * 4095))

        #print [hex(a) for a in port_hex_values]
        
        # 7 values (for 15 total)
        for i in [4095]*7:
            port_hex_values.append( 4095)

        # bits in order 15.H, 15.L, 14.H, ..., 0.H, 0.L
        all_bits = long()
        for (i,val) in enumerate(port_hex_values):
            # 4-bit blocks
            for j in range(2,-1,-1):
                subbyte = val & (0xF << j*4)
                
                # reverse subbytes
                #subbyte = (subbyte & 0xF0 >> 4) subbyte&
                
                all_bits = all_bits | (subbyte << i*12)
                #print i,j, hex(subbyte), hex(val), hex(all_bits)
        
        # bytes in order 0.L, 0.H, 1.L, ..., 15.L, 15.H
        # ready for transmission
        byte_arr = []
        for i in range(0,24):
            byte_arr.append((all_bits >> (i*4*2)) & 0xFF)

        # HACK
        # reverse sub bytes that are shared
        for i in range(1,24,3):
            byte_arr[i] = byte_arr[i] >> 4 | (byte_arr[i] & 0x0F) << 4
        
        print [hex(a) for a in byte_arr]

        #print len(byte_arr)
        self.senddata( 0x00, byte_arr)
        return

def test0():
    tlc.senddata(0x00)
    tlc.senddata(0xFF)
    tlc.senddata(0xF0)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)
    tlc.senddata(0x00)


def test2(on=0.15, off=0.0):
    while True:
        tlc.setcolors( (1,0,0), (0,1,0), (0,0,1), (1,0,0), (0,0,0))
        time.sleep(on)
        tlc.setcolors( (0,0,0), (0,0,0), (0,0,0), (0,0,0), (0,0,0))
        time.sleep(off)        
        tlc.setcolors( (0,1,0), (0,0,1), (0,1,0), (0,1,0), (0,0,0))
        time.sleep(on)
        tlc.setcolors( (0,0,0), (0,0,0), (0,0,0), (0,0,0), (0,0,0))
        time.sleep(off)        
        tlc.setcolors( (0,0,1), (1,0,0), (0,0,1), (0,0,1), (0,0,0))
        time.sleep(on)
        tlc.setcolors( (0,0,0), (0,0,0), (0,0,0), (0,0,0), (0,0,0))
        time.sleep(off)        
        tlc.setcolors( (0,1,0), (0,1,0), (0,1,1), (1,0,0), (0,0,0))
        time.sleep(on)
        tlc.setcolors( (0,0,0), (0,0,0), (0,0,0), (0,0,0), (0,0,0))
        time.sleep(off) 
        tlc.setcolors( (0,0,1), (0,0,1), (0,0,1), (0,0,1), (0,0,0))
        time.sleep(on)
        tlc.setcolors( (0,0,0), (0,0,0), (0,0,0), (0,0,0), (0,0,0))
        time.sleep(off)        

def test3(on=0.05):
    r = 1.0
    b = 0.0
    up = True
    while True:
        tlc.setcolors( (1,1,1), (r, 0, 1-b), (0, r, b), (0, 1-r, 1-b))
        time.sleep(on)
        if up:
            r = r - 0.001
            b = b + 0.001
            if r <= 0: up=False
        else:
            r = r + 0.001
            b = b - 0.001
            if b <= 0: up=True

def test_circle(delay=0.02, angle_step=0.1, saturation=255, value=255):
    angle = 0.0

    color_array = [None]*4

    for led in range(4):
        color_array[led] = kdeui.KColor()
    
    while True:
        angle = (angle + angle_step ) % 360
        
        rgb_array = []
        for led in range(4):
            hue = (angle + led * 90) % 360
            color_array[led].setHsv( hue, saturation, value)            

            rgb_array.append( [float(c)/255 for c in color_array[led].getRgb()])

        tlc.setcolors( *rgb_array)
        
        time.sleep( delay)

    

def test1():
    tlc.senddata(0x00)
    for i in range(24):
        tlc.senddata(0x00)

if __name__ == "__main__":
    tlc = tlc5940_fifo()

    dot_full = [0xFF]*12
    gs_full  = [0xFF]*24

    gs_off   = [0x00]*24

    #tlc.senddata( 0x00, gs_full)
    #tlc.senddata( 0x01,dot_full)
    #tlc.set4colors( (1,1,1), (1,1,1), (1,1,1), (1,1,1))
    #tlc.set4colors( (1,0,0), (0,0,0), (0,0,0), (0,0,0))

    
                                                            
    

