#! /usr/bin/env perl

# take persistent storage created by mapSwitches.pl and dump entries
# to a tab delimited text file on STDOUT.

# v1.2 RWR 20200228
# added description field
# v3.0.0 RWR 20230325
# rewrite to use new data file format

use strict;
use warnings;

use File::Spec; # find location of script so we can put storage in same directory
use File::Basename;
use YAML::Tiny; # apt-get libyaml-tiny-perl under debian
use POSIX qw( strftime ); # to convert timestamp to something readable
use Data::Dumper; # only used for debugging

our $VERSION = '3.0.0';

# where the script is located
my $scriptDir = dirname( File::Spec->rel2abs( __FILE__ ) );
# put the statefile in there
my $STATEFILE = $scriptDir . '/mapSwitches.yaml';

# see https://perldoc.perl.org/Getopt/Long.html
use Getopt::Long;
# allow -vvn (ie, --verbose --verbose --dryrun)
Getopt::Long::Configure ("bundling");



my %switchports;

# pretty up the MAC addresses by inserting colons between
# every pair of numbers
sub prettyMAC {
   my $mac = shift;
   my @mac = ( $mac =~ m/../g );
   return join( ':', @mac );
}

# change the Unix timestamp into yyyy-mm-dd hh:mm:ss
sub prettyTime {
   my $time = shift;
   return '' unless $time;
   return strftime '%Y-%m-%d %H:%M:%S', localtime( $time );
}

sub dumpLine {
   my $delimiter = shift;
   my $encapsulate = shift;
   # read in values, defaulting to empty string if undef
   my @values = map{ $_ ? $_ : '' } @_;
   my $return;
   @values = ( map{ $encapsulate . $_ . $encapsulate } @values ) if $encapsulate;
   return join( $delimiter, @values ) . "\n";
}

# sorts by first column (switch name), second column (switch port number)
# then third column (mac address )
# NOTE: this is not csv safe; only works for tab delimited
sub sortArray {
   my @a = split( "\t", $a );
   my @b = split( "\t", $b );
   return $a[0] cmp $b[0] || $a[1] <=> $b[1] || $a[4] cmp $b[4];
}


# simple display if --help is passed
sub help {
   use File::Basename;
   print basename($0) . " $VERSION\n";
   print <<END
$0 [options]
Reads mapSwitches.yaml and emits the result as a tab delimited text file, with headers.
Output is sorted by switch name, switch port number, then mac address.
Options:
   --version        - display version and exit
   --help           - This page
END
}

# handle any command line parameters that may have been passed in
my $version = 0; # just used to determine if we should display the version
my $help = 0; # also if we want help
GetOptions (
            'help|h'        => \$help,
            'version|v'     => \$version,
            ) or die "Error parsing command line\n";

                  
if ( $help ) { &help() ; exit; }
if ( $version ) { use File::Basename; print basename($0) . " $VERSION\n"; exit; }


# read the saved state into memory if it exists
die "no state file found, looking at $STATEFILE\n" unless -e $STATEFILE;

# get the delimiter and the encapsulation chars
my $delimiter = shift;
my $encapsulate = shift;
# default to tab delimited, no encapsulation if none entered
$delimiter = "\t" unless $delimiter;
$encapsulate = '' unless $encapsulate;

my $yaml = YAML::Tiny->read( $STATEFILE );

my $saveFile = $yaml->[0];
#print Dumper( $saveFile ); die;
# print Dumper( $saveFile->{'macList'} );

my @out;

foreach my $mac ( keys %{ $saveFile->{'macList'} } ) {
   my $switchName = $saveFile->{'switchInfo'}->{$saveFile->{'macList'}->{$mac}->{'switch'}}->{'name'};
   my $portAlias =  $saveFile->{'switchInfo'}->{$saveFile->{'macList'}->{$mac}->{'switch'}}->{'ports'}->{$saveFile->{'macList'}->{$mac}->{'port'}}->{'alias'};
   push @out, &dumpLine( 
      $delimiter, 
      $encapsulate, 
      $saveFile->{'macList'}->{$mac}->{'switch'}, 
      $saveFile->{'macList'}->{$mac}->{'port'},
      $switchName,
      $portAlias,
      &prettyMAC( $mac ),
      $saveFile->{'macList'}->{$mac}->{'ip'},
      $saveFile->{'macList'}->{$mac}->{'hostname'},
      &prettyTime( $saveFile->{'macList'}->{$mac}->{'lastseen'} )
   );
}

print &dumpLine(
   $delimiter, 
   $encapsulate, 
   'switch','port','alias','description','mac','ip','host_name','last_seen'
   );

print sort sortArray @out;

1;
