#!/usr/bin/perl -w -I/opt/eprints2/perl_lib 

use EPrints::Archive;
use SOAP::Transport::HTTP;


SOAP::Transport::HTTP::CGI->dispatch_to("EPrints::WebServices")->handle;

exit;

package EPrints::WebServices;

use Data::Dumper;
use SOAP::Lite;

use strict;

sub echo($$;$)
{
	my( $class, $string, $count ) = @_;

	print STDERR "echo(".join(",",@_).")\n";
	$count = 1 unless defined( $count );

	return SOAP::Data->name("return" => "$string"x$count );
}


sub modifyEprint
{
	my( $class,$eprintid,$eprintdata ) = @_;

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'eprint' ); 
	my $eprint = $dataset->get_object( $session, $eprintid );
	
	if( !defined $eprint )
	{
		$session->terminate;
		die "eprint $eprintid does not exist";
	}
	foreach( keys %{$eprintdata} )
	{
		next if( $_ eq "documents" );
		$eprint->set_value( $_, $eprintdata->{$_} );
	}
	$eprint->commit;

	$eprint->generate_static;
		
	$session->terminate;

	return;
}


sub modifyDocument
{
	my( $class,$docid,$doc_data ) = @_;

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'document' ); 
	my $document = $dataset->get_object( $session, $docid );
	
	if( !defined $document )
	{
		$session->terminate;
		die "document $docid does not exist";
	}

	foreach( keys %{$doc_data} )
	{
		next if( $_ eq "files" );
		$document->set_value( $_, $doc_data->{$_} );
	}
	$document->commit;

	$document->get_eprint->generate_static;
		
	$session->terminate;


	return;
}


sub removeDocument
{
	my( $class,$docid ) = @_;

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'document' ); 
	my $document = $dataset->get_object( $session, $docid );
	
	if( !defined $document )
	{
		$session->terminate;
		die "document $docid does not exist";
	}
	if( !$document->remove )
	{
		$session->terminate;
		die "failed to remove document";
	}
	$document->get_eprint->generate_static;
		
	$session->terminate;

	return; 
}

sub addFile
{
	my( $class,$docid,$filename,$data ) = @_;

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'document' ); 
	my $document = $dataset->get_object( $session, $docid );
	
	if( !defined $document )
	{
		$session->terminate;
		die "document $docid does not exist";
	}
	if( $filename =~ m#[\?\*\ \t\/]# )
	{
		$session->terminate;
		die "$filename contains illegal characters";
	}
	unless( open( FH, ">".$document->local_path."/".$filename ) )
	{
		$session->terminate;
		die "Could not add $filename: $!";
	}
	print FH $data;
	close FH;

	$document->get_eprint->generate_static;
	$document->files_modified;
		
	$session->terminate;

	return;
}

sub removeFile
{
	my( $class,$docid,$filename ) = @_;

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'document' ); 
	my $document = $dataset->get_object( $session, $docid );
	if( !defined $document )
	{
		$session->terminate;
		die "document $docid does not exist";
	}
	if( $filename =~ m#[\?\*\ \t\/]# )
	{
		$session->terminate;
		die "$filename contains illegal characters";
	}
	unless( $document->remove_file( $filename ) )
	{
		$session->terminate;
		die "Could not remove $filename: $!";
	}
		
	$document->get_eprint->generate_static;
	$session->terminate;

	return;
}

sub removeEprint($)
{
	my( $class, $eprintid ) = @_;

	print STDERR "remove_eprint(".join(",",@_).")\n";

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'eprint' ); 
	my $eprint = $dataset->get_object( $session, $eprintid );
	if( !defined $eprint )
	{
		$session->terminate;
		die "eprint $eprintid does not exist";
	}
	$eprint->remove;
	$session->terminate;

	return();
}
	

sub getEprintFiles($)
{
	my( $class, $eprintid ) = @_;

	return $class->getEprint( $eprintid, 1 );
}
	
sub getEprint($;$)
{
	my( $class, $eprintid, $whole_files ) = @_;

	if( !defined $whole_files || $whole_files != 1 ) { $whole_files = 0; }
	#$whole_files = 0;# hack!

	print STDERR "get_eprint(".join(",",@_).")\n";
	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'eprint' ); 
	my $eprint = $dataset->get_object( $session, $eprintid );
	if( !defined $eprint )
	{
		$session->terminate;
		die "eprint $eprintid does not exist";
	}
	my $result = object_to_soap( $eprint, $whole_files );
	$session->terminate;
	
	return SOAP::Data->name("eprint" => $result );
}

# takes eprints data, returns an eprintid
sub putEprint($)
{
	my( $class, $data ) = @_;

	print STDERR "put_eprint(".join(",",@_).")\n";
	print STDERR Dumper( $data );
	$data = clone( $data );
	print STDERR Dumper( $data );

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;

	$data->{type} = "article" unless defined $data->{type};
	$data->{userid} = 1;

	my $eprint_ds = $session->get_repository->get_dataset( "inbox" );
	my $document_ds = $session->get_repository->get_dataset( "inbox" );
	my $eprint = $eprint_ds->create_object( $session, $data );
	if( !defined $eprint ) 
	{
		die "Failed to create eprint";
	}

	foreach my $doc_data ( @{$data->{documents}} )
	{
		$doc_data->{eprintid} = $eprint->get_id;
		my $files = delete $doc_data->{files};
		my $doc = $document_ds->create_object( $session, $doc_data );
		
		foreach my $filedata ( @{$files} )
		{
			my $fn = $doc->local_path."/".$filedata->{filename};
			open( FILE, ">$fn" ) || die "can't write file $fn: $!";
			print FILE $filedata->{data};
			close FILE;
		}
	}


	#my $eprint = $dataset->get_object( $session, $eprintid );
	#if( !defined $eprint )
	#{
		#$session->terminate;
		#die "eprint $eprintid does not exist";
	#}
	#my $result = object_to_soap( $eprint, $whole_files );

	my $id = $eprint->get_id;
	$eprint->generate_static;

	$session->terminate;

	return SOAP::Data->name( 'eprintID', $id )->type( "xsd:int" );
}

sub addDocument
{
	my( $class,$eprintid, $doc_data ) = @_;

	print STDERR "addDocument(".join(",",@_).")\n";
	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'eprint' ); 
	my $eprint = $dataset->get_object( $session, $eprintid );
	if( !defined $eprint )
	{
		$session->terminate;
		die "eprint $eprintid does not exist";
	}



	$doc_data->{eprintid} = $eprint->get_id;
	my $files = delete $doc_data->{files};
	my $doc = $document_ds->create_object( $session, $doc_data );
	
	foreach my $filedata ( @{$files} )
	{
		my $fn = $doc->local_path."/".$filedata->{filename};
		open( FILE, ">$fn" ) || die "can't write file $fn: $!";
		print FILE $filedata->{data};
		close FILE;
	}

	my $id = $doc->get_id;
	$eprint->generate_static;
	
	$session->terminate;

	return $id;
}

sub searchEprint($$)
{
	my( $class, $paramlist, $searchfields ) = @_;

	print STDERR "search(".join(",",@_).")\n";

	my $session = new EPrints::Session();
	if( !defined $session )
	{
		die "can't open session";
	}
	my $repository = $session->get_repository;
	my $dataset = $repository->get_dataset( 'archive' ); 
	my %p = ();
	$paramlist = [] unless defined $paramlist;
	foreach my $pair ( @{$paramlist} )
	{
		next unless defined $pair->{name};
		$p{$pair->{name}} = $pair->{value};
	}
	# page_size?
	my %params = (
		session=>$session,
		dataset=>$dataset
	);
	foreach( qw/ allow_blank satisfy_all custom_order / )
	{
		next unless defined $p{$_};
		$params{$_} = $p{$_};	
	}
	my $sexp = new EPrints::Search( %params );
	foreach my $sf ( @{$searchfields} )
	{
		my @fields = ();
		foreach my $fieldid ( @{$sf->{fields}} )
		{
			# needs a test first!
			my $field = EPrints::Utils::field_from_config_string( $dataset, $fieldid );
			push @fields, $field;
		}
		$sf->{match} = "IN" unless EPrints::Utils::is_set( $sf->{match} );
		$sf->{merge} = "ALL" unless EPrints::Utils::is_set( $sf->{merge} );
		$sexp->add_field( \@fields, $sf->{value}, $sf->{match}, $sf->{merge} );
	}
	$sexp->perform_search;

	my @r = ();
	$sexp->map( sub {
		my( $session, $dataset, $item ) = @_;
		push @r, object_to_soap( $item, 0 );
	} );
	$sexp->dispose;	
	
	$session->terminate;

	my $host = $repository->get_conf( "base_url" );
	return SOAP::Data->name('results', \@r )->attr( {"SOAP-ENC:arrayType"=>"eprints:Eprint[".(scalar @r)."]", "xmlns:eprints"=>"$host/EPrints/WebServices"});
}
	



sub object_to_soap($$);

sub object_to_soap($$)
{
	my( $obj, $whole_files ) = @_;

	my $data = EPrints::Utils::clone( $obj->get_data );

	my $datasetid = $obj->get_dataset->confid;

	if( $datasetid eq "eprint" )
	{
		my @docs = $obj->get_all_documents;
		my @d = ();
		foreach( @docs )
		{
			push @d, object_to_soap( $_, $whole_files );
		}	
		$data->{documents} = SOAP::Data->attr( {"SOAP-ENC:arrayType"=>"eprints:Document[".(scalar @d)."]" }, \@d );
	}

	if( $datasetid eq "document" )
	{
		my %files = $obj->files;
		my @f = ();
		foreach my $filename ( keys %files )
		{
			my $fileinfo = {};
			$fileinfo->{filename} = $filename;
			$fileinfo->{filesize} = SOAP::Data->type( "xsd:string", $files{$filename} );
			$fileinfo->{url} = $obj->get_url( $filename );
			if( $whole_files )
			{
				my $file = $obj->local_path."/".$filename;
				open( FH, $file ) || die "file '$file' read error: $!";
				$fileinfo->{data} = join( "", <FH> );
				close FH;
			}
			push @f, $fileinfo;
		}	
		$data->{files} = SOAP::Data->attr( {"SOAP-ENC:arrayType"=>"eprints:DocumentFilesItem[".(scalar @f)."]" }, \@f );
    }

	my $host = $obj->get_session->get_repository->get_conf( "base_url" );
	foreach my $field ( $obj->get_dataset->get_fields )
	{
		my $fname = $field->get_name;
		$data->{$fname} = soapy( $field, $data->{$fname} );
	}

	return SOAP::Data->name($datasetid, $data )->attr({ "xmlns:eprints"=>"$host/EPrints/WebServices"});
}

sub soapy
{
	my( $field, $value ) = @_;

	my $soaped;
	if( $field->get_property( "multiple" ) )
	{
		my $itemtype = xsd_type_item( $field );
		my $list = [];
		foreach( @{$value} )
		{
			push @{$list}, SOAP::Data->name( "item"=>soapy_single( $field, $_ ))->type( $itemtype );
		}
		my $arraytype=$itemtype."[".(scalar @{$list})."]";
		#$soaped = {list=> SOAP::Data->name('list'=>$list)->attr( {"SOAP-ENC:arrayType"=>$arraytype} )};
		$soaped = SOAP::Data->attr( {"SOAP-ENC:arrayType"=>$arraytype}, $list );
		return $soaped;
	}
	else
	{
		$soaped = soapy_single( $field, $value );
	}

	my $type = xsd_type( $field );
	return SOAP::Data->name(($field->get_name)=>$soaped)->type( $type );
}

sub soapy_single
{
	my( $field, $value ) = @_;

	my $soaped;
	if( $field->get_property( "hasid" ) )
	{
		$soaped = {};
		$soaped->{id} = SOAP::Data->name("id"=>$value->{id})->type( "xsd:string" );
		$soaped->{main} = SOAP::Data->name(main=>soapy_noid( $field, $value->{main} ));
	}
	else
	{
		$soaped = soapy_noid( $field, $value );
	}
	return $soaped;
}

sub soapy_noid
{
	my( $field, $value ) = @_;

	#return $value;
	my $type = xsd_type_basic( $field );
	return SOAP::Data->type( $type=>$value );
}

sub xsd_type_item 
{
	my( $field ) = @_;

	my $dsid = $field->get_dataset->confid;
	my $fn = $field->get_name;
	$fn =~ s/_([a-z])/\u$1/g;
	my $name ="eprints:\u$dsid\u$fn";

	return &xsd_type_single( $field, $name."Item" );
}

sub xsd_type
{
	my( $field ) = @_;

	my $dsid = $field->get_dataset->confid;
	my $fn = $field->get_name;
	$fn =~ s/_([a-z])/\u$1/g;
	my $name ="eprints:\u$dsid\u$fn";

	unless( $field->get_property( "multiple" ) )
	{
		return &xsd_type_single( $field, $name );
	}

	return $name;
}


sub xsd_type_single
{
	my( $field, $name ) = @_;

	unless( $field->get_property( "hasid" ) )
	{
		return &xsd_type_noid( $field, $name );
	}

	return $name;
}

sub xsd_type_noid
{
	my( $field, $name ) = @_;

	unless( $field->get_property( "multilang" ) )
	{
		return &xsd_type_basic( $field, $name );
	}

	return $name;
}


sub xsd_type_basic
{
	my( $field, $name ) = @_;

#	if( $field->get_search_group eq "int" )
#	{
#		return "xsd:int";
#	}
	if( $field->get_search_group eq "name" )
	{
		return "eprints:NameValue";
	}

	return( 'xsd:string' );
}


use UNIVERSAL qw(isa);
sub clone
{
	my( $data ) = @_;

	if( ref($data) eq "" )
	{
		return $data;
	}
	if( isa( $data, "ARRAY" ) )
	{
		my $r = [];
		foreach( @{$data} )
		{
			push @{$r}, clone( $_ );
		}
		return $r;
	}
	if( isa( $data, "HASH" ) )
	{
		my $r = {};
		foreach( keys %{$data} )
		{
			$r->{$_} = clone( $data->{$_} );
		}
		return $r;
	}


	# dunno
	return $data;			
}



