#!/usr/bin/perl -w
use strict;

use lib '/home/peter/projects/mp3-find/lib';
use DBI;
use MP3::Find;

use File::Spec::Functions qw(catfile);
use Getopt::Long;
GetOptions(
    'create' => \my $CREATE,
    'file|f=s' => \my $DB_FILE,
);

$DB_FILE ||= catfile($ENV{HOME}, 'mp3.db');

#TODO: hints on numeric columns
my @COLUMNS = (
    [ mtime => 'INTEGER' ],
    [ FILENAME => 'TEXT' ], 
    [ TITLE => 'TEXT' ], 
    [ ARTIST => 'TEXT' ], 
    [ ALBUM => 'TEXT' ],
    [ YEAR => 'INTEGER' ], 
    [ COMMENT => 'TEXT' ], 
    [ GENRE => 'TEXT' ], 
    [ TRACKNUM => 'INTEGER' ], 
    [ VERSION => 'NUMERIC' ],
    [ LAYER => 'INTEGER' ], 
    [ STEREO => 'TEXT' ],
    [ VBR => 'TEXT' ],
    [ BITRATE => 'INTEGER' ], 
    [ FREQUENCY => 'INTEGER' ], 
    [ SIZE => 'INTEGER' ], 
    [ OFFSET => 'INTEGER' ], 
    [ SECS => 'INTEGER' ], 
    [ MM => 'INTEGER' ],
    [ SS => 'INTEGER' ],
    [ MS => 'INTEGER' ], 
    [ TIME => 'TEXT' ],
    [ COPYRIGHT => 'TEXT' ], 
    [ PADDING => 'INTEGER' ], 
    [ MODE => 'INTEGER' ],
    [ FRAMES => 'INTEGER' ], 
    [ FRAME_LENGTH => 'INTEGER' ], 
    [ VBR_SCALE => 'INTEGER' ],
);

my @DIRS = @ARGV;
push @DIRS, $ENV{HOME} unless @DIRS;


my $dbh = DBI->connect("dbi:SQLite:dbname=$DB_FILE",'','', { RaiseError => 1 });

create_table($dbh) if $CREATE;
read_mp3s(\@DIRS);

sub read_mp3s {
    my $dirs = shift;

    my $mtime_sth = $dbh->prepare('SELECT mtime FROM mp3 WHERE FILENAME = ?');
    my $insert_sth = $dbh->prepare(
        'INSERT INTO mp3 (' . 
            join(',', map { $$_[0] } @COLUMNS) .
        ') VALUES (' .
            join(',', map { '?' } @COLUMNS) .
        ')'
    );
    my $update_sth = $dbh->prepare(
        'UPDATE mp3 SET ' . 
            join(',', map { "$$_[0] = ?" } @COLUMNS) . 
        ' WHERE FILENAME = ?'
    );
    
    for my $mp3 (find_mp3s(dir => $dirs, no_format => 1)) {
        # see if the file has been modified since it was first put into the db
        $mp3->{mtime} = (stat($mp3->{FILENAME}))[9];
        $mtime_sth->execute($mp3->{FILENAME});
        my $records = $mtime_sth->fetchall_arrayref;
        
        warn "Multiple records for $$mp3{FILENAME}\n" if @$records > 1;
        
        if (@$records == 0) {
            $insert_sth->execute(map { $mp3->{$$_[0]} } @COLUMNS);
            print "A $$mp3{FILENAME}\n";
        } elsif ($mp3->{mtime} > $$records[0][0]) {
            # the mp3 file is newer than its record
            $update_sth->execute((map { $mp3->{$$_[0]} } @COLUMNS), $mp3->{FILENAME});
            print "U $$mp3{FILENAME}\n";
        }
    }
    
    # as a workaround for the 'closing dbh with active staement handles warning
    # (see http://rt.cpan.org/Ticket/Display.html?id=9643#txn-120724)
    # NOT WORKING!!!
    foreach ($mtime_sth, $insert_sth, $update_sth) {
        $_->{Active} = 1;
        $_->finish;
    }
}

#TODO: hints on numeric vs. string columns, for proper sorting
sub create_table {
    my $dbh = shift;
    $dbh->do('CREATE TABLE mp3 (' . join(',', map { "$$_[0] $$_[1]" } @COLUMNS) . ')');
}

=begin

    CREATE TABLE mp3 (
        mtime,
        
        FILENAME,
        
        TITLE,
        ARTIST,
        ALBUM,
        YEAR,
        COMMENT,
        GENRE,
        TRACKNUM,
        
        VERSION,         -- MPEG audio version (1, 2, 2.5)
        LAYER,           -- MPEG layer description (1, 2, 3)
        STEREO,          -- boolean for audio is in stereo
    
        VBR,             -- boolean for variable bitrate
        BITRATE,         -- bitrate in kbps (average for VBR files)
        FREQUENCY,       -- frequency in kHz
        SIZE,            -- bytes in audio stream
        OFFSET,          -- bytes offset that stream begins
    
        SECS,            -- total seconds
        MM,              -- minutes
        SS,              -- leftover seconds
        MS,              -- leftover milliseconds
        TIME,            -- time in MM:SS
    
        COPYRIGHT,       -- boolean for audio is copyrighted
        PADDING,         -- boolean for MP3 frames are padded
        MODE,            -- channel mode (0 = stereo, 1 = joint stereo,
                         -- 2 = dual channel, 3 = single channel)
        FRAMES,          -- approximate number of frames
        FRAME_LENGTH,    -- approximate length of a frame
        VBR_SCALE        -- VBR scale from VBR header
    );

=cut