#!/usr/bin/env perl
#
# Mock-up datastore registration
#
# Does not enforce validity of metadata items.
#

use strict;

use Getopt::Long qw( GetOptions );
use Pod::Usage qw( pod2usage );
use Digest::MD5::File qw( file_md5_hex ); 

my $add;
my $del;

my $ds_dir = './dsroot';
my $product;
my $fileset;

### fill in some bogus values here
my $type = 'OBJECT';
my $ra = '00:00:00.00';
my $dec = '00:00:00.00';
my $equinox = 2000;
my $etime = 30.0;
my $filter = 'z';
my $airmass = 1.2;

my $date;

#
# parse args
#

GetOptions(
    'add'               => \$add,
    'del'               => \$del,
    'dsdir|ds=s'        => \$ds_dir,
    'type|t=s'          => \$type,
    'ra|r=s'            => \$ra,
    'dec|d=s'           => \$dec,
    'equinox|eq=f'      => \$equinox,
    'etime|ex|e=f'      => \$etime,
    'filter|f=s'        => \$filter,
	'date=s'			=> \$date,
    'airmass|a=f'       => \$airmass,
) or pod2usage(2);

my $err;

$err .= "Must specify a product and a fileset.\n"
    unless $#ARGV == 1;

# will exit if neither or both -add and -del were specified
$err .= "Must specify either -add or -del.\n"
    unless $add xor $del;

$err .= "Datastore not found at '$ds_dir'.\n"
    if stat("$ds_dir/index.txt") == undef;

# bail if any of the above args were improper
show_usage($err)
    if defined($err);

$product = shift;

show_usage("Invalid product '$product'.")
    unless stat("$ds_dir/$product/index.txt") != undef;

$fileset = shift;

#
# run
#

# allow write for the mockup
umask 0011;


#
# delete fileset
#
if ($del) {
    if (stat("$ds_dir/$product/$fileset/index.txt") == undef) {
        print "Fileset '$fileset' doesn't exist under $product.\n";
        exit(3);
    }

    # When iterating through the product index below, we rememeber the
    # fileset and time of the newest (assumes chrono order) fileset that is
    # not the fileset we're deleting, just in case we have to update the
    # root index.
    my $last_fileset;
    my $last_time;


    # Read the product index into a buffer, minus $fileset's entry

    open(IO, "$ds_dir/$product/index.lst")
        or die("Failed to open '$ds_dir/$product/index.lst' for reading\n");

    my $buf;

    while (<IO>) {
        my ($fs, $time, my $tmp) = split(/\|/, $_, 3);
        $fs =~ s/^\s+|\s+$//;
       
        # skip the one we're removing
        next if $fs eq $fileset;

        # record the last one that wasn't the one we're removing
        $last_fileset = $fs;
        $last_time = $time;

        $buf .= $_;
    }

    close(IO);


    # Write the new index

    open(IO, ">", "$ds_dir/$product/index.lst")
        or die("Failed to open '$ds_dir/$product/index.lst' for writing\n");
        
    print IO $buf;
    close(IO);
    

    # Remove the fileset index file

    unlink("$ds_dir/$product/$fileset/index.txt")
        or die("Failed to delete '$ds_dir/$product/$fileset/index.txt'\n");

    # Check the root index; if the fileset we just removed was the
    # 'most_recent' one, then replace it.

    my $doreplace = 0;
    
    open(IO, "$ds_dir/index.txt")
        or die("Failed to open '$ds_dir/index.txt' for reading\n");

    $buf = '';

    while (<IO>) {
        my ($prod, $oldrecent, $oldtime, $extra) = split(/\|/, $_, 4);
        $prod =~ s/^\s+|\s+$//;
        
        if ($prod ne $product) {
            $buf .= $_;
            next;
        }

        if ($oldrecent ne $fileset) {
            $buf .= $_;
            next;
        }
        
        $buf .= "$prod|$last_fileset|$last_time|$extra";
        $doreplace = 1;
    }

    close(IO);

    # Write the new root index if needed
    if ($doreplace) {
        open(IO, ">", "$ds_dir/index.txt")
            or die("Failed to open '$ds_dir/index.txt' for writing\n");
        
        print IO $buf;
        close(IO);
    }
 
    print "Removed $fileset from $product.\n";
}


#
# add fileset
#
else {
    unless (stat("$ds_dir/$product/$fileset/index.txt") == undef) {
        print "Fileset '$fileset' already exists under $product.\n";
        exit(4);
    }

    
	# Create the fileset index

    open(IO, ">", "$ds_dir/$product/$fileset/index.txt")
        or die("Failed to open '$ds_dir/$product/$fileset/index.txt' for writing");
    print IO "# fileID      |bytes   |md5sum                          |type|chipname\n";
    
    #print "-- Send file list with lines of the form 'fileID type chipname' "
    #    ."followed by EOF.\n";


    # Read file data from STDIN

    my $lineno = 0;

    while (<STDIN>) {
        $lineno++;
        
        unless (/([A-Za-z0-9-_.]+)\s+([A-Za-z0-9-_.]+)\s+([A-Za-z0-9-_.]+)/) {
            print "Line $lineno: ignored malformed input line.\n";
            next;
        }

        # $1 = file id
        # $2 = type
        # $3 = chip

        my @finfo = stat("$ds_dir/$product/$fileset/$1");

        unless (@finfo) {
            print "Line $lineno: referenced file "
                ."($ds_dir/$product/$fileset/$1) does not exist.\n";
            next;
        }
        
        # Get MD5 sum
        my $md5 = file_md5_hex("$ds_dir/$product/$fileset/$1");

        # Write line to the fileset index
        print IO "$1|$finfo[7]|$md5|$2|$3\n";
    }

    close(IO);
    
	
    # Add reference to the new fileset to the product index

	$date = get_iso8601() if not defined($date);

	#FIXME this should probably be inserted in order!

    my $indexstr =
        "$fileset|$date|$type|$ra  $dec $equinox|$etime|$filter|$airmass";

    #open(IO, ">>", "$ds_dir/$product/index.txt")
    #    or die("Failed to open $ds_dir/$product/index.txt\n");
    open(IO, ">>", "$ds_dir/$product/index.lst")
        or die("Failed to open $ds_dir/$product/index.lst\n");
    print IO $indexstr, "\n";
    close(IO);


     # Update the root listing to reflect the new most-recent fileset

    open(IO, "$ds_dir/index.txt")
        or die("Failed to open '$ds_dir/index.txt' for reading\n");

    my $buf;

    while (<IO>) {
        my ($prod, $oldrecent, $oldtime, $extra) = split(/\|/, $_, 4);
        $prod =~ s/^\s+|\s+$//;
        
        if ($prod ne $product) {
            $buf .= $_;
            next;
        }

        $buf .= "$prod|$fileset|$date|$extra";
    }

    close(IO);


    # Write the new index

    open(IO, ">", "$ds_dir/index.txt")
        or die("Failed to open '$ds_dir/index.txt' for writing\n");
        
    print IO $buf;
    close(IO);
    
    print "Added $fileset to $product.\n";
}

sub show_usage {
    my $str = shift;

    chomp $str;

    my @tmp = split(/\//, $0);

    pod2usage(
        -msg => 
"usage: $tmp[$#tmp] [-add|-del] product fileset [options]

Commands:

    -del        Remove a fileset.
    -add        Add a new fileset with the given metadata options.
                File information will be read per-line from STDIN, in the form:
                    fileID type chipname
                These entries are read until EOF is sent.
Options:
    
    -dsdir      Specify the path to the Datastore (defaults to CWD).
    
 (The following metadata is optional because this is just a mock-up.)

    -date       ISO8601 date string. (File timestamp used if not specified.)
    -type       Fileset type.
    -ra         Telescope pointing RA.
    -dec        Telescope pointing Dec.
    -equinox    Equinox.
    -etime      Exposure time.
    -filter     Filter used.
    -airmass    Air mass.
    
$str",
        -exitval => 2
    );
}

# get the current UTC time in iso8601 format
# this is used if no time parameter is specified
sub get_iso8601() {
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
        gmtime(time);

    return sprintf("%4d-%02d-%02dT%02d:%02d:%02dZ",
        $year+1900, $mon+1, $mday, $hour, $min, $sec);
}
