# Copyright (C) 2005  Joshua Hoblitt
#
# $Id: MetaDB.pm,v 1.14 2006-01-21 02:39:05 jhoblitt Exp $

package PS::IPP::MetaDB;

use strict;
use warnings FATAL => qw( all );

use vars qw( $VERSION ); 
$VERSION = '0.01';

use base qw( Class::DBI::mysql );

use Carp qw( croak );

sub import {
    my $class = shift;

    $class->init( @_ ) if @_;
}

my @child_inits;

sub init {
    my $class = shift;
    my %p = @_;
    
    croak "dsn is required" unless exists $p{dsn};

    $class->set_db('Main', $p{dsn}, $p{user}, $p{passwd});

    foreach my $init (@child_inits) {
        &$init;
    }
}

sub register_init {
    my $class = shift;
    push @child_inits, shift;
}

# lifted from Class::DBI docs (modified)
sub do_transaction {
    my $class = shift;
    my ( $code ) = @_;

    # Turn off AutoCommit for this scope.
    # A commit will occur at the exit of this block automatically,
    # when the local AutoCommit goes out of scope.
    local $class->db_Main->{ AutoCommit };

    # Execute the required code inside the transaction.
    eval { $code->() };
    if ( $@ ) {
        my $commit_error = $@;
        eval { $class->dbi_rollback }; # might also die!
        croak $commit_error;
    }
}

sub add_fetch {
    my $class = shift;
    my %p = @_;

    PS::IPP::MetaDB::Image::Fetch->create({
        exp_id      => $p{exp_id},
        class       => $p{class},
        class_id    => $p{class_id},
        camera      => $p{camera},
        stamp       => $p{stamp},
        file_id     => $p{file_id},
        size        => $p{size},
        md5         => $p{md5},
        url         => $p{url},
    });
}

sub fetch_fields
{
    return PS::IPP::MetaDB::Image::Raw->fields;
}

sub find_fetch {
    my $class = shift;
    my %p = @_;

    my @rows;
    if (exists $p{exp_id}) {
        @rows = PS::IPP::MetaDB::Image::Fetch ->search(
            exp_id => $p{exp_id},
            { order_by => 'class_id' },
        );
    } else {
        @rows = PS::IPP::MetaDB::Image::Fetch->retrieve_all;
    }

    my @fields = fetch_fields();
    my @result;
    foreach my $obj (@rows) {
        my %hash;
        @hash{@fields} = $obj->get(@fields);
        push(@result, \%hash);
    }

    return @result;
}

sub fetch2raw {
    my $class = shift;
    my %p = @_;

    $class->do_transaction(sub { $class->_fetch2raw(%p) });
}

sub _fetch2raw {
    my $class = shift;
    my %p = @_;

    my $fetch = PS::IPP::MetaDB::Image::fetch->search(
        exp_id      => $p{exp_id},
        class       => $p{class},
        class_id    => $p{class_id},
    );

    PS::IPP::MetaDB::Image::NotifyReceived->create({
        file_id => $fetch->file_id,
    });

    PS::IPP::MetaDB::Image::Raw->create({
        exp_id      => $fetch->exp_id,
        class       => $fetch->class,
        class_id    => $fetch->class_id,
        camera      => $fetch->camera,
        # stamp should auto-update
        file_id     => $fetch->file_id,
        size        => $fetch->size,
        md5         => $fetch->md5,
        # new storage url
        url         => $p{url},
    });

    $fetch->delete;
}


#package PS::IPP::MetaDB::Image::Prepared;
#package PS::IPP::MetaDB::Image::Reduced;
#package PS::IPP::MetaDB::Image::Analyzed;
#package PS::IPP::MetaDB::Image::Combined;

=pod

=head2 Temporal Queues

These queues all describe work that I<needs> to be performed but hasn't
happened yet.

=over 4

=item * image_fetch

This table contains a list of new data waiting to be downloaded from the
summit.  The location of fetched data files is written into the C<raw> table.

Table description:

=over 4

=item * exp_id

A unique exposure ID #.

=item * class

The 'type' of data this is.

=item * class_id

A unique identifier for data of the same class in the same exposure.

=item * camera

Camera that this data was acquired from.

=item * stamp

Date/Time stamp of file when the file became available.

=item * file_id

OTIS's ID string for the file, i.e. it's filename.

=item * size

The size of the file in bytes.

=item * md5

The md5 checksum of the file in hexadecimal form (ASCII).

=item * url

URL to retrieve data from.

=back

=cut

package PS::IPP::MetaDB::Image::Fetch;

use base qw( PS::IPP::MetaDB );

__PACKAGE__->register_init(\&init);

sub init
{
    __PACKAGE__->table("image_fetch");
    __PACKAGE__->create_table(q{
        exp_id      BIGINT          NOT NULL PRIMARY KEY,
        class       VARCHAR(255),
        class_id    BIGINT          NOT NULL,
        camera      VARCHAR(255),
        stamp       DATETIME,
        file_id     VARCHAR(255),
        size        INT,
        md5         VARCHAR(32),
        url         VARCHAR(255)    NOT NULL
    });
    __PACKAGE__->set_up_table;
}

sub fields
{
    return qw( exp_id class class_id camera stamp file_id size md5 url );
}

=item * notify_received

XXX no longer needed?

This is a queue of pending file received notifications that need to be send to
OTIS.  Once a notification is successfully sent to OTIS the entry is removed
from this queue.

=over 4

=item * file_id

OTIS's ID string for the file, i.e. it's filename.

=back

=cut

package PS::IPP::MetaDB::Image::NotifyReceived;

use base qw( PS::IPP::MetaDB );

__PACKAGE__->register_init(\&init);

sub init
{
    __PACKAGE__->table("notify_received");
    __PACKAGE__->create_table(q{
        file_id      BIGINT          NOT NULL PRIMARY KEY,
    });
    __PACKAGE__->set_up_table;
}

=item * metadata_fetch

This table contains a list of new metadata waiting to be downloaded from the
summit.  XXX Are these files written to disk or just parsed in memory?

Table description:

=over 4

=item * stamp

Date/Time stamp of file when the file became available.

=item * file_id

OTIS's ID string for the file, i.e. it's filename.

=item * size

The size of the file in bytes.

=item * md5

The md5 checksum of the file in hexadecimal form (ASCII).

=item * url

URL to retrieve data from.

=back

=cut

package PS::IPP::MetaDB::Metadata::Fetch;

use base qw( PS::IPP::MetaDB );

__PACKAGE__->register_init(\&init);

sub init
{
    __PACKAGE__->table("metadata_fetch");
    __PACKAGE__->create_table(q{
        stamp       DATETIME,
        file_id     VARCHAR(255)    NOT NULL PRIMARY KEY,
        size        INT,
        md5         VARCHAR(32),
        url         VARCHAR(255)    NOT NULL
    });
    __PACKAGE__->set_up_table;
}

=item * exposure_prepare

Phase 1: A list of exposure IDs to extra metadata from.

=over 4

=item * exp_id

exposure ID #, keyed against the C<raw> table.

=back

=cut

package PS::IPP::MetaDB::Exposure::Prepare;

use base qw( PS::IPP::MetaDB );

__PACKAGE__->register_init(\&init);

sub init
{
    __PACKAGE__->table("exposure_prepare");
    __PACKAGE__->create_table(q{
        exp_id      BIGINT          NOT NULL PRIMARY KEY,
    });
    __PACKAGE__->set_up_table;
}

=item * image_reduce

Phase 2: A list of exposure components to reduce.

=over 4

=item * exp_id

exposure ID #, keyed against the C<prepared> table.

=item * class_id

class ID #, keyed against the C<prepared> table.

=item * prepare_version

version #, keyed against the C<prepared> table.

=back

=item * exposure_mosaic

Phase 3: A list of exposures to process.

=over 4

=item * exp_id

exposure ID #, keyed against the C<reduced> table.

=item * exp_config

Class ID #, keyed against the C<exposure_config> table.

=back

=item * combine

=over 4

=item * run_id???

=back

=back

=head2 Metadata

These tables describe data in other tables.

=over 4

=item * exposure

A list of ALL exposure ID #s.

=over 4

=item * exp_id

exposure ID #.

=item * camera

Name of the camera the generated the exposure.

=item * type

The type of exposure taken.

=item * n_components

The number of file that make of the complete exposure.

=back

=item * exposure_component

A list of the files that together make up a complete exposure.

=over 4

=item * exp_id

exposure ID #, keyed against the C<exposure> table.

=item * class_id

class ID #.

=item * class

The type of component this is

=back

=item * exposure_config

This table provides a way to configure which 'version' of components of an
'exposure' to use.

=over 4

=item * exp_config

exposure config ID #.

=item * class_id

class ID #, keyed against the C<exposure_component> table.

=item * version

version ID #, keyed against the C<exposure_component> table.

=item * n_components

=back

=item * exposure_reduced

A list of version ID #'s assigned after reducing data.  This table is used to
make sure that all components of an exposure get the same version ID assigned
to them.

=over 4

=item * exp_id

exposure ID #, keyed on the C<exposure> table.

=item * reduced_version

Version ID # used.

=back

=back

=head2 File Storage

These tables describe data actually stored on disk.

=over 4

=item * raw 

Note that there is only one 'version' of the original data.

=over 4

=item * exp_id

exposure ID #, keyed on the C<exposure_component> table.

=item * class

The 'type' of data this is.

=item * class_id

class ID #, keyed on the C<exposure_componet> table.

=item * url

url to the storage location of the file.

=back

=cut

package PS::IPP::MetaDB::Image::Raw;

use base qw( PS::IPP::MetaDB );

__PACKAGE__->register_init(\&init);

sub init
{
    __PACKAGE__->table("raw");
    __PACKAGE__->create_table(q{
        exp_id      BIGINT          NOT NULL PRIMARY KEY,
        class       VARCHAR(256)    NOT NULL,
        class_id    BIGINT          NOT NULL,
        camera      VARCHAR(255),
        stamp       DATETIME,
        file_id     VARCHAR(255),
        size        INT,
        md5         VARCHAR(32),
        url         VARCHAR(255)    NOT NULL
    });
    __PACKAGE__->set_up_table;
}


=item * prepared

Data that has been through Phase: 0.

=over 4

=item * exp_id

exposure ID #, keyed on the C<exposure_component> table.

=item * class_id

class ID #, keyed on the C<exposure_componet> table.

=item * prepare_version

prepare step version ID #


=item * url

url to the storage location of the file.

=back

=item * reduced

Data that's been through Phase: 2.

=over 4

=item * exp_id

exposure ID #, keyed on the C<exposure_component> table.

=item * class_id

class ID #, keyed on the C<exposure_componet> table.

=item * prepare_version

prepare step version ID #

=item * reduce_version

reduce step version ID #

=item * url

url to the storage location of the file.

=back

=item * mosaiced

???

=item * combined

???

=back

=cut

1;

__END__
