###########################################################################
#
# ImagePlug.pm -- simple text plugin
# A component of the Greenstone digital library software
# from the New Zealand Digital Library Project at the 
# University of Waikato, New Zealand.
#
# Copyright (C) 1999 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 ImagePlug;

use BasPlug;

use strict;
no strict 'refs'; # allow filehandles to be variables and viceversa

sub BEGIN {
    @ImagePlug::ISA = ('BasPlug');
}

my $arguments =
    [ { 'name' => "process_exp",
	'desc' => "{BasPlug.process_exp}",
	'type' => "regexp",
	'deft' => &get_default_process_exp(),
	'reqd' => "no" },
      { 'name' => "noscaleup",
	'desc' => "{ImagePlug.noscaleup}",
	'type' => "flag",
	'reqd' => "no" },
      { 'name' => "thumbnailsize",
	'desc' => "{ImagePlug.thumbnailsize}",
	'type' => "int",
	'deft' => "100",
	'range' => "1,",
	'reqd' => "no" },
      { 'name' => "thumbnailtype",
	'desc' => "{ImagePlug.thumbnailtype}",
	'type' => "string",
	'deft' => "gif",
	'reqd' => "no" },
      { 'name' => "screenviewsize",
	'desc' => "{ImagePlug.screenviewsize}",
	'type' => "int",
	'deft' => "0",
	'range' => "1,",
	'reqd' => "no" },
      { 'name' => "screenviewtype",
	'desc' => "{ImagePlug.screenviewtype}",
	'type' => "string",
	'deft' => "jpg",
	'reqd' => "no" },
      { 'name' => "converttotype",
	'desc' => "{ImagePlug.converttotype}",
	'type' => "string",
	'deft' => "",
	'reqd' => "no" },
      { 'name' => "minimumsize",
	'desc' => "{ImagePlug.minimumsize}",
	'type' => "int",
	'deft' => "100",
	'range' => "1,",
	'reqd' => "no" } ];

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



sub new {
    my ($class) = shift (@_);
    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
    push(@$pluginlist, $class);

    if(defined $arguments){ push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});}
    if(defined $options) { push(@{$hashArgOptLists->{"OptList"}},$options)};

    my $self = new BasPlug($pluginlist, $inputargs, $hashArgOptLists);

    # Check that ImageMagick is installed and available on the path (except for Windows 95/98)
    if (!($ENV{'GSDLOS'} eq "windows" && !Win32::IsWinNT())) {
	my $result = `identify 2>&1`;
	if ($? == -1 || $? == 256) {  # Linux and Windows return different values for "program not found"
	    $self->{'imagemagick_not_installed'} = 1;
	}
    }

       
    return bless $self, $class;
}

sub get_default_process_exp {
    my $self = shift (@_);

    return q^(?i)(\.jpe?g|\.gif|\.png|\.bmp|\.xbm|\.tif?f)$^; 
}

# this makes no sense for images
sub block_cover_image
{
    my $self =shift (@_);
    my ($filename) = @_;

    return;
}
# Create the thumbnail and screenview images, and discover the Image's
# size, width, and height using the convert utility.

sub run_convert {
    my $self = shift (@_);
    my $filename = shift (@_);   # filename with full path
    my $file = shift (@_);       # filename without path
    my $doc_obj = shift (@_);
    my $section = $doc_obj->get_top_section();
    
    my $verbosity = $self->{'verbosity'};
    my $outhandle = $self->{'outhandle'};

    # check the filename is okay
    return 0 if ($file eq "" || $filename eq "");

#    Code now extended to quote filenames in 'convert' commnads
#    Allows spaces in filenames, but note needs spaces to be escaped in URL as well
#    if ($filename =~ m/ /) {
#	print $outhandle "ImagePlug: \"$filename\" contains a space. choking.\n";
#	return undef;
#    }

    my $minimumsize = $self->{'minimumsize'};
    if (defined $minimumsize && (-s $filename < $minimumsize)) {
        print $outhandle "ImagePlug: \"$filename\" too small, skipping\n"
	    if ($verbosity > 1);
    }


    # Convert the image to a new type (if required).
    my $converttotype = $self->{'converttotype'};
    my $originalfilename = "";  # only set if we do a conversion
    my $type = "unknown";


    if ($converttotype ne "" && $filename =~ m/$converttotype$/) {

	$originalfilename = $filename;
	$filename = &util::get_tmp_filename() . ".$converttotype";
	$self->{'tmp_filename'} = $filename;

	my $command = "convert -interlace plane -verbose \"$originalfilename\" \"$filename\"";
	print $outhandle "$command\n" if ($verbosity > 2);
	my $result = '';
	$result = `$command`;
	print $outhandle "RESULT = $result\n" if ($verbosity > 2);

	$type = $converttotype;
    }
    

    # Add the image metadata
    my $url = $file;
    $url =~ s/ /%20/g;

    $doc_obj->add_metadata ($section, "Image", $url);

    # Also want to set filename as 'Source' metadata to be
    # consistent with other plugins
    $doc_obj->add_metadata ($section, "Source", $url);

    my ($image_type, $image_width, $image_height, $image_size) 
	= &identify($filename, $outhandle, $verbosity);

    if ($image_type ne " ") {
	$type = $image_type;
    }
    
    $doc_obj->add_metadata ($section, "FileFormat", $type);
    $doc_obj->add_metadata ($section, "FileSize",   $image_size);

    $doc_obj->add_metadata ($section, "ImageType",   $image_type);
    $doc_obj->add_metadata ($section, "ImageWidth",  $image_width);
    $doc_obj->add_metadata ($section, "ImageHeight", $image_height);
    $doc_obj->add_metadata ($section, "ImageSize",   $image_size);

    $doc_obj->add_metadata ($section, "srclink", 
			    "<a href=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Image]\">");
    $doc_obj->add_metadata ($section, "/srclink", "</a>");

    $doc_obj->add_metadata ($section, "srcicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Image]\" width=100>");

    
    # Add the image as an associated file
    $doc_obj->associate_file($filename,$file,"image/$type",$section);


    # Make the thumbnail image
    my $thumbnailsize = $self->{'thumbnailsize'} || 100;
    my $thumbnailtype = $self->{'thumbnailtype'} || 'gif';
	
    my $thumbnailfile = &util::get_tmp_filename() . ".$thumbnailtype";
    $self->{'tmp_filename2'} = $thumbnailfile;

    # Generate the thumbnail with convert
    my $command = "convert -interlace plane -verbose -geometry $thumbnailsize" 
	. "x$thumbnailsize \"$filename\" \"$thumbnailfile\"";
    print $outhandle "THUMBNAIL: $command\n" if ($verbosity > 2);
    my $result = '';
    $result = `$command 2>&1` ;
    print $outhandle "THUMB RESULT: $result\n" if ($verbosity > 2);

    # Add the thumbnail as an associated file ...
    if (-e "$thumbnailfile") { 
	$doc_obj->associate_file("$thumbnailfile", "thumbnail.$thumbnailtype", 
				 "image/$thumbnailtype",$section);
	$doc_obj->add_metadata ($section, "ThumbType", $thumbnailtype);
	$doc_obj->add_metadata ($section, "Thumb", "thumbnail.$thumbnailtype");

	$doc_obj->add_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Thumb]\" width=[ThumbWidth] height=[ThumbHeight]>");
    }

    # Extract Thumnail metadata from convert output
    if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
	$doc_obj->add_metadata ($section, "ThumbWidth", $1);
	$doc_obj->add_metadata ($section, "ThumbHeight", $2);
    }

    # Make a screen-sized version of the picture if requested
    if ($self->{'screenviewsize'}) {

	# To do: if the actual image smaller than the screenview size,
	# we should use the original !

	my $screenviewsize = $self->{'screenviewsize'};
	my $screenviewtype = $self->{'screenviewtype'} || 'jpeg';
	my $screenviewfilename = &util::get_tmp_filename() . ".$screenviewtype";
	$self->{'tmp_filename3'} = $screenviewfilename;

	# make the screenview image
	my $command = "convert -interlace plane -verbose -geometry $screenviewsize" 
	    . "x$screenviewsize \"$filename\" \"$screenviewfilename\"";
	print $outhandle "$command\n" if ($verbosity > 2);
	my $result = "";
	$result = `$command  2>&1` ;
	print $outhandle "$result\n" if ($verbosity > 3);

	# get screenview dimensions, size and type
        if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) {
	    $doc_obj->add_metadata ($section, "ScreenWidth", $1);
	    $doc_obj->add_metadata ($section, "ScreenHeight", $2);
	}
	else {
	    $doc_obj->add_metadata ($section, "ScreenWidth", $image_width);
	    $doc_obj->add_metadata ($section, "ScreenHeight", $image_height);
	}

	#add the screenview as an associated file ...
	if (-e "$screenviewfilename") { 
	    $doc_obj->associate_file("$screenviewfilename", "screenview.$screenviewtype",
				     "image/$screenviewtype",$section);
	    $doc_obj->add_metadata ($section, "ScreenType", $screenviewtype);
	    $doc_obj->add_metadata ($section, "Screen", "screenview.$screenviewtype");

	    $doc_obj->add_metadata ($section, "screenicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Screen]\" width=[ScreenWidth] height=[ScreenHeight]>");
	} else {
	    print $outhandle "ImagePlug: couldn't find \"$screenviewfilename\"\n";
	}
    }

    return $type;


}



# Discover the characteristics of an image file with the ImageMagick
# "identify" command.

sub identify { 
    my ($image, $outhandle, $verbosity) = @_;

    # Use the ImageMagick "identify" command to get the file specs
    my $command = "identify \"$image\" 2>&1";
    print $outhandle "$command\n" if ($verbosity > 2);
    my $result = '';
    $result = `$command`;
    print $outhandle "$result\n" if ($verbosity > 3);

    # Read the type, width, and height
    my $type =   'unknown';
    my $width =  'unknown';
    my $height = 'unknown';

    my $image_safe = quotemeta $image;
    if ($result =~ /^$image_safe (\w+) (\d+)x(\d+)/) {
	$type = $1;
	$width = $2;
	$height = $3;
    }

    # Read the size
    my $size = "unknown";
    if ($result =~ m/^.* ([0-9]+)b/) {
	$size = $1;
    }
    elsif ($result =~ m/^.* ([0-9]+)(\.([0-9]+))?kb?/) {
	$size = 1024 * $1;
	if (defined($2)) {
	    $size = $size + (1024 * $2);
	    # Truncate size (it isn't going to be very accurate anyway)
	    $size = int($size);
	}
    }

    print $outhandle "file: $image:\t $type, $width, $height, $size\n" 
	if ($verbosity > 2);

    # Return the specs
    return ($type, $width, $height, $size);
}


# The ImagePlug read() function. 
# ImagePlug overrides read() because there is no need to read the actual 
# text of the file in, because the contents of the file is not text...
#
# Return number of files processed, undef if can't process
# Note that $base_dir might be "" and that $file might 
# include directories

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

    my $outhandle = $self->{'outhandle'};

    #check process and block exps, smart block, etc
    my ($block_status,$filename) = $self->read_block(@_);    
    return $block_status if ((!defined $block_status) || ($block_status==0));

    print STDERR "<Processing n='$file' p='ImagePlug'>\n" if ($gli);
    print $outhandle "ImagePlug processing $file\n"
	    if $self->{'verbosity'} > 1;

    # None of this works very well on Windows 95/98...
    if ($ENV{'GSDLOS'} eq "windows" && !Win32::IsWinNT()) {
	if ($gli) {
	    print STDERR "<ProcessingError n='$file' r='Windows 95/98 not supported'>\n";
	}
	print $outhandle "ImagePlug: Windows 95/98 not supported\n";
	return -1;
    }

    # None of this is going to work very well without ImageMagick...
    if ($self->{'imagemagick_not_installed'}) {
	if ($gli) {
	    print STDERR "<ProcessingError n='$file' r='ImageMagick not installed'>\n";
	}
	print $outhandle "ImagePlug: ImageMagick not installed\n";
	return -1;
    }

    #if there's a leading directory name, eat it...
    $file =~ s/^.*[\/\\]//;
    
    # create a new document
    my $doc_obj = new doc ($filename, "indexed_doc");
    $doc_obj->set_OIDtype ($processor->{'OIDtype'}, $processor->{'OIDmetadata'});    
    $doc_obj->add_utf8_metadata($doc_obj->get_top_section(), "Plugin", "$self->{'plugin_type'}");

    #run convert to get the thumbnail and extract size and type info
    my $result = run_convert($self, $filename, $file, $doc_obj);
    
    if (!defined $result)
    {
	if ($gli) {
	    print STDERR "<ProcessingError n='$file'>\n";
	}
	print $outhandle "ImagePlug: couldn't process \"$filename\"\n";
	return -1; # error during processing
    }

     
    #create an empty text string so we don't break downstream plugins
    my $text = &gsprintf::lookup_string("{BasPlug.dummy_text}",1);
   
    # include any metadata passed in from previous plugins 
    # note that this metadata is associated with the top level section
    my $section = $doc_obj->get_top_section();
    $self->extra_metadata ($doc_obj, $section, $metadata);

    # do plugin specific processing of doc_obj
    unless (defined ($self->process(\$text, $pluginfo, $base_dir, $file, $metadata, $doc_obj))) {
	print STDERR "<ProcessingError n='$file'>\n" if ($gli);
	return -1;
    }

    # do any automatic metadata extraction
    $self->auto_extract_metadata ($doc_obj);

    # if we haven't found any Title so far, assign one
    # this was shifted to here from inside read()
    $self->title_fallback($doc_obj,$section,$file);
    # add an OID
    $doc_obj->set_OID();
    $doc_obj->add_utf8_text($section, $text);

    # process the document
    $processor->process($doc_obj);

    # clean up temporary files - we do this here instead of in  
    # run_convert becuase associated files aren't actually copied 
    # until after process has been run.
    if (defined $self->{'tmp_filename'} && 
	-e $self->{'tmp_filename'}) {
      &util::rm($self->{'tmp_filename'})
    }

    if (defined $self->{'tmp_filename2'} && 
	-e $self->{'tmp_filename2'}) {
      &util::rm($self->{'tmp_filename2'})
    }
    if (defined $self->{'tmp_filename3'} && 
	-e $self->{'tmp_filename3'}) {
      &util::rm($self->{'tmp_filename3'})
    }

    $self->{'num_processed'}++;

    return 1;
}

# do plugin specific processing of doc_obj
sub process {
    my $self = shift (@_);
    my ($textref, $pluginfo, $base_dir, $file, $metadata, $doc_obj) = @_;
    my $outhandle = $self->{'outhandle'};
    
    return 1;
}

1;











