#!/usr/local/bin/perl -w
###########################################
# artscan - Scan articles in batches
# Mike Schilli, 2010, <m@perlmeister.com>
###########################################
use strict;
use local::lib;
use POE;
use POE::Wheel::Run;
use Curses::UI::POE;
use Sysadm::Install qw(:all);
use File::Temp qw(tempfile);
use File::Basename;

my $PDF_DIR = "/tmp/artscan";
mkd $PDF_DIR unless -d $PDF_DIR;

my $pidfile = "$PDF_DIR/pid";
blurt "$$\n", $pidfile;

my @LBOX_LINES  = ();
my $BUSY        = 0;
my $LAST_PDF;
my @IMAGES      = ();
my $HEAP;

my $CUI = Curses::UI::POE->new(
  -color_support => 1, 
  inline_states  => {
    _start => sub {
      $HEAP = $_[HEAP];
      $_[KERNEL]->sig( "USR1", 
                       "article_scan" );
    },
    scan_finished => \&scan_finished,
    article_scan  => \&article_scan,
});

my $WIN = $CUI->add("win_id", "Window");

my $TOP = $WIN->add( qw( top Label 
  -y 0 -width -1 -paddingspaces 1 
  -fg white -bg blue
), -text => "artscan v1.0" );

my $LBOX = $WIN->add(qw( lb Listbox
        -padtop 1 -padbottom 1 -border 1),
);

my $FOOT = $WIN->add(qw( bottom Label
  -y -1 -paddingspaces 1
  -fg white -bg blue));

footer_update();

$CUI->set_binding(sub { exit 0; },   "q");
$CUI->set_binding( \&article_new,    "n");
$CUI->set_binding( \&article_scan,   "s" );
$CUI->set_binding( \&article_finish, "f" );

$CUI->mainloop;

###########################################
sub article_new {
###########################################
  return if $BUSY;
  @IMAGES = ();
  footer_update();
}

###########################################
sub article_finish {
###########################################
  return if $BUSY;
  $BUSY = 1;

  $FOOT->text("Converting ...");
  $FOOT->draw();

  my @jpg_files = ();

  for my $image ( @IMAGES ) {
      my $jpg_file = 
        "$PDF_DIR/" . basename( $image );
      $jpg_file =~ s/\.pnm$/.jpg/;
      push @jpg_files, $jpg_file;
      task("convert", $image, $jpg_file);
  }

  my $pdf_file = next_pdf_file();

  $FOOT->text("Writing PDF ...");
  $FOOT->draw();

  task("convert", @jpg_files, $pdf_file);
  unlink @jpg_files;

  $LAST_PDF = $pdf_file;
  @IMAGES = ();

  lbox_add("PDF $LAST_PDF ready.");
  footer_update();
  $BUSY = 0;
}

###########################################
sub next_pdf_file {
###########################################
  my $idx = 0;

  my @pdf_files = sort <$PDF_DIR/*.pdf>;

  if( scalar @pdf_files > 0 ) {
    ($idx) = ($pdf_files[-1] =~ /(\d+)/);
  }

  return "$PDF_DIR/" . 
    sprintf("%04d", $idx + 1) . ".pdf";
}

###########################################
sub task {
###########################################
  my($command, @args) = @_;

  lbox_add("Running $command" . " @args");
  tap($command, @args);
}

###########################################
sub article_scan {
###########################################
  return if $BUSY;
  $BUSY = 1;

  my($fh, $tempfile) = tempfile(
      DIR    => $PDF_DIR,
      SUFFIX => ".pnm", UNLINK => 1);

  lbox_add("Scanning $tempfile");

  my $wheel =
    POE::Wheel::Run->new(
      Program     => "./scan.sh",
      ProgramArgs => [$tempfile],
      StderrEvent => 'ignore',
      CloseEvent  => "scan_finished",
  );

  $HEAP->{scanner} = {
    wheel => $wheel, file  => $tempfile };

  $FOOT->text("Scanning ... ");
  $FOOT->draw();
}

###########################################
sub scan_finished {
###########################################
  my($heap) = @_[HEAP, KERNEL];

  push @IMAGES, $heap->{scanner}->{file};
  delete $heap->{scanner};
  footer_update();
  $BUSY = 0;
}

###########################################
sub footer_update {
###########################################
  my $text = "[n]ew [s]can [f]inish [q]" .
  "uit (" . scalar @IMAGES . " pending)";

  if( defined $LAST_PDF ) {
      $text .= " [$LAST_PDF]";
  }
  $FOOT->text($text);
  $FOOT->draw();
}

###########################################
sub lbox_add {
###########################################
  my($line) = @_;

  if( scalar @LBOX_LINES >= 
      $LBOX->height() - 4) {
    shift @LBOX_LINES;
  }
  push @LBOX_LINES, $line;

  $LBOX->{-values} = [@LBOX_LINES];
  $LBOX->{-labels} = { map { $_ => $_ }
                     @LBOX_LINES };
  $LBOX->draw();
}
