#!/usr/bin/perl
#
# This file is part of POE-Component-Client-SimpleFTP
#
# This software is copyright (c) 2014 by Apocalypse.
#
# This is free software; you can redistribute it and/or modify it under
# the same terms as the Perl 5 programming language system itself.
#
use strict; use warnings;
package PutClient;

# a simple client to put a file

sub POE::Component::Client::SimpleFTP::DEBUG () { 1 };

use MooseX::POE::SweetArgs;
use POE::Component::Client::SimpleFTP;

with qw(
	MooseX::Getopt
);

has hostname => (
	isa => 'Str',
	is => 'ro',
	required => 1,
);

has port => (
	isa => 'Int',
	is => 'ro',
	default => 21,
);

has usetls => (
	isa => 'Bool',
	is => 'ro',
	default => 0,
);

has username => (
	isa => 'Str',
	is => 'ro',
	required => 1,
);

has password => (
	isa => 'Str',
	is => 'ro',
	required => 1,
);

has file => (
	isa => 'Str',
	is => 'ro',
	required => 1,
);

has passive => (
	isa => 'Bool',
	is => 'ro',
	default => 1,
);

has local_addr => (
	isa => 'Str',
	is => 'ro',
	default => '0.0.0.0',
);

# our ftp object
has ftp => (
	traits => ['NoGetopt'],
	isa => 'POE::Component::Client::SimpleFTP',
	is => 'rw',
	weak_ref => 1,
	init_arg => undef,
);

# the file we are currently transferring
has filefh => (
	traits => ['NoGetopt'],
	isa => 'Ref',
	is => 'rw',
	init_arg => undef,
);

sub START {
	my $self = shift;

	$self->ftp( POE::Component::Client::SimpleFTP->new(
		remote_addr => $self->hostname,
		remote_port => $self->port,
		local_addr => $self->local_addr,
		username => $self->username,
		password => $self->password,
		( $self->passive ? ( connection_mode => 'passive' ) : ( connection_mode => 'active' ) ),
		( $self->usetls ? ( tls_cmd => 1, tls_data => 1 ) : () ),
	) );

	# now we just wait for the connection to succeed/fail
	return;
}

event _child => sub { return };

event connected => sub {
	my $self = shift;

	# do nothing hah

	return;
};

event connect_error => sub {
	my( $self, $code, $reply ) = @_;

	die "CONNECT error: $code $reply";

	return;
};

event login_error => sub {
	my( $self, $code, $reply ) = @_;

	die "LOGIN error: $code $reply";

	return;
};

event authenticated => sub {
	my $self = shift;

	# Okay, get the file!
	$self->ftp->yield( 'put', $self->file );

	return;
};

event put_error => sub {
	my( $self, $code, $reply, $path ) = @_;

	die "put error: $code $reply";

	return;
};

event put_connected => sub {
	my( $self, $path ) = @_;

	# okay, we can send the first block of data!
	if ( open( my $fh, '<', $self->file ) ) {
		$self->filefh( $fh );

		# send the first chunk
		$self->send_chunk;
	} else {
		die "put error: unable to open " . $self->file . ": $!";
	}

	return;
};

event put_flushed => sub {
	my( $self, $path ) = @_;

	# read the next chunk of data from the fh
	$self->send_chunk;

	return;
};

sub send_chunk {
	my $self = shift;

	my $buf;
	my $retval = read( $self->filefh, $buf, 1024 );
	if ( $retval ) {
		$self->ftp->yield( 'put_data', $buf );
	} elsif ( $retval == 0 ) {
		# all done with the file
		if ( close( $self->filefh ) ) {
			$self->ftp->yield( 'put_close' );
		} else {
			die "put error: unable to close: $!";
		}
	} else {
		# error reading file
		die "put error: unable to read: $!";
	}

	return;
}

event put => sub {
	my( $self, $code, $reply, $path ) = @_;

	# done with the file, we disconnect
	print "Successfully uploaded " . $self->file . "\n";
	$self->ftp->yield( 'quit' );

	return;
};

# run the client!
my $ftp = PutClient->new_with_options;
POE::Kernel->run;