package POE::Component::IRC::Plugin::RTorrentStatus;

use strict;
use warnings;
use Carp qw(croak);
use DateTime;
use DateTime::Duration;
use DateTime::Format::Human::Duration;
use File::Basename qw(fileparse);
use Format::Human::Bytes;
use POE::Component::IRC::Plugin qw(PCI_EAT_NONE);
use POE::Component::IRC::Common qw(NORMAL DARK_GREEN DARK_BLUE ORANGE TEAL);
use POE::Component::IRC::Plugin::FollowTail;

our $VERSION = '0.01';

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

    if (!defined $self->{Torrent_log} || !-e $self->{Torrent_log}) {
        croak __PACKAGE__ . ": the specified torrent log doesn't exist";
    }

    if (ref $self->{Channels} ne 'ARRAY' || !$self->{Channels}) {
        croak __PACKAGE__ . ': No channels defined';
    }

    # defaults
    $self->{Method} = 'notice' if !defined $self->{Method};
    $self->{Color} = 1 if !defined $self->{Color};

    return $self;
}

sub PCI_register {
    my ($self, $irc) = @_;

    $irc->plugin_add(TorrentTail => POE::Component::IRC::Plugin::FollowTail->new(
        filename => $self->{Torrent_log},
    ));

    $irc->plugin_register($self, 'SERVER', qw(tail_input));
    return 1;
}

sub PCI_unregister {
    my ($self, $irc) = @_;
    return 1;
}

sub S_tail_input {
    my ($self, $irc) = splice @_, 0, 2;
    my $filename = ${ $_[0] };
    my $input    = ${ $_[1] };
    return if $filename ne $self->{Torrent_log};

    my ($action, $args) = split /,/, $input, 2;
    my $method = "_${action}_torrent";
    my $msg = $self->$method($args);
    
    if (defined $msg) {
        for my $chan (@{ $self->{Channels} }) {
            $irc->yield($self->{Method}, $chan, $msg);
        }
    }

    return PCI_EAT_NONE;
}

sub _inserted_new_torrent {
    my ($self, $args) = @_;
    my (undef, $hash, $bytes, undef, $file) = split /,/, $args, 5;
    return if !defined $hash || !defined $bytes || !defined $file;

    my $name = fileparse($file);
    $name =~ s/\.torrent$//;
    my $user = getpwuid((stat $file)[4]);
    my $size = _fmt_bytes($bytes);

    my $msg = $self->{Color}
        ? DARK_BLUE.'Enqueued: '.ORANGE.$name.NORMAL." ($size, by $user)"
        : "Enqueued: $name ($size, by $user)";

    return $msg;
}

sub _finished_torrent {
    my ($self, $args) = @_;
    my ($finished, $hash, $bytes, $rars, $file) = split /,/, $args, 5;
    return if !defined $finished || !defined $hash || !defined $bytes || !defined $rars || !defined $file;

    my $enqueued = (stat $file)[10];
    my $duration = _duration($enqueued, $finished);
    my $name = fileparse($file);
    $name =~ s/\.torrent$//;

    my $secs = $finished - $enqueued;
    # Sometimes it's that fast, avoid division by zero
    $secs = 1 if $secs == 0;

    my $bps = $bytes / $secs;
    my $size = _fmt_bytes($bps);
    my $rate = "$size/s";

    my $msg = $self->{Color}
        ? DARK_GREEN.'Finished: '.ORANGE.$name.NORMAL." in $duration ($rate)"
        : "Finished: $name in $duration ($rate)";
    my $archives = $rars > 1 ? 'archives' : 'archive';
    $msg .= "; $rars $archives to unrar" if $rars;

    return $msg;
}

sub _unrar_torrent {
    my ($self, $args) = @_;
    my ($start, $finish, $rars, $file) = split /,/, $args, 5;
    return if !defined $start || !defined $finish || !defined $rars || !defined $file;

    my $duration = _duration($start, $finish);
    my $name = fileparse($file);
    $name =~ s/\.torrent$//;

    my $archives = $rars > 1 ? 'archives' : 'archive';
    my $msg = $self->{Color}
        ? TEAL.'Unrared: '.ORANGE.$name.NORMAL." in $duration ($rars $archives)"
        : "Unrared: $name in $duration ($rars $archives)";

    return $msg;
}

sub _duration {
    my ($start, $finish) = @_;

    my $enq_date = DateTime->from_epoch(epoch => $start);
    my $fin_date = DateTime->from_epoch(epoch => $finish);
    my $dur_obj = $fin_date - $enq_date;
    my $span = DateTime::Format::Human::Duration->new();
    return $span->format_duration($dur_obj);
}

sub _fmt_bytes { return Format::Human::Bytes::base2($_[0]) }

1;

=encoding utf8

=head1 NAME

POE::Component::IRC::Plugin::RTorrentStatus - A PoCo-IRC plugin which prints
RTorrent status messages to IRC

=head1 SYNOPSIS

 use POE::Component::IRC::Plugin::RTorrentStatus;

 # post status updates to #foobar
 $irc->plugin_add(Torrent => POE::Component::IRC::Plugin::RTorrentStatus->new(
     Torrent_log => '/tmp/torrentlog',
     Channels    => ['#foobar'],
 ));

=head1 DESCRIPTION

POE::Component::IRC::Plugin::RTorrentStatus is a
L<POE::Component::IRC|POE::Component::IRC> plugin. It reads a log file
generated by the included L<irctor-queue> program and posts messages to
IRC describing the events. See the documentation for L<irctor-queue>
on how to set it up with RTorrent.

=head1 METHODS

=head2 C<new>

Takes the following arguments:

B<'Torrent_log'>, the path to the torrent log file generated by the
L<irctor-queue> program. This argument is required.

B<'Channels'>, an array reference of channels to post messages to. You must
specify at least one channel.

B<'Color'>, whether to print colorful status messages. True by default.

B<'Method'>, how you want messages to be delivered. Valid options are
'notice' (the default) and 'privmsg'.

Returns a plugin object suitable for feeding to
L<POE::Component::IRC|POE::Component::IRC>'s C<plugin_add> method.

=head1 TODO

Handle RTorrent restarts. Currently the torrent name will be replaced with
the torrent hash after a restart.

Handle Magnet links. RTorrent doesn't support them yet, but when it does,
there won't be a torrent file laying around for us to look up.

=head1 AUTHOR

Hinrik E<Ouml>rn SigurE<eth>sson, hinrik.sig@gmail.com

=head1 LICENSE AND COPYRIGHT

Copyright 2010 Hinrik E<Ouml>rn SigurE<eth>sson

This program is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
