package MediaServe::SFTP;

use strict;
use warnings;

use Net::uFTP;
use Net::SFTP;

#use Net::SFTP::Constants qw( SSH2_FILEXFER_ATTR_PERMISSIONS );
#use Net::SFTP::Attributes;
use File::Basename;

use lib '/app/tools/common/lib';
use Common::RSApp;
use Common::Util;
use Common::Assert;

# ----> How to use this class <----
#
# Use SFTP to transfer files between a computer and a remote host. All parameters
# to the constructor are optional.
#
# my $sftp = new MediaServe::SFTP
# (
#   user     => 'uname',
#   password => 'password',
#   identity => '~/.ssh/id_dsa',
#   debug    => 1
# );
#
# $sftp->connect( "remote.host.com" );
# $sftp->put( "srcfile", "destfile" );
# $sftp->get( "srcfile", "destfile" );
#

sub new {
    my ( $class, %args ) = @_;
    my $self = bless {}, $class;

    return $self->_init(%args);
}

sub _init {
    my ( $self, %args ) = @_;
    $self->{_user}     = $args{user};
    $self->{_password} = $args{password};
    $self->{_identity} = $args{identity};
    $self->{_debug}    = $args{debug};

    if ( $args{host} ) {
        $self->connect( $args{host} );
    }

    return $self;
}

#
# $sftp->get_file_type( '/path/to/file' )
#
# Return the first byte of the long file listing.  So directory will return 'd'
# a file will return '-' etc.
#
sub get_file_type {
    my $self = shift;
    my $path = shift;
    my $sftp = $self->{_sftp};

    assert($self);
    assert($sftp);
    assert($path);

    my ( $dir, $file );

    $dir  = dirname($path);
    $file = basename($path);

    assert( $dir && $file );

    my $files = $self->{_sftp}->ls($dir);

    foreach (@$files) {
        return substr( $_->{longname}, 0, 1 )
          if ( $_->{filename} eq $file );
    }

    # File not found, return nothing;
    return undef;
}

#
# $sftp->connect( 'somehost.name.com' )
#
# Connect to a remote host and cache the connection for later use.  This might
# only reconnects if the hostname has changed.
#
sub connect {
    my $self   = shift;
    my $host   = shift;
    my $sftp   = $self->{_sftp};
    my $uname  = $self->{_user};
    my $passwd = $self->{_password};

    assert($self);
    assert($host);
    assert($uname);
    assert($passwd);

    if ( !$sftp || !$sftp->{host} || !$sftp->{host} eq $host ) {
        Common::Log::Debug("   + Connecting to host: $host");

        ###
        # Net::SFTP
        ###

        #$sftp = new Net::SFTP( $host,
        #                       user     => $self->{_user},
        #                       password => $self->{_password},
        #                       debug    => $self->{_verbose},
        #                       ssh_args => { identity_file => [ $self->{_identity} ] } );

        ###
        # Net::xFTP
        ###

        #$sftp = new Net::xFTP( 'SFTP', $host, debug => $self->{_debug},
        #                       user => $self->{_user}, password => $self->{_password} ) ||
        #    die "Can not connect to host: $@";

        ###
        # Net::uFTP
        ###

        # Disable warnings because /usr/local/lib/perl/5.10.0/File/Spec/Unix.pm is throwing
        # uninitialized warnings
        $^W = 0;

        $sftp = new Net::uFTP( $host, port => 22, type => 'SFTP', debug => $self->{_debug} )
          || die "Can not connect to host: $@";
        $sftp->login( $uname, $passwd )
          || die "Can not login: ";    # . $sftp->message;
        $sftp->binary();
        $sftp->change_root('/tmp');

        $sftp->change_root('/');
        my @dir = $sftp->ls();

    }

    $self->{_sftp} = $sftp;
}

sub DESTROY {
    my $self = shift;
    $self->close();
}

sub close {
    my $self = shift;
    $self->{_sftp} = undef;
}

#
# $sftp->put( '/src/bar.txt', '/dest/out.txt' );
#
# Passthrough to the Net::SFTP put method to put a file on a remote host.  You
# must have called the $sftp->connect() method prior to calling this method.
#
sub put {
    my $self = shift;
    my $src  = shift;
    my $dest = shift;

    assert($self);
    assert($src);
    assert($dest);
    assert( $self->{_sftp} );

    $self->mkdir( dirname($dest) )
      if ( $dest =~ /\// );

    return $self->{_sftp}->put( $src, $dest );
}

#
# $sftp->get( '/src/bar.txt', '/dest/out.txt' );
#
# Passthrough to the Net::SFTP get method to pull a file from the remote host.  You
# must have called the $sftp->connect() method prior to calling this method.
#
sub get {
    my $self = shift;
    my $src  = shift;
    my $dest = shift;

    assert($self);
    assert($src);
    assert($dest);
    assert( $self->{_sftp} );

    return $self->{_sftp}->get( $src, $dest );
}

#
# $sftp->mkdir( '/foo/bar' );
#
# Recursivly build a directory on a remote host
#
sub mkdir {
    my $self = shift;
    my $path = shift;
    my $sftp = $self->{_sftp};
    my $status;
    my $cpwd;
    my $file_type;

    my $recursive = 1;

    assert($self);
    assert($sftp);

    return unless ( $path && $path ne '/' && $path ne '.' );

    Common::Log::Debug("   -  Building $path if needed");

    # Net::xFTP mkdir is already recursive unlike Net::SFTP
    return $self->{_sftp}->mkdir( dirname($path), $recursive );

    # Recursivly call until we get to the root
    $self->mkdir( dirname($path) );

    $file_type = $self->get_file_type($path);

    if ( !$file_type ) {
        Common::Log::Debug("   -  mkdir $path");
        $sftp->do_mkdir( $path, Net::SFTP::Attributes->new() );
    } elsif ( $file_type eq 'd' || $file_type eq 'l' ) {

        #Common::Log::Debug( "   -  $path is a directory or a link already" );
    } else {
        print STDERR "Can not create director $path because a file already exists";
    }
}

1;
