###########################################################################
#
# ISISPlug.pm -- A plugin for CDS/ISIS databases
#
# A component of the Greenstone digital library software
# from the New Zealand Digital Library Project at the 
# University of Waikato, New Zealand.
#
# Copyright 1999-2004 New Zealand Digital Library Project
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
###########################################################################

package ISISPlug;


use multiread;
use SplitPlug;


# ISISPlug is a sub-class of SplitPlug.
sub BEGIN {
    @ISA = ('SplitPlug');
}


my $arguments = 
    [ { 'name' => "process_exp",
	'desc' => "{BasPlug.process_exp}",
	'type' => "regexp",
	'reqd' => "no",
	'deft' => &get_default_process_exp() },
      { 'name' => "block_exp",
	'desc' => "{BasPlug.block_exp}",
	'type' => "regexp",
	'reqd' => "no",
	'deft' => &get_default_block_exp() },
      { 'name' => "split_exp",
	'desc' => "{SplitPlug.split_exp}",
	'type' => "regexp",
	'reqd' => "no",
	'deft' => &get_default_split_exp() },

      # The interesting options
      { 'name' => "entry_separator",
	'desc' => "{ISISPlug.entry_separator}",
	'type' => "string",
	'reqd' => "no",
	'deft' => "<br>" },
      { 'name' => "subfield_separator",
	'desc' => "{ISISPlug.subfield_separator}",
	'type' => "string",
	'reqd' => "no",
	'deft' => ", " },
      { 'name' => "document_field",
	'desc' => "{ISISPlug.document_field}",
	'type' => "string",
	'reqd' => "no",
	'deft' => "" },
      { 'name' => "document_prefix",
	'desc' => "{ISISPlug.document_prefix}",
	'type' => "string",
	'reqd' => "no",
	'deft' => "" },
      { 'name' => "document_suffix",
	'desc' => "{ISISPlug.document_suffix}",
	'type' => "string",
	'reqd' => "no",
	'deft' => "" } 
      ];

my $options = { 'name'     => "ISISPlug",
		'desc'     => "{ISISPlug.desc}",
		'abstract' => "no",
		'inherits' => "yes",
		'args'     => $arguments };


# This plugin processes files with the suffix ".mst"
sub get_default_process_exp {
    return q^(?i)(\.mst)$^;
}


# This plugin blocks files with the suffix ".fdt" and ".xrf"
sub get_default_block_exp {
    return q^(?i)(\.fdt|\.xrf)$^;
}

    
# This plugin splits the input text at the "----------" lines
sub get_default_split_exp {
    return q^\n----------\n^;
}


sub new {
    my $class = shift(@_);

    my $self = new SplitPlug($class, @_);
    if (!parsargv::parse(\@_, 
			 q^subfield_separator/.*/, ^, \$self->{'subfield_separator'},
			 q^entry_separator/.*/<br>^, \$self->{'entry_separator'},
			 q^document_field/.*/^, \$self->{'document_field'},
			 q^document_prefix/.*/^, \$self->{'document_prefix'},
			 q^document_suffix/.*/^, \$self->{'document_suffix'},
			 "allow_extra_options")) {
	print STDERR "\nIncorrect options passed to ISISPlug, check your collect.cfg configuration file\n";
	die "\n";
    }

    # To allow for proper inheritance of arguments
    my $option_list = $self->{'option_list'};
    push(@{$option_list}, $options);
    $self->{'plugin_type'} = "ISISPlug";

    return bless $self, $class;
}


sub read
{
    my $self = shift(@_);
    my ($pluginfo, $base_dir, $file, $metadata, $processor, $maxdocs) = @_;

    my $result = &SplitPlug::read($self, @_);
    if ($file =~ /$self->{'process_exp'}/ && $self->{'document_field'}) {
	&end_metadata_xml_file($self->{'documents_metadata_xml_file'});
    }

    return $result;
}


sub read_file {
    my $self = shift (@_);
    my ($filename, $encoding, $language, $textref) = @_;

    my ($databasename) = ($filename =~ /([^\.]+)\.mst$/i);

    # Check the associated .fdt and .xrf files exist
    # These files must have a lowercase extension for the IsisGdl program to work
    # Bailing out because of this is kind of crappy but it is only an issue on Unix
    my $fdtfilename = $databasename . ".fdt";
    if (! -e $fdtfilename) {
	die "Error: Could not find ISIS FDT file $fdtfilename.\n";
    }
    my $xrffilename = $databasename . ".xrf";
    if (! -e $xrffilename) {
	die "Error: Could not find ISIS XRF file $xrffilename.\n";
    }

    # The text to split is exported from the database by the IsisGdl program
    open(FILE, "IsisGdl \"$filename\" |");

    my $reader = new multiread();
    $reader->set_handle ('ISISPlug::FILE');
    $reader->set_encoding ($encoding);
    $reader->read_file ($textref);

    close(FILE);

    # Parse the associated ISIS database Field Definition Table file (.fdt)
    my %fdtmapping = &parse_field_definition_table($fdtfilename);

    # Map the tag numbers to tag names, using the FDT mapping
    $$textref =~ s/\ntag=(\d+) /\ntag=$fdtmapping{$1}{'title'} /g;

    # Remove the line at the start so it is split and processed properly
    $$textref =~ s/^----------\n//;

    # Obtain the documents specified in the CDS/ISIS database, if requested
    if ($self->{'document_field'}) {
	# Create a directory to store the document files
	$self->{'documents_directory'} = $databasename . ".all";
	if (-e $self->{'documents_directory'}) {
	    &util::rm_r($self->{'documents_directory'});
	}
	&util::mk_dir($self->{'documents_directory'});

	# ...and a metadata.xml file for the document metadata (extracted from the database)
	$self->{'documents_metadata_xml_file'} = &util::filename_cat($self->{'documents_directory'}, "metadata.xml");
	if (-e $self->{'documents_metadata_xml_file'}) {
	    &util::rm($self->{'documents_metadata_xml_file'});
	}
	&begin_metadata_xml_file($self->{'documents_metadata_xml_file'});
    }
}


sub process
{
    my $self = shift (@_);
    my ($textref, $pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_;
    my $outhandle = $self->{'outhandle'};

    my $subfield_separator = $self->{'subfield_separator'};
    my $entry_separator = $self->{'entry_separator'};

    # Report that we're processing the file
    print STDERR "<Processing n='$file' p='ISISPlug'>\n" if ($gli);
    print $outhandle "IsisPlug: processing $file\n"
	if ($self->{'verbosity'}) > 1;

    # Process each line of the ISIS record, one at a time
    foreach $line (split(/\n/, $$textref)) {
	$line =~ /^tag=(.+) data=(.+)$/;
	my $rawtagname = $1;
	my $rawtagdata = $2;
	# print "Raw tag: $rawtagname, Raw data: $rawtagdata\n";

	# Metadata field names: title case, then remove spaces
	my $tagname = "";
	foreach $word (split(/\s+/, $rawtagname)) {
	    substr($word, 0, 1) =~ tr/a-z/A-Z/;
	    $tagname .= $word;
	}

	# Make sure there is nothing bad in the tag names
	$tagname =~ s/&//g;

	# Handle each piece of metadata ('%' separated)
	my $completetagvalue = "";
	foreach $rawtagvalue (split(/%/, $rawtagdata)) {
	    $completetagvalue .= $entry_separator unless ($completetagvalue eq "");

	    # Metadata field values: take care with subfields
	    my $completeentryvalue = "";
	    while ($rawtagvalue ne "") {
		# If there is a subfield specifier, parse it off
		my $subfieldname = "";
		if ($rawtagvalue =~ s/^\^([a-z])//) {
		    $subfieldname = "." . $1;
		}

		# Parse the metadata value off
		$rawtagvalue =~ s/^([^\^]*)//;
		my $metadatafieldname = $tagname . $subfieldname;
		my $metadatafieldvalue = $1;
		# print "Metadata: $metadatafieldname -> $metadatafieldvalue\n";

		# Handle Keywords specially
		if ($metadatafieldname eq "Keywords") {
		    my $keywordmetadatavalue = $metadatafieldvalue;
		    my $keywordlist = "";
		    while ($keywordmetadatavalue =~ s/\<([^\>]+)\>//) {
			my $keyword = $1;
			$doc_obj->add_utf8_metadata($cursection, $metadatafieldname, $keyword); 
			$keywordlist .= ", " unless ($keywordlist eq "");
			$keywordlist .= $keyword;
		    }

		    $metadatafieldvalue = $keywordlist;
		}

		else {
		    $doc_obj->add_utf8_metadata($cursection, $metadatafieldname, $metadatafieldvalue); 
		}

		$completeentryvalue .= $subfield_separator unless ($completeentryvalue eq "");
		$completeentryvalue .= $metadatafieldvalue;
	    }

	    $completetagvalue .= $completeentryvalue;
	}

	# print "Metadata: $tagname.all -> $completetagvalue\n";
	$doc_obj->add_utf8_metadata($cursection, $tagname . ".all", $completetagvalue); 
    }
    # print "\n";

    # Add the full record as the document text
    $$textref =~ s/\</&lt;/g;
    $$textref =~ s/\>/&gt;/g;
    $doc_obj->add_utf8_text ($cursection, $$textref);

    # Obtain the documents specified in the CDS/ISIS database, if requested
    if ($self->{'document_field'}) {
	my $document_field = $self->{'document_field'};
	my $document_prefix = $self->{'document_prefix'} || "";
	my $document_suffix = $self->{'document_suffix'} || "";

	my $documents_directory = $self->{'documents_directory'};
	my $document_obtained = 0;

	# Look at all the metadata assigned to this record
	my $record_metadata = $doc_obj->get_all_metadata($cursection);
	foreach my $pair (@$record_metadata) {
	    my ($field, $value) = (@$pair);

	    # Does this metadata element specify a document to obtain?
	    if ($field eq $document_field) {
		my $document_file_full = $document_prefix . $value . $document_suffix;

		my $document_file = &obtain_document($self, $document_file_full, $documents_directory);
		if ($document_file) {
		    $document_obtained = 1;
		    &write_metadata_xml_file($self->{'documents_metadata_xml_file'},
					     $document_file, $record_metadata);
		}
	    }
	}

	# If there was a document obtained for this record we don't want the record as well
	if ($document_obtained) {
	    return 0;
	}
    }

    # Record was processed successfully (and there was no document obtained)
    return 1;
}


sub parse_field_definition_table
{
    my $fdtfilename = shift(@_);

    my %fdtmapping = ();

    open(FDT_FILE, "<$fdtfilename") || die "Error: Could not open file $fdtfilename.\n";

    my $amongstdefinitions = 0;
    foreach $fdtfileline (<FDT_FILE>) {
	$fdtfileline =~ s/(\s*)$//;  # Remove any nasty spaces at the end of the lines

	if ($amongstdefinitions) {
	    my $fieldtitle     = substr($fdtfileline,  0, 30);
	    my $fieldsubfields = substr($fdtfileline, 30, 20);
	    my $fieldspecs     = substr($fdtfileline, 50);

	    # Remove extra spaces
	    $fieldtitle =~ s/(\s*)$//;
	    $fieldsubfields =~ s/(\s*)$//;

	    # Map from tag number to metadata field title and subfields
	    my $fieldtag = (split(/ /, $fieldspecs))[0];
	    $fdtmapping{$fieldtag} = { 'title' => $fieldtitle,
				       'subfields' => $fieldsubfields };
	}
	elsif ($fdtfileline eq "***") {
	    $amongstdefinitions = 1;
	}
    }

    close(FDT_FILE);

    return %fdtmapping;
}


sub obtain_document
{
    my $self = shift(@_);
    my $document_file_full = shift(@_);
    my $documents_directory = shift(@_);

    my $outhandle = $self->{'outhandle'};
    print $outhandle "Obtaining document file $document_file_full...\n"
	if ($self->{'verbosity'} > 1);

    my $document_file_name;
    my $local_document_file;

    # Document specified is on the web
    if ($document_file_full =~ /^http:/ || $document_file_full =~ /^ftp:/) {
	$document_file_full =~ /([^\/]+)$/;
	$document_file_name = $1;
	$local_document_file = &util::filename_cat($documents_directory, $document_file_name);

	my $wget_options = "--quiet";
	$wget_options = "--verbose" if ($self->{'verbosity'} > 2);
	$wget_options .= " --timestamping";  # Only re-download files if they're newer
	`wget $wget_options $document_file_full --output-document $local_document_file`;
    }
    # Document specified is on the disk
    else {
	my $dir_sep = &util::get_os_dirsep();
	$document_file_full =~ /(.+$dir_sep)?(.*)$/;
	$document_file_name = $2;
	$local_document_file = &util::filename_cat($documents_directory, $document_file_name);

	&util::cp($document_file_full, $documents_directory);
    }

    # Check the document was obtained successfully
    if (!-e $local_document_file) {
	print STDERR "WARNING: Could not obtain document file $document_file_full\n";
	return undef;
    }

    return $document_file_name;
}


sub begin_metadata_xml_file
{
    my $metadata_xml_file = shift(@_);

    open(METADATA_XML_FILE, ">$metadata_xml_file");
    print METADATA_XML_FILE
	"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" .
	"<!DOCTYPE DirectoryMetadata SYSTEM \"http://greenstone.org/dtd/DirectoryMetadata/1.0/DirectoryMetadata.dtd\">\n" .
	"<DirectoryMetadata>\n";
    close(METADATA_XML_FILE);
}


sub write_metadata_xml_file
{
    my $metadata_xml_file = shift(@_);
    my $file_name = shift(@_);
    my $record_metadata = shift(@_);

    # Make $file_name XML-safe
    $file_name =~ s/</&lt;/g;
    $file_name =~ s/>/&gt;/g;

    open(METADATA_XML_FILE, ">>$metadata_xml_file");

    print METADATA_XML_FILE
	"\n" .
        "  <FileSet>\n" .
	"    <FileName>$file_name</FileName>\n" .
	"    <Description>\n";

    foreach my $pair (@$record_metadata) {
	my ($field, $value) = (@$pair);

	# We're only interested in metadata from the database
	next if ($field eq "gsdlsourcefilename");
	next if ($field eq "gsdldoctype");
	next if ($field eq "Language");
	next if ($field eq "Encoding");
	next if ($field eq "Identifier");
	next if ($field eq "Source");
	next if ($field eq "SourceSegment");
	next if ($field eq "Plugin");

	# Make $value XML-safe
	$value =~ s/</&lt;/g;
	$value =~ s/>/&gt;/g;
	
	print METADATA_XML_FILE "      <Metadata name=\"$field\">$value</Metadata>\n";
    }

    print METADATA_XML_FILE
	"    </Description>\n" .
	    "  </FileSet>\n";

    close(METADATA_XML_FILE);
}


sub end_metadata_xml_file
{
    my $metadata_xml_file = shift(@_);

    open(METADATA_XML_FILE, ">>$metadata_xml_file");
    print METADATA_XML_FILE "\n</DirectoryMetadata>\n";
    close(METADATA_XML_FILE);
}


1;
