package EPrints::Plugin::Export::METS;

=head1 NAME

EPrints::Plugin::Export::METS - Export plugin for METS

=head1 DESCRIPTION

This plugin exports EPrint objects in METS xml.

This plugin is based on work by Jon Bell, UWA.

=cut 

use strict;
use warnings;

use EPrints::Plugin::Export;
our @ISA = qw( EPrints::Plugin::Export );

our $PREFIX = "mets:";
our $MODS_PREFIX = "mods:";

sub new
{
	my( $class, %opts ) = @_;

	my $self = $class->SUPER::new( %opts );

	$self->{name} = "METS";
	$self->{accept} = [ 'dataobj/eprint', 'list/eprint' ];
	$self->{visible} = "all";
	$self->{suffix} = ".xml";
	$self->{mimetype} = "text/xml";
	
	$self->{xmlns} = "http://www.loc.gov/METS/";
	$self->{schemaLocation} = "http://www.loc.gov/standards/mets/mets.xsd";

	return $self;
}

sub output_list
{
	my( $plugin, %opts ) = @_;

	my $type = $opts{list}->get_dataset->confid;
	my $toplevel = "mets-objects";
	
	my $r = [];

	my $part;
	$part = <<EOX;
<?xml version="1.0" encoding="utf-8" ?>

<$toplevel>
EOX
	if( defined $opts{fh} )
	{
		print {$opts{fh}} $part;
	}
	else
	{
		push @{$r}, $part;
	}

	foreach my $dataobj ( $opts{list}->get_records )
	{
		$part = $plugin->output_dataobj( $dataobj, %opts );
		if( defined $opts{fh} )
		{
			print {$opts{fh}} $part;
		}
		else
		{
			push @{$r}, $part;
		}
	}	

	$part= "</$toplevel>\n";
	if( defined $opts{fh} )
	{
		print {$opts{fh}} $part;
	}
	else
	{
		push @{$r}, $part;
	}


	if( defined $opts{fh} )
	{
		return;
	}

	return join( '', @{$r} );
}

sub output_dataobj
{
	my( $plugin, $dataobj ) = @_;

	my $xml = $plugin->xml_dataobj( $dataobj );

	return EPrints::XML::to_string( $xml );
}


sub xml_dataobj
{
	my( $plugin, $dataobj ) = @_;

	my $session = $plugin->{ session };

	my $id = $dataobj->get_gid;

	my $mods_plugin = $session->plugin( "Export::MODS" )
		or die "Couldn't get Export::MODS plugin";
	my $conv_plugin = $session->plugin( "Convert" )
		or die "Couldn't get Convert plugin";
	
	my $nsp = "xmlns:${PREFIX}";
	chop($nsp); # remove the trailing ':'
	my $mods_nsp = "xmlns:${MODS_PREFIX}";
	chop($mods_nsp); # remove the trailing ':'
	my $mets = $session->make_element(
		"${PREFIX}mets",
		"OBJID" => $id,
		"LABEL" => "Eprints Item",
		$nsp => $plugin->{ xmlns },
		$mods_nsp => $mods_plugin->{ xmlns },
		"xmlns:xlink" => "http://www.w3.org/1999/xlink",
		"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
		"xsi:schemaLocation" =>
			$plugin->{ xmlns } . " " . $plugin->{ schemaLocation } . " " .
			$mods_plugin->{ xmlns } . " " . $mods_plugin->{ schemaLocation }				
	);
	
	# metsHdr
	$mets->appendChild( _make_header( $session, $dataobj ));

	# dmdSec
	my $mods_id = "DMD_".$id."_mods"; # also used in structMap
	$mets->appendChild(my $mods_dmd = $session->make_element(
		"${PREFIX}dmdSec",
		"ID" => $mods_id
	));
	$mods_dmd->appendChild(my $mods_mdWrap = $session->make_element(
		"${PREFIX}mdWrap",
		"MDTYPE" => "mods"
	));
	my $mods = $mods_plugin->xml_dataobj( $dataobj, $MODS_PREFIX );
	$mods_mdWrap->appendChild( my $xmlData = $session->make_element(
		"${PREFIX}xmlData"
	));
	# copy in the child nodes (we don't need to repeat the MODS namespace)
	$xmlData->appendChild( $_ ) for ($mods->getChildNodes);
	
	# amdSec
	my $amd_id = "TMD_".$id;
	my $rights_id = "rights_".$id."_mods";
	$mets->appendChild( _make_amdSec( $session, $dataobj, $amd_id, $rights_id ));
	
	# fileSec
	$mets->appendChild( _make_fileSec( $session, $dataobj, $id, $conv_plugin ));
	
	# structMap
	$mets->appendChild( _make_structMap( $session, $dataobj, $id, $mods_id, $amd_id ));

	return $mets;
}

sub _make_header
{
	my( $session, $dataobj ) = @_;
	
	my $time = EPrints::Time::get_iso_timestamp();
	my $repo = $session->get_repository;
	
	my $header = $session->make_element(
		"${PREFIX}metsHdr",
		"CREATEDATA" => $time
	);
	$header->appendChild( my $agent = $session->make_element(
		"${PREFIX}agent",
		"ROLE" => "CUSTODIAN",
		"TYPE" => "ORGANIZATION"
	));
	$agent->appendChild( my $name = $session->make_element(
		"${PREFIX}name",
	));
	my $aname = $session->phrase( "archive_name" );
	$name->appendChild( $session->make_text( $aname ));
	
	return $header;
}

sub _make_amdSec
{
	my( $session, $dataobj, $amd_id, $rights_id ) = @_;
	
	my $amdSec = $session->make_element(
		"${PREFIX}amdSec",
		"ID" => $amd_id
	);
	
	$amdSec->appendChild(my $rightsMD = $session->make_element(
		"${PREFIX}rightsMD",
		"ID" => $rights_id
	));
	
	$rightsMD->appendChild( my $mdWrap = $session->make_element(
		"${PREFIX}mdWrap",
		"MDTYPE" => "mods"
	));
	
	$mdWrap->appendChild( my $xmlData = $session->make_element(
		"${PREFIX}xmlData",
	));
	
	$xmlData->appendChild( my $mods_use = $session->make_element(
		"${MODS_PREFIX}useAndReproduction"
	));
	$mods_use->appendChild( $session->html_phrase( "deposit_agreement_text" ));
	
	return $amdSec;
}

sub _make_fileSec
{
	my( $session, $dataobj, $id, $conv_plugin ) = @_;

	my $fileSec = $session->make_element(
		"${PREFIX}fileSec"
	);
	
	foreach my $doc ($dataobj->get_all_documents)
	{
		my $baseurl = $doc->get_baseurl;
		my $id_base = $id."_".$doc->get_id;
		my %files = $doc->files;

		$fileSec->appendChild(my $fileGrp = $session->make_element(
			"${PREFIX}fileGrp",
			"USE" => "reference"
		));

		my $file_idx = 0;
		while( my( $name, $size) = each %files )
		{
			$file_idx++;
			my $url = $baseurl . $name;
			my $mimetype = $doc->mime_type( $name );
			$mimetype = 'application/octet-stream' unless defined $mimetype;

			$fileGrp->appendChild( my $file = $session->make_element(
				"${PREFIX}file",
				"ID" => $id_base."_".$file_idx,
				"SIZE" => $size,
				"OWNERID" => $url,
				"MIMETYPE" => $mimetype
			));

			$file->appendChild( $session->make_element(
				"${PREFIX}FLocat",
				"LOCTYPE" => "URL",
				"xlink:type" => "simple",
				"xlink:href" => $url
			));
		}
	}
	
	return $fileSec;
}

sub _make_structMap
{
	my( $session, $dataobj, $id, $dmd_id, $amd_id ) = @_;
	
	my $structMap = $session->make_element( "${PREFIX}structMap" );
	
	$structMap->appendChild( my $top_div = $session->make_element(
		"${PREFIX}div",
		"DMDID" => $dmd_id,
		"AMDID" => $amd_id
	));
	
	foreach my $doc ($dataobj->get_all_documents)
	{
		my $id_base = $id."_".$doc->get_id;
		my $file_idx = 0;
		my %files = $doc->files;
		if( scalar keys %files > 1 )
		{
			while( my( $name, $size ) = each %files )
			{
				$file_idx++;
				my $file_id = $id_base."_".$file_idx;
				$top_div->appendChild( my $div = $session->make_element(
					"${PREFIX}div"
				));
				$div->appendChild( $session->make_element(
					"${PREFIX}fptr",
					"FILEID" => $file_id
				));
			}
		}
		else
		{
			$file_idx++;
			my $file_id = $id_base."_".$file_idx;
			$top_div->appendChild( $session->make_element(
				"${PREFIX}fptr",
				"FILEID" => $file_id
			));
		}
	}
	
	return $structMap;
}

1;
