#!/usr/bin/perl -w

# buffycli is Copyright 2008 Penny Leach <penny@mjollnir.org>

# This file is part of buffycli.

# buffycli is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# buffycli is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with buffycli.  If not, see <http://www.gnu.org/licenses/>.

=pod

=head1 NAME

  Buffycli - text mode alternative to Buffy. Monitors new and unread messages in a collection of mail folders.

=head1 DESCRIPTION

  Buffy is a program that displays a compact summary of your mail folders, and allows to invoke a command (usually a mail reader) on them.  It is written with the intent
  of being a handy everyday tool for people handling large volumes of mail.  For mutt users, this can be a nice  front-end  to  supplement  the  simple  built-in  folder
  browser when one has many folders to keep track of.

=head1 USAGE

  [#] to launch the mail reader     q:quit

  oc[C]:order by column             scall:show all columns
  hc[C]:hide column                 sc[C]:show column
  st: show totals row               ht: hide totals row

  sf[#[,#,#]]:always show folder(s) hf[#],#,#,#]]:always hide folder(s)
  sfall:show all folders with mail  usage:toggle and save usage display

  si[#]: set the mail folder poll interval in seconds
  oi:run offlineimap -o in the foreground
  oibg:run offlineimap in the background (and attempt to kill it on quit)
  oikill:kill the background offlineimap process

=head1 OPTIONS

  Buffycli has no startup options.
  Consult the running help or the man page for documentation.

=head1 AUTHORS

  Buffycli is written and maintained by Penny Leach <penny@mjollnir.org>


=cut

use strict;
use warnings;

use FindBin;
use lib  "$FindBin::Bin/../lib/";

use Buffy;
use Buffy::CLI;
use Pod::Usage;

our $VERSION = '0.2.1';

my $forceusage = 0;

my $offlineimap = '/usr/bin/offlineimap'; #  @todo - this is hackish... it could be a startup option instead
my $uid = $<;
my $oirunning;
my $oiresponsible;
my $mailer;

my $buffycli = Buffy::CLI->new();
my $interval = $buffycli->config->general()->interval();
$SIG{ALRM} = sub { die "timeout" };
$SIG{WINCH} = sub { redraw(); };
$SIG{INT} = sub { sigint(); };
&settitle;

while (1) {
    $oirunning = `pgrep -u $uid offlineimap`;

    redraw();
    $forceusage = 0; # reset it after the first time we've displayed it

    my $input;
    eval {
        alarm($interval);
        chomp($input = <STDIN>);
        alarm(0);
    };

    if ($@) {
        next if ($@ =~ /timeout/); # just a timeout 
        alarm(0);           # clear the still-pending alarm
        die;                # propagate unexpected exception
    } 

    next unless defined $input and $input ne '';
    quit() if $input eq 'q';

    if ($input eq '?') {
        $forceusage = 1;
        next;
    }

    if ($input =~ /^(oc|hc|sc)([A-Z]+|all)$/) { # order, hide or show a column
        if ($1 eq 'oc') {
            $buffycli->setOrder($2);
        }
        elsif ($1 eq 'hc') {
            $buffycli->hideColumn($2);
        }
        elsif ($1 eq 'sc') { 
            $buffycli->showColumn($2);
        }
    }
    elsif ($input eq 'sfall') {
        $buffycli->showAllFolders();
    }
    elsif ($input =~ /^(hf|sf)([0-9,]+)$/) { # hide or show a particular folder
        my @folders = split ',', $2;
        if ($1 eq 'hf') {
            $buffycli->hideFolders(\@folders);
        }
        elsif ($1 eq 'sf') {
            $buffycli->showFolders(\@folders);
        }
    }
    elsif ($input eq 'usage') {
        $buffycli->setShowUsage();
    }
    elsif ($input =~ /^si([0-9]+)$/) { # set interval
        $buffycli->setInterval($1);
        $interval = $1;
    }
    elsif ($input =~ /^(h|s)t$/) {
        $buffycli->setShowTotals($1 eq 's');
    }
    elsif ($input =~ /^[0-9]+$/) { # launch mail reader
        my $folder = $buffycli->lastinteresting()->[$input]->{folder};
        unless ($folder) {
            $buffycli->raiseInputMessage('E: Invalid folder!');
            next;
        }
        $buffycli->config->selectedMailProgram()->run($folder, 'term');
        $mailer = 1;
        wait();
        $mailer = 0;
        &settitle;
    }
    elsif ($input =~ /^oi($|bg$)/) {
        if (! -X $offlineimap) {
            $buffycli->raiseInputMessage('E: Could not find offlineimap!');
        }
        runOfflineimap($input eq 'oibg');
    }
    elsif ($input =~ /^oikill$/) {
        oikill(1);
    }
    else {
        $buffycli->raiseInputMessage('E: Invalid command');
    }
}

sub printUsage {
    my $extended = shift;
    my $oirunning = shift;
    my $extra = "Poll interval: $interval"
        . ($oirunning ? ", Offlineimap is running" : '');

    unless ($extended) {
        print "\nCommand (? for usage, $extra): \n";
        return;
    }

    pod2usage( {
        -message  => "\nCommand: ($extra) \n",
        -sections => "USAGE",
        -exitval  => "NOEXIT",
        -verbose  => 99,
        -output   => \*STDOUT,
    });
    sleep 1;
}

sub runOfflineimap {
    my $bg = shift;

    return if $oirunning;

    my $command = $offlineimap;

    if ($bg) {
        $command .= ' -u Noninteractive.Quiet 2>&1 > /dev/null &';
        $oiresponsible = 1;
        $buffycli->raiseInputMessage('I: Starting offlineimap');
    }
    else {
        $command .= ' -o';
    }
    system($command)
}

sub redraw {
    return if $mailer;
    system('clear');
    $buffycli->printInteresting();
    printUsage($forceusage || $buffycli->getShowUsage(), $oirunning);
}

sub oikill {
    my $requested = shift;
    if ($oirunning && $oiresponsible) {
        system("pkill -u $uid offlineimap");
        sleep 1 if $requested; # so by the time the screen redraws it's gone
    } elsif ($oirunning && !$oiresponsible && $requested) {
        # user has asked to terminate oi, but it's running outside buffycli
        $buffycli->raiseInputMessage('W: Not killing offlineimap, since we\'re not responsible for it running!');
    }

    $oirunning = $oiresponsible = 0;
}

sub quit {
    oikill();
    exit 0;
}

sub sigint {
    return if $mailer;
    quit();
}

sub settitle {
    print "\033]0;buffycli\007";
}
