#!/usr/bin/env perl
# PODNAME: stick - simple script to control a blinkstick
# ABSTRACT: Control a Blinkstick

=head1 NAME

stick

=head1 SYNOPSIS

  Syntax: stick [options] 
  
  About:  Control a BlinkStick
  
  [options]
    -h, -?, --help                    Show help
    --blink                           number of times to blink
    -b, --brightness                  set LED brightness percentage, 0..100 [DEFAULT: 20]
    --channel                         blinkstick pro only, which block of 64 LEDs to write to
    -c, --color                       set color by name, hex triplet or random
    --delay                           Delay time in msecs between blinks [DEFAULT: 150]
    -d, --device                      use device/serial what ever matches
    -i, --index                       blinkstick pro only, which LED in block to write to
    --info                            get info about connected devices
    --inverse                         original blinkstick only, use inverse mode,
    -m, --mode                        blinkstick pro only, set mode, 0 normal, 1 inverse, 2 WS2812, 3 WS2812 mirror
    --name                            use device that has this name
    --serial                          Use device that has this serial number
    --set_leds                        blinkstick pro only, set number of leds attached to device
    --set_name, --set-infoblock1      Set name into infoblock 1
    --set_token, --set-infoblock2     Set access token into infoblock 2
    -v, --verbose                     Dump extra useful information

=head1 DESCRIPTION

Control a USB blinkstick L<http://blinkstick.com>
  
=cut

#
# (c) Kevin Mulholland, moodfarm@cpan.org
# this code is released under the Perl Artistic License

# notes
# serial number ends
#   * -1.2 standard stick
#   * -2.1 pro stick
#   * -3.0 blinkstrip/square (8 leds)
#   * nano has 2 leds
#   * flex (32 pixels)
# if multiple sticks and no device picked then error
# if pro action on a std stick then either ignore or error

use 5.10.0 ;
use strict ;
use warnings ;
use App::Basis ;
use Device::BlinkStick ;
use Data::Printer ;

# -----------------------------------------------------------------------------

use constant DEFAULT_BLINK_TIME => 150 ;
use constant MODE_INVERSE       => 1 ;

# -----------------------------------------------------------------------------

sub show_info
{
    my $stick = shift ;
    my $info  = $stick->info() ;
    
    # Manufacturer:   $info->{manufacturer}
    # Description:    $info->{product}
    say "Device $info->{serial_number}" ;
    say "    Name:   $info->{device_name}"  if ( $info->{device_name} ) ;
    say "    Token:  $info->{access_token}" if ( $info->{access_token} ) ;
    say "    Color:  " . $info->{colorname} ;
    say "    Mode:   $info->{mode}" ;
    say "    Type:   $info->{type}" ;
    say "    Leds:   $info->{leds}" if ( $info->{type} eq 'pro' ) ;
}

# -----------------------------------------------------------------------------
# main

my $program = get_program() ;
my $action  = 0 ;

my %opt = init_app(
    help_text    => "Control a BlinkStick",
    help_cmdline => "",
    options      => {
        'verbose|v' => 'Dump extra useful information',
        'inverse'   => 'original blinkstick only, use inverse mode,',
        'color|c=s' => { desc => 'set color by name, hex triplet or random' },
        'mode|m=i'  => {
            desc =>
                'blinkstick pro only, set mode, 0 normal, 1 inverse, 2 WS2812, 3 WS2812 mirror',
            # validate => sub { my $m = shift ; $m > 0 && $m <= 3 ; }
        },
        'info'                       => 'get info about connected devices',
        'set_name|set-infoblock1=s'  => 'Set name into infoblock 1',
        'set_token|set-infoblock2=s' => 'Set access token into infoblock 2',
        'set_leds=i' =>
            'blinkstick pro only, set number of leds attached to device',
        'serial=s'   => 'Use device that has this serial number',
        'name=s'     => 'use device that has this name',
        'device|d=s' => 'use device/serial what ever matches',
        "index|i=i" =>
            { desc => "blinkstick pro only, which LED in block to write to" },
        "channel=i" => {
            desc => "blinkstick pro only, which block of 64 LEDs to write to"
        },
        "brightness|b=i" => {
            desc    => "set LED brightness percentage, 0..100",
            default => 20
        },
        "delay=i" => {
            desc    => "Delay time in msecs between blinks",
            default => DEFAULT_BLINK_TIME
        },
        "blink=i" =>
            { desc => "number of times to blink", requires => 'color' },
    },
) ;

my $bs = Device::BlinkStick->new(
    verbose => $opt{verbose},
    inverse => $opt{inverse},
) ;

my $stick ;
my $devices = $bs->devices() ;
if ( $opt{serial} || $opt{name} || $opt{device} ) {
    # decide which parameter to use
    my $name = $opt{serial} || $opt{name} || $opt{device} ;
    # do we have it
    $stick = $bs->find($name) ;

    if ( !$stick ) {
        msg_exit( "A matching device could not be found", 1 ) ;
    }
}
if ( $opt{info} ) {
    if ($stick) {
        show_info($stick) ;
    } else {
        foreach my $s ( sort keys %$devices ) {
            show_info( $devices->{$s} ) ;
        }
    }
    exit ;
}

# set a default stick
$stick = $bs->first() if ( !$stick ) ;

if ( !$stick ) {
    msg_exit( "Could not find any Blinkstick devices", 1 ) ;
}

if ( $opt{mode} ) {
    if ( $stick->type eq 'pro' ) {
        $stick->set_mode( $opt{mode} ) ;
    } elsif ( $opt{mode} == MODE_INVERSE ) {
        $stick->inverse(1) ;
    } else {
        say STDERR "Mode note suitable for device" ;
    }
}

if ( $opt{set_leds} ) {
    if ( $stick->type ne 'pro' ) {
        say STDERR "Cannot set number of LEDs on this type of device" ;
    } else {
        $action++ ;
        # set the number of leds connected to this device
        $stick->set_leds( $opt{set_leds} ) ;
    }
}

if ( $opt{set_name} ) {
    $action++ ;
    $stick->set_device_name( $opt{set_name} ) ;
}
if ( $opt{set_token} ) {
    $action++ ;
    $stick->set_access_token( $opt{set_token} ) ;
}

if ( $opt{brightness} ) {
    $stick->brightness( $opt{brightness} ) ;
}

if ( $opt{color} ) {
    $action++ ;

    $opt{index}-- if ( $opt{index} ) ;    # e.g. 0..7
    if ( $opt{blink} ) {
        $stick->blink(
            color   => $opt{color},
            times   => $opt{blink},
            delay   => $opt{delay},
            channel => $opt{channel},
            index   => $opt{index}
        ) ;
    } else {
        $stick->led(
            color   => $opt{color},
            channel => $opt{channel},
            index   => $opt{index}
        ) ;
    }
}

if ( !$action ) {
    show_usage("No useful parameters passed") ;
}