#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use lib '../module/blib/lib';
use lib '../module/blib/arch';
use lib '../module/lib';

use HiPi::Energenie;
use HiPi::Utils::Config;
use Getopt::Long qw( GetOptionsFromArray );

our $VERSION ='0.61';

my @commandargs = @ARGV;

$commandargs[0] = 'missing' unless @commandargs;

my $command = shift @commandargs;

if( $command !~ /^help|version|config|group|switch|pair/) {
    display_usage('unknown command : ' . $command);
}

my $commandsub = qq(command_$command);
main->$commandsub;

sub display_usage {
    my $error = shift;
    my $msg = q(
  usage : hipi-energenie <command> [options]
  
  command :
    help        Print this message
    version     Print the version
    config      Configure the board type ( ENER314_RT or ENER314 )
    switch      Switch a socket or switch on or off
    pair        Pair a socket or switch
    group       Manage groups for use with sockets
    adaptor     Switch an adaptor device on or off
    monitor     Query a monitoring device for values
    join        Configure a monitor or adaptor

  For help on each command use:
    hipi-energenie <command> -h

);
    
    if( $error ) {
        print STDERR qq(ERROR : $error\n);
        print STDERR $msg;
    } else {
        print $msg;
    }
}

sub display_usage_for_config {
    my $error = shift;
    my $msg = q(
  usage : hipi-energenie config <options>
  
  options :

    --help        -h  Display this message

    --list        -l  List the current config

    --board       -b  < ENER314 | ENER314_RT > Set the board type that
                      you have connected to the Raspberry Pi.
                      Default is 'ENER314_RT'

    --devicename  -d  < devicename > Set the SPI device used by the
                      ENER314_RT board. Default is '/dev/spidev0.1'
    
    --copy        -c  Copy configuration from one user to another.
                      Options --from and --to must also be specified.
                      Can only work when called as root or using sudo.

    --from        -f  User to copy config from (e.g. 'pi' or 'root')

    --to          -t  User to copy config to (e.g. 'pi' or 'root')

);
    
    if( $error ) {
        print STDERR qq(\nERROR : $error\n);
        print STDERR $msg;
    } else {
        print $msg;
    }
}

sub display_usage_for_group {
    my $error = shift;
    my $msg = q(
  usage : hipi-energenie group <options>
    
  options :

    --help        -h  Display this message

    --list        -l  List the currently configured groups

    --create      -c  Create a new group. Must be accompanied by
                      options for --name or --group or both. If
                      --name is supplied and not --group, then
                      the system will create a new unused group id
                      and associate the supplied name with it. If
                      --group is supplied and not --name then the
                      system will store the --group without a friendly
                      name. If both --name and --group are supplied,
                      the new group specified will be stored with the
                      associated name supplied. It is expected that
                      common usage will be to supply --name and let
                      the system assign a new unused group id.
                      e.g. hipi-energenie group -c -n 'my group 1'

    --delete      -d  Delete an existing group. Must be accompanied
                      by options for --name or --group to identify
                      the group you wish to delete.
                      e.g. hipi-energenie group -d -n 'my group 1'
                      
    --rename      -r  Rename an existing group. Must be accompanied
                      by options for both --group and --name. The
                      --group option identifies the group you wish to
                      rename with the new name in --name.
                      e.g. hipi-energenie group -r -g 0x6def -n 'my group 2'

    --group       -g  <groupid> The group id identifier that is used
                      to control a group of four switches or sockets,
                      or one 4 way gang extension. This is a number
                      between 0x01 and 0xFFFFF. The parameter can be
                      passed in decimal, hexadecimal or binary
                      notation. It is parsed by the Perl 'oct'
                      function.
    
    --name        -n  The friendly name of a group

);
    
    if( $error ) {
        print STDERR qq(\nERROR : $error\n);
        print STDERR $msg;
    } else {
        print $msg;
    }
}

sub command_help {
    display_usage();
}

sub command_version {
    say qq(hipi-energenie version $VERSION);
}

sub command_config {
    my $opt = {
        devicename => undef,
        board      => undef,
        list       => 0,
        help       => 0,
    };
    
    GetOptionsFromArray(
        \@commandargs, $opt,
        'help|h!',
        'list|l!',
        'devicename|d:s',
        'board|b:s',
        'copy|c!',
        'from|f:s',
        'to|t:s',
    );
    
    if( $opt->{help} ) {
        display_usage_for_config();
        return;
    } elsif( $opt->{list} ) {
        list_current_config();
        return;
    } else {
        my $config = get_configuration();
        my $conf = $config->config;
        my $effuser =  getpwuid($>);
        my( $newdevice, $newboard );
    
        if ( $opt->{devicename} ) {
            $newdevice = $opt->{devicename};
        }
    
        if( $opt->{board} ) {
            if( my ($backend) = ( $opt->{board} =~ /^(ENER314|ENER314_RT)$/i ) ) {
                $newboard = uc($backend);
            } else {
                ( $newdevice, $newboard ) = ( undef, undef );
                display_usage_for_config(qq(Invalid board $opt->{board} sepcified));
            }
        }
        
        if( $newdevice ) {
            $conf->{devicename} = $newdevice;
            print qq(Device name for SPI transceiver board ENER314_RT set to $conf->{devicename} for user '$effuser'\n);
        }
        
        if( $newboard ) {
            $conf->{backend} = $newboard;
            print qq(Energenie board set to $conf->{backend} for user '$effuser'\n)
        }
    }
    return;
}

sub command_group {
    my $opt = {};
    GetOptionsFromArray(
        \@commandargs, $opt,
        'help|h!',
        'create|c!',
        'delete|d!',
        'rename|r!',
        'group|g:o',
        'name|n:s',
        'list|l!',
    );
    
    if( $opt->{help} ) {
        display_usage_for_group();
        return;
    }
    
    my $config = get_configuration();
    my $conf = $config->config;
    my $effuser =  getpwuid($>);
    
    if ( $opt->{list} ) {
        list_groups( $conf );
        return;
    } elsif ( $opt->{create} ) {
        my $group = $opt->{group};
        my $gname = $opt->{name};
        unless( $group || $gname ) {
            display_usage_for_group(qq(You must provide options --name or --group or both to create a new group));
            return;
        }
        
        if( $group ) {
            if(exists($conf->{groups}->{$group})) {
                my $groupid   = sprintf('0x%05x', $group);
                print STDERR qq(The group id $groupid already exists\n);
                return;
            }
        }
        
        my $newgroup = $group;
        
        while(! $newgroup ) {
            $group = generate_switch_group();
            unless(exists($conf->{groups}->{$group})) {
                $newgroup = $group;
            }
        }
        
        if( $gname ) {
            for my $existing( keys %{ $conf->{groups} } ) {
                if(lc($conf->{groups}->{$existing}) eq lc($gname) ) {
                    my $groupid   = format_group($existing);
                    print STDERR qq(The name '$gname' is already in use for group $groupid\n);
                    return;
                }
            }
        } else {
            $gname = format_group($newgroup);
        }
        
        $conf->{groups}->{$newgroup} = $gname;
        my $fgroup = format_group($newgroup);
        print qq(Group named '$gname' created for group id $fgroup\n);
        return;
    } elsif ( $opt->{delete} ) {
        my $group = $opt->{group};
        my $gname = $opt->{name};
        unless( $group || $gname ) {
            display_usage_for_group(qq(You must provide options --name or --group to delete an existing group));
            return;
        }
        
        if( $group && $gname ) {
            display_usage_for_group(qq(You cannot provide both options --name and --group when deleting an existing group));
            return;
        }
        
        if( $gname ) {
            for my $group( keys %{ $conf->{groups} } ) {
                if(lc($conf->{groups}->{$group}) eq lc($gname) ) {
                    delete($conf->{groups}->{$group});
                    my $groupid = format_group($group);
                    print qq(Group named '$gname' with id $groupid has been deleted\n);
                    return;
                }
            }
            print STDERR qq(A group with name '$gname' was not found\n);
            return;
        }
        
        if( $group ) {
            my $groupid = format_group($group);
            if(exists($conf->{groups}->{$group})) {
                my $gname = $conf->{groups}->{$group};
                delete($conf->{groups}->{$group});
                print qq(Group named '$gname' with id $groupid has been deleted\n);
                return;
            } else {
                print STDERR qq(A group with id $groupid was not found\n);
                return;
            }
        }
    } elsif( $opt->{rename} ) {
        my $group = $opt->{group};
        my $gname = $opt->{name};
        unless( $group && $gname ) {
            display_usage_for_group(qq(You must provide both options --name and --group to rename an existing group));
            return;
        }
        
        for my $existing( keys %{ $conf->{groups} } ) {
            if(lc($conf->{groups}->{$existing}) eq lc($gname) ) {
                my $groupid   = format_group($existing);
                print STDERR qq(The name '$gname' is already in use for group $groupid\n);
                return;
            }
        }
        
        my $groupid = format_group( $group );
        
        if( exists($conf->{groups}->{$group})) {
            $conf->{groups}->{$group} = $gname;
            print qq(Group id $groupid renamed as '$gname' for user '$effuser'\n);
        } else {
            print STDERR qq(Group id $groupid is not configured\n);
        }
        
        return;
        
    } else {
        display_usage_for_group(qq(You must specify one of --create --delete --rename --list --help));
    }
}

sub make_default_config {
    
    my $conf = {
        version    => $VERSION,
        backend    => 'ENER314_RT',
        devicename => '/dev/spidev0.1',
        groups     => {}, 
        adaptors   => {},
        monitors   => {},
        switches   => {},
    };
    
    return $conf;
}

sub list_current_config {
    my $conf = get_configuration()->config;
    my $effuser =  getpwuid($>);
    my $msg = qq(HiPi Energenie configuration for user '$effuser'\n\n);
    # main
    $msg .= qq(  HiPi Energenie Version  : $VERSION\n);
    $msg .= qq(  Energenie Board         : $conf->{backend}\n);
    if(  $conf->{backend} eq 'ENER314_RT' ) {
        $msg .= qq(  SPI Device              : $conf->{devicename}\n);
    }
    $msg .= qq(\n);
    
    print $msg;
}

sub list_groups {
    my $conf = shift;
    my $effuser =  getpwuid($>);
    my @groups= ( keys %{ $conf->{groups} } );
    if( @groups ) {
        print qq(Groups configured for user '$effuser'\n\n);
        print qq(  Group Name                  Group Id\n);
        print qq(  ------------------------------------\n);
        for my $group ( sort { $a <=> $b } @groups ) {
            my $groupname = sprintf("%-27s", $conf->{groups}->{$group});
            my $groupid   = sprintf('0x%05X', $group);
            print qq(  $groupname $groupid\n);
        }
    } else {
        print qq(There are no groups configured for user '$effuser'\n);
    }
}

sub get_configuration {
    my $conf = HiPi::Utils::Config->new(
        configclass => 'scripts/energenie',
        default     => make_default_config(),
    );
    return $conf;
}

sub generate_switch_group {
    # a number between 0x1 and 0xFFFFF
    my $group = 1 + int(rand(0xFFFFE));
    return $group;
}

sub format_group {
    my $group = shift;
    return sprintf('0x%05X', $group);
}

1;

__END__
