#!/usr/bin/perl
#################################################
# Package for controlling the BWCT LCD device   #
# Requires Inline::C                            #
# Inline C code based on "ulcd" by Bernd Walter #
#################################################


package BWCT::LCD;

# Loading C inline code
use Inline (
        C => "DATA",
        ($ENV{LIBUSB_LIBDIR}
            ? ( LIBS => "-L\"$ENV{LIBUSB_LIBDIR}\" " .
                        ($^O eq 'MSWin32' ? ' -llibusb -L\"$ENV{WINDDK}\\lib\\crt\\i386\" -lmsvcrt ' : '-lusb') )
            : ( LIBS => '-lusb', )
        ),
        ($ENV{LIBUSB_INCDIR} ? ( INC => "-I\"$ENV{LIBUSB_INCDIR}\"" ) : () )

   );
 
Inline->init();

#Constructor
sub new {
   my $invocant = shift;
   
   my $self = {};
   my $class=ref($invocant) || $invocant;   

   # Display specific stuff
   $self->{'max_lines'} = 2;
   $self->{'max_chars'} = 16;
   
   
   my @lines;
   for (my $i = 0; $i < $self->{'max_lines'}; $i++) {
      my %line;
      $line{'text'} = "";
      $line{'moving'} = 1;      
      $line{'currentPos'} = 0;

      $lines[$i] = \%line;
   }
   $self->{'lines'} = \@lines;   
   
   #Display Specific
   $self->{'lines'}->[0]->{'offset'} = 128;
   $self->{'lines'}->[1]->{'offset'} = 192;

   $self->{'lines'}->[0]->{'borderLeft'} = "";
   $self->{'lines'}->[0]->{'borderRight'} = "";
   $self->{'lines'}->[1]->{'borderLeft'} = "";
   $self->{'lines'}->[1]->{'borderRight'} = "";   
 
   bless $self, $class;
   return $self;
}

# Display Initialisation
sub init {
   my $self = shift;
   initLCDDisplay();
   my $len = $self->{'max_lines'};
   for (my $i = 0; $i < $len; $i++) {
      $self->flush($i);
   }
}

# Display something on the Display
# Params:
# $line: 0 or 1 (for displaying text on the first or second line)
# $text: the text to be displayed
# $moving: 0 or 1 (set to 1 to allow scrolling the text if it is longer than the display)
# $centered: 0 or 1 (1 centers the text (if it is shorter than the display width)
# $borderLeft: A string containing characters that create a left "border" around the displayed text (the border
#     will still visible, even if the text scrolls)
# $borderRight: A string containing characters that create a right "border" around the displayed text (the border
#     will still visible, even if the text scrolls)
sub display {
   my $self = shift;   
   my $line = shift;
   my $text = shift;
   my $moving = shift;
   my $centered = shift;
   my $borderLeft = shift;
   my $borderRight = shift;
    
   my $lineRef = $self->{'lines'}->[$line];
    # Centering    
    if (($centered == 1) && (length($text)+length($borderLeft)+length($borderRight) < $self->{'max_chars'})) {
          my $beginPos = $self->{'max_chars'} / 2 - (length($text)+length($borderLeft)+length($borderRight)) / 2;          
          for (my $x = 0; $x < int($beginPos); $x++) {
             $text = " ".$text;
          }
    }
 
    # Filling up text in Display
    for (my $x = length($text); $x < $self->{'max_chars'}-length($borderRight)-length($borderLeft); $x++) {
       $text .= " ";
    }
    $self->{'lines'}->[$line]->{'text'} = $text;
    $self->{'lines'}->[$line]->{'moving'} = $moving;
    $self->{'lines'}->[$line]->{'currentPos'} = 0; 
    $self->{'lines'}->[$line]->{'borderLeft'} = $borderLeft;       
    $self->{'lines'}->[$line]->{'borderRight'} = $borderRight;
    $self->tick();
}
    
# Method to be executed on every tick (once per second or so)
sub tick {
   my $self = shift;
   my $len = $self->{'max_lines'};
   for (my $i = 0; $i < $len; $i++) {
       my $lineRef = $self->{'lines'}->[$i];
       my $text = $lineRef->{'text'};              

	# Calculating space for borders	
	my $borderWidthLeft = length($lineRef->{'borderLeft'});
	my $borderWidthRight = length($lineRef->{'borderRight'});
       
	
	# Execute        
	displayOnLCDDisplay ($lineRef->{'offset'}."", $lineRef->{'borderLeft'}.substr($text,$lineRef->{'currentPos'},$self->{'max_chars'}-$borderWidthLeft-$borderWidthRight).$lineRef->{'borderRight'});
	             
       if (($lineRef->{'moving'} == 1) && (length($text)+$borderWidthLeft+$borderWidthRight > $self->{'max_chars'})) {                    
$self->{'lines'}->[$i]->{'currentPos'} = ($lineRef->{'currentPos'} +1) % (length($text)+$borderWidthLeft+$borderWidthRight - ($self->{'max_chars'}-1));

	  
	  
       }
   }
}

# Empty a line on the display
sub flush {
   my $self = shift;
   my $line = shift;
   my $len = $self->{'max_lines'};
   my $text = "";
   my $lineRef = $self->{'lines'}->[$line];
   
   for (my $x = 0; $x < $self->{'max_chars'}; $x++) {
        $text .= " ";
   }
   displayOnLCDDisplay ($lineRef->{'offset'}."",$text); 
}


1;

__DATA__
 
__C__

#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <usb.h>
#include "ulcd.h"

extern int usb_debug;
usb_dev_handle *LCDDEV = NULL;
int LCDDEVNR = -1;

/**
* Returns the LCD device
**/
usb_dev_handle *getLCDDevice() {
	struct usb_bus *busses;
	struct usb_bus *bus;
	usb_dev_handle *lcd;
	lcd = NULL;
	usb_init();
	usb_find_busses();
	usb_find_devices();
	busses = usb_get_busses();
        int c = 0;
        int i = 0;
        int a = 0;

	for (bus = busses; bus; bus = bus->next) {
		struct usb_device *dev;

		for (dev = bus->devices; dev; dev = dev->next) {
			/* Check if this device is a BWCT device */
			if (dev->descriptor.idVendor != 0x03da) {
				continue;
			}
			/* Loop through all of the configurations */
			for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
				/* Loop through all of the interfaces */
				for (i = 0; i < dev->config[c].bNumInterfaces; i++) {
					/* Loop through all of the alternate settings */
					for (a = 0; a < dev->config[c].interface[i].num_altsetting; a++) {
						/* Check if this interface is a ulcd */
						if ((dev->config[c].interface[i].altsetting[a].bInterfaceClass == 0xff &&
						    dev->config[c].interface[i].altsetting[a].bInterfaceSubClass == 0x01) ||
						    dev->descriptor.idProduct == 0x0002) {
							lcd = usb_open(dev);
							usb_claim_interface(lcd, i);
							return(lcd);
						}
					}
				}
			}
		}
	}
	return NULL;
}

/**
* Returns the LCD device number
**/
int getDeviceNr() {
	struct usb_bus *busses;
	struct usb_bus *bus;
	usb_dev_handle *lcd;
	lcd = NULL;
	usb_init();
	usb_find_busses();
	usb_find_devices();
	busses = usb_get_busses();
        int c = 0;
        int i = 0;
        int a = 0;

	for (bus = busses; bus; bus = bus->next) {
		struct usb_device *dev;

		for (dev = bus->devices; dev; dev = dev->next) {
			/* Check if this device is a BWCT device */
			if (dev->descriptor.idVendor != 0x03da) {
				continue;
			}
			/* Loop through all of the configurations */
			for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
				/* Loop through all of the interfaces */
				for (i = 0; i < dev->config[c].bNumInterfaces; i++) {
					/* Loop through all of the alternate settings */
					for (a = 0; a < dev->config[c].interface[i].num_altsetting; a++) {
						/* Check if this interface is a ulcd */
						if ((dev->config[c].interface[i].altsetting[a].bInterfaceClass == 0xff &&
						    dev->config[c].interface[i].altsetting[a].bInterfaceSubClass == 0x01) ||
						    dev->descriptor.idProduct == 0x0002) {							
							return(i);
						}
					}
				}
			}
		}
	}
	return -1;
}


void initLCDDisplay () {
   usb_dev_handle *lcd;
   int port;
   lcd = getLCDDevice();
   if (lcd == NULL) {
		printf("failed to open LCD device\n");
		exit(1);
   }
   LCDDEV = lcd;
   LCDDEVNR = getDeviceNr();
   port = 0;
   // Init
   usb_control_msg(lcd, USB_TYPE_VENDOR, VENDOR_LCD_RESET, 0, port, NULL, 0, 1000);
   usb_control_msg(lcd, USB_TYPE_VENDOR, VENDOR_LCD_CMD, atol("40"), port, NULL, 0, 1000);
   usb_control_msg(lcd, USB_TYPE_VENDOR, VENDOR_LCD_CMD, atol("14"), port, NULL, 0, 1000);
   usb_control_msg(lcd, USB_TYPE_VENDOR, VENDOR_LCD_CMD, atol("6"), port, NULL, 0, 1000);
   // Setting contrast
   usb_control_msg(lcd, USB_TYPE_VENDOR, VENDOR_LCD_CONTRAST, 10, port, NULL, 0, 1000);
   usb_release_interface(LCDDEV, LCDDEVNR);
   //usb_close(lcd);
}

void displayOnLCDDisplay (char *offset, char *text) {
   usb_dev_handle *lcd;
   char* tmp;
   int port;
   /*lcd = getLCDDevice();
   if (lcd == NULL) {
		printf("failed to open LCD device\n");
		exit(1);
   }*/
   port = 0;
   // Init
   usb_control_msg(LCDDEV, USB_TYPE_VENDOR, VENDOR_LCD_CMD, atol(offset), port, NULL, 0, 500);

   // Putting string to device
   for (tmp = text; *tmp != '\0'; tmp++) {
      usb_control_msg(LCDDEV, USB_TYPE_VENDOR, VENDOR_LCD_DATA, *tmp, port, NULL, 0, 500);
   }
   //usb_release_interface(lcd, getDeviceNr());
   //usb_close(lcd);
}





