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

=head1 NAME

stick

=head1 SYNOPSIS

    > stick ..options..

    to get full help use
    > stick --help   

=head1 DESCRIPTION

Description of what your application does

=head1 TODO

--set-mode (for pro 0 default, 1 inverse, 2 ws2812)

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 error

=cut

#
# (c) yourname, your@email.address.com
# this code is released under the Perl Artistic License

use 5.10.0 ;
use strict ;
use warnings ;
use App::Basis ;
use Device::BlinkStick ;
use WebColors ;
use Time::HiRes qw(usleep) ;
use Data::Printer ;

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

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

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

sub show_info
{
    my $stick = shift ;
    my $info  = $stick->info() ;
    my ( $r, $g, $b ) = @{ $info->{color} } ;
    my $color ;
    $color = rgb_to_colorname( $r, $g, $b ) ;
    if ( !$color ) {
        $color = sprintf( "#%02X%02X%02X", $r, $g, $b ) ;
    }

    # 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:  $color" ;
    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} ) {
    foreach my $f (qw( name serial device)) {
        $opt{$f} = lc( $opt{$f} ) if ( $opt{$f} ) ;
    }
    if ( $opt{device} ) {
        $opt{serial} = $opt{device} ;
        $opt{name}   = $opt{device} ;
    }
    if ( $opt{serial} && $devices->{ $opt{serial} } ) {
        $stick = $devices->{ $opt{serial} } ;
    } else {
        foreach my $s ( keys %$devices ) {
            if ( lc( $devices->{$s}->device_name ) eq lc( $opt{name} ) ) {
                $stick = $devices->{$s} ;
            }
        }
    }
    if ( !$stick ) {
        msg_exit("A matching device could not be found") ;
    }
}
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 ( $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} ) {
    $action++ ;
    $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++ ;
    my $r = $opt{color} ;
    my ( $g, $b ) ;
    if ( defined && $r !~ /^\d+$/ ) {
        $r = lc($r) ;
        if ( $r eq 'off' ) {
            ( $r, $g, $b ) = ( 0, 0, 0 ) ;
        } elsif ( $r =~ /(random|rand|rnd)/i ) {
            ( $r, $g, $b )
                = ( int( rand(255) ), int( rand(255) ), int( rand(255) ) ) ;
        } else {
            # try to determine what it should be
            ( $r, $g, $b ) = to_rgb($r) ;
        }
    }

    $opt{index}-- if ( $opt{index} ) ;    # 0..7
    if ( $opt{blink} ) {
        for ( my $i = 0; $i < $opt{blink}; $i++ ) {
            $stick->set_color( 0, 0, 0, $opt{channel}, $opt{index} ) ;
            usleep( $opt{delay} * 1000 ) ;
            $stick->set_color( $r, $g, $b, $opt{channel}, $opt{index} ) ;
            usleep( $opt{delay} * 1000 ) ;
        }
    } else {
        $stick->set_color( $r, $g, $b, $opt{channel}, $opt{index} ) ;
    }
}

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