#!/usr/bin/perl -w
###########################################
# noworries - Developing with a safety net
# Mike Schilli, 2005 (m@perlmeister.com)
###########################################
use strict;
use Sysadm::Install qw(:all);
use File::Find;
use SGI::FAM;
use Log::Log4perl qw(:easy);
use File::Basename;
use Getopt::Std;
use File::Spec::Functions qw(rel2abs
                             abs2rel);
use DateTime;
use DateTime::Format::Strptime;
use Pod::Usage;

my $RCS_DIR = "$ENV{HOME}/.noworries.rcs";
my $SAFE_DIR = "$ENV{HOME}/noworries";

my $CI   = "ci";
my $CO   = "co";
my $RLOG = "rlog";

getopts("dr:wl", \my %opts);

mkd $RCS_DIR unless -d $RCS_DIR;

Log::Log4perl->easy_init({ 
  category => 'main',
  level    => $opts{d} ? $DEBUG : $INFO, 
  file     => $opts{w} && !$opts{d} ? 
      "/tmp/noworries.log" : "stdout",
  layout   => "%d %p %m%n" });

if($opts{w}) {
  INFO "$0 starting up";
  watcher();

} elsif($opts{r} or $opts{l}) {
  my($file) = @ARGV;
  pod2usage("No file given") 
      unless defined $file;

  my $filename = basename $file;

  my $absfile = rel2abs($file);
  my $relfile = abs2rel($absfile,
                        $SAFE_DIR);

  my $reldir = dirname($relfile);
  cd "$RCS_DIR/$reldir";

  if($opts{l}) {
    rlog($filename);
  } else {
    sysrun("co", "-r$opts{r}", 
           "-p", $filename);
  }
  cdback;

} else {
    pod2usage("No valid option given");
}

###########################################
sub watcher {
###########################################

  cd $SAFE_DIR;
    
  my $fam = SGI::FAM->new();
  watch_subdirs(".", $fam);

  while (1) {
      # Block until next event
    my $event=$fam->next_event();
    
    my $dir = $fam->which($event);
    my $fullpath = $dir . "/" . 
                   $event->filename();
    
      # Emacs temp files
    next if $fullpath =~ /~$/;
      # Vi temp files
    next if $fullpath =~ /\.sw[px]x?$/;
    
    DEBUG "Event: ", $event->type, 
          "(", $event->filename, ")";
    
    if($event->type eq "create" and 
       -d $fullpath) {
      DEBUG "Dynamically adding monitor ",
            "for directory $fullpath\n";
      $fam->monitor($fullpath);

    } elsif($event->type =~ /create|change/
            and -f $fullpath) {
      check_in($fullpath);
    }
  }
}
    
###########################################
sub watch_subdirs {
###########################################
    my($start_dir, $fam) = @_;

    $fam->monitor($start_dir);

    for my $dir (subdirs($start_dir)) {
        DEBUG "Adding monitor for $dir";
        $fam->monitor($dir);
    }

    return $fam;
}

###########################################
sub subdirs {
###########################################
    my($dir) = @_;

    my @dirs = ();

    find sub {
        return unless -d;
        return if /^\.\.?$/;
        push @dirs, $File::Find::name;
    }, $dir;

    return @dirs;
}

###########################################
sub check_in {
###########################################
  my ($file) = @_;

  if(! -T $file) {
      DEBUG "Skipping non-text file $file";
      return;
  }

  my $rel_dir = dirname($file);
  my $rcs_dir = "$RCS_DIR/$rel_dir/RCS";

  mkd $rcs_dir unless -d $rcs_dir;

  cd "$RCS_DIR/$rel_dir";
  cp "$SAFE_DIR/$file", ".";
  my $filename = basename($file);

  INFO "Checking $filename into RCS";
  my ($stdout, $stderr, $exit_code) = 
      tap($CI, "-t-", "-m-", $filename);
  INFO "Check-in result: ",
       "rc=$exit_code $stdout $stderr";

  ($stdout, $stderr, $exit_code) = 
                 tap($CO, "-l", $filename);
  cdback;
}

###########################################
sub time_diff {
###########################################
  my ($dt) = @_;

  my $dur = DateTime->now() - $dt;

  for(qw(weeks days hours 
         minutes seconds)) {
      my $u = $dur->in_units($_);
      return "$u $_" if $u;
  }
}

###########################################
sub rlog {
###########################################
  my ($file) = @_;

  my ($stdout, $stderr, $exit_code) = 
                        tap($RLOG, $file);

  my $p = DateTime::Format::Strptime->new(
           pattern => '%Y/%m/%d %H:%M:%S');

  while($stdout =~ /^revision\s(\S+).*?
                    date:\s(.*?); 
                    (.*?)$/gmxs) {
    my($rev, $date, $rest) = ($1, $2, $3);

      (my $lines) = 
               ($rest =~ /lines:\s+(.*)/);
      $lines ||= "first version";

      my $dt = $p->parse_datetime($date);

      print "$rev ", time_diff($dt), 
            " ago ($lines)\n";
  }
}

__END__

=head1 NAME

    noworries - Developing with a safety net

=head1 SYNOPSIS

                  # Print previous version
    noworries -r revision file # C

                  # List all revisions
    noworries -l file

    noworries -w  # Start the watcher
