#!/usr/bin/perl -w
###########################################
# rummage - Index and search the home dir
# 2005, Mike Schilli <m@perlmeister.com>
###########################################
use strict;

use Getopt::Std;
use File::Find;
use DBI;
use Class::DBI::Loader;
use Log::Log4perl qw(:easy);
use SWISH::API::Common;
use Time::Piece::MySQL;

my $MAX_SIZE   = 100_000;
my $DSN        = "dbi:mysql:dts";
my @DIRS       = ("$ENV{HOME}");
my $COUNTER    = 0;

@DIRS = map { -l $_ ? readlink $_ : $_ } @DIRS;

sub psearch($);
getopts("un:m:k:p:v", \my %opts);

if($opts{u}) {
  Log::Log4perl->easy_init({
    level => $opts{v} ? $DEBUG : $INFO, 
    file  => ">/tmp/rummage.log",
  });
}

db_init($DSN);

my $loader = Class::DBI::Loader->new(
 dsn        => $DSN,
 user       => "root",
 namespace  => "Rummage",
);

my $filedb = $loader->find_class("file");

my $swish = SWISH::API::Common->new(
    file_len_max   => $MAX_SIZE,
    preserve_atime => 1,
);
  # Keyword search
if($opts{k}) {
  my @docs = $swish->search($opts{k});
  print $_->path(), "\n" for @docs;

  # Search by mtime
} elsif($opts{m}) {
  $filedb->set_sql(modified => qq{
    SELECT __ESSENTIAL__
    FROM __TABLE__
    WHERE DATE_SUB(NOW(), 
            INTERVAL $opts{m}) <= mtime
  });
  psearch($filedb->search_modified());

  # Search by path
} elsif($opts{p}) {
  psearch($filedb->search_like(
                path => "%$opts{p}%"));

  # Search newest
} elsif(exists $opts{n}) {
  $opts{n} = 10 unless $opts{n};

  $filedb->set_sql(newest => qq{
    SELECT __ESSENTIAL__
    FROM __TABLE__
    ORDER BY mtime DESC
    LIMIT $opts{n}
  });

  psearch($filedb->search_newest());

    # Index Home Directory
} elsif($opts{u}) {
    # Set all documents unchecked
  $filedb->set_sql("uncheck_all", qq{
    UPDATE __TABLE__ 
    SET checked=0
  });
  $filedb->sql_uncheck_all()->execute();
  find(\&wanted, @DIRS);

      # Update keyword index
  $swish->index_remove();
  $swish->index(@DIRS);

    # Delete all dead documents in the DB
  $filedb->set_sql("delete_dead", qq{
    DELETE FROM __TABLE__ 
    WHERE checked=0
  });
  $filedb->sql_delete_dead()->execute();

} else {
  LOGDIE "usage: $0 [-u] [-v] [-n [N]] ",
         "[-p pathlike] [-k keyword] ",
         "[-m interval]";
}

###########################################
sub wanted {
###########################################
  return unless -f;

  DEBUG ++$COUNTER, 
        " $File::Find::name";

  my($size,$atime,$mtime) = 
                         (stat($_))[7,8,9];
  $atime = mysqltime($atime);
  $mtime = mysqltime($mtime);

  my $entry;

  if(($entry) = $filedb->search(
              path => $File::Find::name)) {
    if($entry->mtime() eq $mtime) {
      DEBUG "$File::Find::name unchanged";
    } else {
      INFO "$File::Find::name changed";
      $entry->mtime($mtime);
      $entry->size($size);
      $entry->atime($atime);
    }
  } else {
    $entry = $filedb->create({
      path       => $File::Find::name,
      mtime      => $mtime,
      atime      => $atime,
      size       => $size,
      first_seen => mysqltime(time()),
    });
  }

  $entry->checked(1);
  $entry->update();
  return;
}

###########################################
sub db_init {
###########################################
  my($dsn) = @_;

  my $dbh = DBI->connect($dsn, "root", 
            "", { PrintError => 0 });

  LOGDIE "Connecting to DB failed: ", 
         DBI::errstr unless $dbh;

  if(! $dbh->do(q{select * from 
                  file limit 1})) {
    $dbh->do(q{
      CREATE TABLE file (
        fileid     INTEGER
                   PRIMARY KEY 
                   AUTO_INCREMENT,
        path       VARCHAR(255),
        size       INTEGER,
        mtime      DATETIME,
        atime      DATETIME,
        first_seen DATETIME,
        type       VARCHAR(255),
        checked    INTEGER
    )}) or LOGDIE "Cannot create table";

    $dbh->do(q{
      CREATE INDEX file_idx ON file (path)
    });
  }
}

###########################################
sub psearch($) {
###########################################
  my($it) = @_;

  while(my $doc = $it->next()) {
    print $doc->path(), 
    " (", $doc->mtime(), ")", "\n";
  }
}

###########################################
sub mysqltime {
###########################################
  my($time) = @_;
  return Time::Piece
         ->new($time)->mysql_datetime();
}
