#!/usr/bin/perl
#
# Reload a picture into the database
#
# Author: Bill MacAllister <bill@ca-zephyr.org>

use AppConfig qw(:argcount :expand);
use DBI;
use File::Slurp;
use File::Find;
use Getopt::Long;
use Image::Magick;
use Pod::Usage;
use strict;
use Time::Local;

my $CONF;
my $DBH;
my $DBH_UPDATE;
my $DEBUG_TIME;
my $UPDATE_CNT = 0;

my $opt_conf = 'rings.conf';
my $opt_example;
my $opt_debug;
my $opt_dir;
my $opt_help;
my $opt_manual;
my $opt_update;

# ------------------------------------------------
# output debugging information

sub dbg {

    (my $tmp) = @_;

    my $now     = time;
    my $elapsed = $now - $DEBUG_TIME;
    print "$now ($elapsed) $tmp \n";
    $DEBUG_TIME = $now;
    return;

}

# ------------------------------------------------
# output text

sub msg {
    (my $tmp) = @_;
    print $tmp;
    return;
}

# ------------------------------------------------
# sql date time string from unix time stamp

sub sql_datetime {

    my ($dt) = @_;

    if (length($dt) == 0) {
        $dt = time;
    }
    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
      = localtime($dt);
    $mon++;
    $year += 1900;

    return sprintf "%04d-%02d-%02d %02d:%02d:%02d",
      $year, $mon, $mday, $hour, $min, $sec;
}

# ------------------------------------------------
# unix time stamp from sql date time string

sub unix_seconds {

    my ($dt) = @_;

    my $ret = time;
    if ($dt =~ m/(\d+)\-(\d+)\-(\d+)\s+(\d+):(\d+):(\d+)/) {
        my $yyyy = $1;
        my $mm   = $2;
        my $dd   = $3;
        my $h    = $4;
        my $m    = $5;
        my $s    = $6;
        $mm--;
        $ret = timelocal($s, $m, $h, $dd, $mm, $yyyy);
    }
    return $ret;
}

# ------------------------------------------------
# Read configuration

sub read_conf {
    my $conf_file = '/etc/whm/rings.fix';
    if ($opt_conf) {
        $conf_file = $opt_conf;
    }

    my $conf = AppConfig->new({});
    $conf->define(
        'db_host',
        {
            ARGCOUNT => ARGCOUNT_ONE,
            DEFAULT  => 'shelter-db.ca-zephyr.internal'
        }
    );
    $conf->define(
        'db_name',
        {
            ARGCOUNT => ARGCOUNT_ONE,
            DEFAULT  => 'rings'
        }
    );
    $conf->define(
        'db_credentials',
        {
            ARGCOUNT => ARGCOUNT_ONE,
            DEFAULT  => '/etc/whm/rings_db.conf'
        }
    );

    if (-e $conf_file) {
        $conf->file($conf_file);
    } else {
        die("Configuration file is required");
    }

    # Read database credentials and add them to the $conf structure
    my $db_conf = AppConfig->new({});
    $db_conf->define('db_user', { ARGCOUNT => ARGCOUNT_ONE });
    $db_conf->define('db_pass', { ARGCOUNT => ARGCOUNT_ONE });
    if (-e $conf->db_credentials()) {
        $db_conf->file($conf->db_credentials());
    } else {
        msg('INFO: db_credentials = ' . $conf->db_credentials . "\n");
        die("Data base password and user required in configuration file.");
    }
    $conf->define('db_user');
    $conf->db_user($db_conf->db_user());
    $conf->define('db_pass');
    $conf->db_pass($db_conf->db_pass());

    return $conf;
}

sub example_conf {

    msg("# ----------------------------------------------------\n");
    msg("# Example /etc/whm/rings.conf\n");
    msg("#\n");
    msg("picture_dir = /opt/rings\n");
    msg("db_host = localhost\n");
    msg("db_name = rings\n");
    msg("db_credentials = /etc/whm/rings_db.conf\n");
    msg("\n");
    msg("# ----------------------------------------------------\n");
    msg("# Example /etc/whm/rings_db.conf\n");
    msg("# ----------------------------------------------------\n");
    msg("#\n");
    msg("db_user = rings\n");
    msg("db_pass = pass\n");
    return;
}

# ------------------------------------------------------------------------
# Find the entry in the database, load the file into the raw table,
# and add row to action queue table.

sub update_db {
    my ($this_file) = $_;

    my $this_path = $opt_dir . '/' . $this_file;

    my $this_search_target = $this_file;
    $this_search_target =~ s/[.].*//xms;

    my $this_pop    = '2011-07-bryn-karla-wedding';
    my $this_camera = 'Canon EOS DIGITAL REBEL XT';

    my $sel = 'SELECT pictures_information.pid, ';
    $sel .= 'pictures_information.file_name, ';
    $sel .= 'pictures_information.camera ';
    $sel .= 'FROM pictures_information ';
    $sel .= 'JOIN picture_rings ';
    $sel .= 'ON (picture_rings.pid = pictures_information.pid) ';
    $sel .= 'WHERE pictures_information.file_name = ? ';
    $sel .= 'AND pictures_information.camera = ? ';
    $sel .= 'AND picture_rings.uid = ? ';

    my $sth = $DBH->prepare($sel);
    if ($opt_debug) {
        dbg($sel);
    }
    my $cnt = 0;
    my $this_pid;
    $sth->execute($this_search_target, $this_camera, $this_pop);
    if ($sth->err) {
        sql_die($sel, $sth->err, $sth->errstr);
    }
    while (my $row = $sth->fetchrow_hashref) {
        $cnt++;
        $this_pid = $row->{pid};
    }

    if ($cnt == 0) {
        print("INFO: Cannot find rings entry for $this_file\n");
        return;
    }
    if ($cnt > 1) {
        print("INFO: Multiple entries for $this_file\n");
        return;
    }

    # read in the whole picture file
    my $this_picture = read_file($this_path, binmode => ':raw')
      or die("ERROR: problem reading $this_path\n");

    # store the picture in the database
    my $this_date = sql_datetime();

    my $cmd = 'UPDATE pictures_raw SET ';
    $cmd .= 'picture = ? ';
    $cmd .= 'WHERE pid = ? ';
    if ($opt_debug) {
        dbg($cmd);
    }
    my $sth_update = $DBH_UPDATE->prepare($cmd);
    if ($opt_update) {
        msg("Updating $this_pid\n");
        $sth_update->execute($this_picture, $this_pid);
        if ($sth_update->err) {
            sql_die($cmd, $sth_update->err, $sth_update->errstr);
        }
    } else {
        msg("Proposing to update $this_pid\n");
    }

    queue_action_set($this_pid, 'INFO');
    queue_action_set($this_pid, 'SIZE');

    return;
}

# -------------
# Main routine
# -------------

# -- get options
GetOptions(
    'conf=s'  => \$opt_conf,
    'debug'   => \$opt_debug,
    'dir=s'   => \$opt_dir,
    'example' => \$opt_example,
    'help'    => \$opt_help,
    'manual'  => \$opt_manual,
    'update'  => \$opt_update
);

# -- help the poor souls out
if ($opt_help) {
    pod2usage(-verbose => 0);
}
if ($opt_manual) {
    pod2usage(-verbose => 1);
}
if ($opt_example) {
    example_conf();
    exit 1;
}
if (!$opt_dir) {
    print "ERROR: --dir is required\n";
    pod2usage(-verbose => 0);
}
if (!-d $opt_dir) {
    print "ERROR: directory $opt_dir not found\n";
    pod2usage(-verbose => 0);
}
if ($opt_debug) {
    dbg("Initialize timer.");
}

$CONF = read_conf();

# -- Open up connections to the MySQL data
my $db_host = $CONF->db_host();
my $db_name = $CONF->db_name();
my $dbi     = "dbi:mysql:host=$db_host;database=$db_name";
$DBH = DBI->connect($dbi, $CONF->db_user(), $CONF->db_pass())
  or die "ERROR: Can't connect to database $dbi for read\n";
$DBH->{LongTruncOk} = 1;
$DBH->{LongReadLen} = 10000000;
$DBH_UPDATE         = DBI->connect($dbi, $CONF->db_user(), $CONF->db_pass())
  or die "ERROR: Can't connect to database $dbi for write\n";

if ($opt_debug) {
    dbg("opt_dir: $opt_dir");
}
opendir(my $dh, $opt_dir) || die "ERROR: problem opening $opt_dir $!";
while (readdir $dh) {
    my $this_file = $_;
    if ($opt_debug) {
        dbg("this_file: $this_file");
    }
    if ($this_file =~ /[.](jpg|jpeg)$/xmsi) {
        update_db($this_file);
    }
}

$DBH->disconnect
  or die "MAC-F-DISCFAIL, Disconnect failed for $dbi (read)";
$DBH_UPDATE->disconnect
  or die "MAC-F-DISCFAIL, Disconnect failed for $dbi (update)";

exit;

__END__

=head1 NAME

ring-reload-db - Restore raw pictures in database from file

=head1 SYNOPSIS

ring-reload-db --dir=some-path [--update] [--debug] [--help] [--manual]
[--conf=file] [--example]

=head1 DESCRIPTION

Restore raw pictures in database from file.

=head1 OPTIONS AND ARGUMENTS

=over 4

=item --dir=somepath

The directory holding the files to be re-loaded into the database.

=item --conf=file

Configuration file defaults to ./ring-fix.conf.

=item --example

Print an example configuration file and exit.

=item --update

Actually load the data into the rings database.

=item --help

Displays help text.

=item --manual

Displays more complete help text.

=item --debug

Turns on debugging displays.

=back

=head1 AUTHOR

Bill MacAllister <bill@ca-zephyr.org>

=cut
