#!/usr/bin/env perl
#
# This file is part of StorageDisplay
#
# This software is copyright (c) 2020 by Vincent Danjean.
#
# This is free software; you can redistribute it and/or modify it under
# the same terms as the Perl 5 programming language system itself.
#

# PODNAME: storage2dot
# ABSTRACT: analyse and generate a graphical view of a machine storage

use strict;
use warnings;

our $VERSION = '1.0.1'; # VERSION

use StorageDisplay;
use StorageDisplay::Collect;

sub collect_from_remote {
    my $remote = shift;
    my $content='';
    use Net::OpenSSH;
    use Term::ReadKey;
    END {
        ReadMode('normal');
    }
    my $ssh = Net::OpenSSH->new($remote);
    $ssh->error and
        die "Couldn't establish SSH connection: ". $ssh->error;

    my ($in, $out, $pid) = $ssh->open2(
        #'cat',
        'perl', '--', '-',
        );

    my $fdperlmod;
    open($fdperlmod, '<', $INC{'StorageDisplay/Collect.pm'})
        or die "Cannot open ".INC{'StorageDisplay/Collect.pm'}.": $!\n";
    #use Sys::Syscall;
    #Sys::Syscall::sendfile($in, $fdperlmod);
    {
        while(defined(my $line=<$fdperlmod>)) {
            print $in $line;
        }
        close $fdperlmod;
    }
    #print $in "StorageDisplay::Collect::dump_collect;\n";
    my @args = (@_, 'LocalBySSH');
    my $cmd = "StorageDisplay::Collect::dump_collect('".join("','", @args)."');\n";
    print STDERR 'Running through SSH: ',$cmd;
    print $in $cmd;
    print $in "__END__\n";
    flush $in;

    use IO::Select;
    use POSIX ":sys_wait_h";
    my $sel = IO::Select->new(\*STDIN, $out);
    my $timeout = 1;
    ReadMode('noecho');
    my ($in_closed,$out_closed) = (0,0);
    while(1) {
        $!=0;
        my @ready = $sel->can_read($timeout);
        if ($!) {
            die "Error with select: $!\n";
        }
        if (scalar(@ready)) {
            foreach my $fd (@ready) {
                if ($fd == $out) {
                    my $line=<$out>;
                    if (defined($line)) {
                        $content .= $line;
                    } else {
                        $sel->remove($out);
                        close $out;
                        $out_closed=1;
                    }
                } else {
                    my $line=<STDIN>;
                    if (print $in $line) {
                        flush $in;
                    } else {
                        $sel->remove(\*STDIN);
                        close $in;
                        $in_closed=1;
                    }
                }
            }
        } else {
            my $res = waitpid($pid, WNOHANG);
            if ($res==-1) {
                die "Some error occurred ".($? >> 8).": $!\n";
            }
            if ($res) {
                if (!$in_closed) {
                    $sel->remove(\*STDIN);
                    close $in;
                }
                ReadMode('normal');
                last;
            }
            #print STDERR "timeout for $pid\n";
        }
    }
    if (!$out_closed) {
        while (defined(my $line=<$out>)) {
            $content .= $out;
        }
        $sel->remove($out);
        close $out;
    }
    return $content;
}

use Getopt::Long;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Purity = 1;

#use Carp::Always;

my $remote;
my $data;
my $output;

my $collect;
my $recordfile;
my $replayfile;

my $verbose;
my $help;

GetOptions ("d|data=s"       => \$data,      # string
            "r|remote=s"     => \$remote,  # string
            "o|output=s"     => \$output,    # string
            "c|collect-only" => \$collect,    # flag
            "record-file=s"       => \$recordfile,  # string
            "replay-file=s"       => \$replayfile,  # string
            "verbose"        => \$verbose,     # flag
            "help"           => \$help,     # flag
    ) or die("Error in command line arguments\n");

sub main() {

    if (defined($data) && (
            defined($remote)
            || defined($recordfile)
            || defined($replayfile)
        )) {
        die "E: --data cannot be used with --remote, --record, nor --replay\n";
    }

    if (defined($replayfile) && (
            defined($remote)
            || defined($recordfile)
        )) {
        die "E: --replay cannot be used with --remote, nor --record\n";
    }

    my $infos;

    if ($replayfile) {
        my $dh;
        open($dh, "<", $replayfile)
            or die "Cannot open '$replayfile': $!" ;
        my $replay=join('', <$dh>);
        my $replaydata;
        close($dh);
        {
            my $VAR1;
            eval($replay); ## no critic (ProhibitStringyEval)
            #print STDERR "c: $content\n";
            $replaydata = $VAR1;
        }
        $infos = StorageDisplay::Collect->new(
            'Replay', 'replay-data' => $replaydata)->collect();
    }

    my $contents;
    my @recorder;
    if (defined($recordfile)) {
        @recorder = ('Proxy::Recorder', 'recorder-reader');
    }
    if (defined($data)) {
        my $dh;
        open($dh, "<", $data)
            or die "Cannot open '$data': $!" ;
        $contents=join('', <$dh>);
        close($dh);
    } elsif (defined($remote)) {
        $contents = collect_from_remote($remote, @recorder);
    } elsif (not defined($infos)) {
        $infos = StorageDisplay::Collect->new(@recorder, 'Local')->collect();
    }

    # data are in $contents (if got through Data::Dumper) or directly in $infos
    if (defined($contents)) {
        # moving data from $contents to $infos
        {
            my $VAR1;
            eval($contents); ## no critic (ProhibitStringyEval)
            #print STDERR "c: $content\n";
            $infos = $VAR1;
        }
    }

    if (defined($recordfile)) {
        if (! exists($infos->{'recorder'})) {
            print STDERR "W: skpping recording: no records!\n";
        } else {
            my $dh;
            open($dh, ">", $recordfile)
                or die "Cannot open '$data': $!";
            print $dh Dumper($infos->{'recorder'});
            close($dh);
        }
    }
    delete($infos->{'recorder'});

    my $oldout;
    if (defined($output)) {
        open(my $oldout, ">&STDOUT")     or die "Can't dup STDOUT: $!";
        open(STDOUT, '>', $output) or die "Can't redirect STDOUT to $output: $!";
    }

    if ($collect) {
        print Dumper($infos);
        return;
    }
    my $st=StorageDisplay->new('infos' => $infos);

    $st->createElems();
    $st->display;
}

main

__END__

=pod

=encoding UTF-8

=head1 NAME

storage2dot - analyse and generate a graphical view of a machine storage

=head1 VERSION

version 1.0.1

=head1 AUTHOR

Vincent Danjean <Vincent.Danjean@ens-lyon.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2020 by Vincent Danjean.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
