###########################################
package CameraStore;
###########################################
# Mike Schilli, 2003 (m@perlmeister.com)
###########################################
use warnings;
use strict;

use Class::DBI;
use Log::Log4perl qw(:easy);

###########################################
sub new {       # Constructor
###########################################
    my($class) = @_;
    bless {}, $class;
}

###########################################
sub _img {      # INTERNAL: Get image by ID
###########################################
    my($stamp) = @_;

    my($img) = CameraStore::IDB::Image->
                 search( stamp => $stamp );

    ERROR "No such ID: $stamp" unless
                              defined $img;
    return $img;
}

###########################################
sub _cat {   # INTERNAL: Get category by ID
###########################################
    my($cname) = @_;

    my($cat) = CameraStore::IDB::Category->
            search( name => $cname );

    ERROR "No such tag: $cname" unless
                              defined $cat;
    return $cat;
}

###########################################
sub list_tags {   # Get all tags of one img
###########################################
    my($self, $stamp) = @_;

    my @found = ();

    (my $img = _img($stamp)) or return();

    for my $tag ($img->tags) {
        push @found, $tag->category->name;
    }
  
    return @found;
}

###########################################
sub add_image {  # Add new image (plus tag)
###########################################
    my($self, $stamp, $path, $cname) = @_;

    if(CameraStore::IDB::Image->search( 
      stamp => $stamp ))  {
        ERROR "ID $stamp already exists";
        return undef;
    }

    CameraStore::IDB::Image->create({
                     stamp     => $stamp,
                     path      => $path});

    return 1 unless $cname; # No tag but ok

    return $self->add_tag($cname, $stamp);
}

###########################################
sub delete_image {# Remove image (and tags)
###########################################
    my($self, $stamp) = @_;

    (my $img = _img($stamp)) or 
        return undef;

    $img->delete();
}

###########################################
sub add_tag {          # Add a new tag name
###########################################
    my($self, $cname, $stamp) = @_;

    INFO "Adding tag $cname/$stamp";

    (my $img = _img($stamp)) or 
        return undef;

        # Add category by name
    my $cat = CameraStore::IDB::Category->
      find_or_create({name => $cname});

    if(CameraStore::IDB::Tag->search(
                   image    => $img->id,
                   category => $cat->id)) {
        ERROR "$stamp already has $cname";
        return undef;
    }
        # Add image/cat link to tags table
    $cat->add_to_tags({image => $img->id});
}

###########################################
sub delete_tag {       # Take tag off image
###########################################
    my($self, $cname, $stamp) = @_;

    INFO "Strip $cname from $stamp";

    (my $img = _img($stamp)) or 
        return undef;

    (my $cat = _cat($cname)) or 
        return undef;

    my($tag)=CameraStore::IDB::Tag->search(
                image    => $img->id,
                category => $cat->id,
              );

    unless($tag) {
        ERROR "No $cname on $stamp";
        return undef;
    }

    $tag->delete();
}

###########################################
sub search_tag {
###########################################
    my($self, $tag, $paths, $stamp) = @_;

    my @matches = CameraStore::IDB::Image->
                       match($tag, $stamp);
    my $field = $paths ? "path" : "stamp";
    return map { $_->$field } @matches;
}

###########################################
package CameraStore::IDB;
###########################################
use base q(Class::DBI);
__PACKAGE__->set_db('Main', 
                    'dbi:mysql:idb', 
                    'root', '');

###########################################
package CameraStore::IDB::Image;
###########################################
use base q(CameraStore::IDB);

__PACKAGE__->table('images');
__PACKAGE__->columns(
    All => qw(id stamp path));
__PACKAGE__->has_many('tags', 
    'CameraStore::IDB::Tag' => 'image');

__PACKAGE__->set_sql(rawmatch => q{
SELECT DISTINCT images.stamp, images.path
FROM categories, tags, images
WHERE
  categories.name LIKE ? AND
  categories.id = tags.category AND
  images.id = tags.image AND
  images.stamp LIKE ?
});

sub match {
    my ($class, $rex, $stamp) = @_;
    $stamp = "%" unless defined $stamp;
    my $sth = $class->sql_rawmatch;
    $sth->execute($rex, $stamp);
    return $class->sth_to_objects($sth);
}

###########################################
package CameraStore::IDB::Category;
###########################################
use base q(CameraStore::IDB);

__PACKAGE__->table('categories');
__PACKAGE__->has_many('tags',
    'CameraStore::IDB::Tag' => 'category');
__PACKAGE__->columns(
    All => qw(id name));

###########################################
package CameraStore::IDB::Tag;
###########################################
use base q(CameraStore::IDB);

__PACKAGE__->table('tags');
__PACKAGE__->columns(
    All => qw(id image category));
__PACKAGE__->has_a('category',
             'CameraStore::IDB::Category');
__PACKAGE__->has_a('image',
             'CameraStore::IDB::Image');
1;
