###########################################################################
#
# HTMLPlug.pm -- basic html 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.
#
###########################################################################

# creates simple single-level document from .htm or .html files 
# (case-insensitive match on filenames). Adds Title metadata 
# taken from <title> tags if found otherwise first 100 characters
# outside of tags.
# will also attempt to include images which it will search for in
# the same directory as the document itself (it will also search 
# directories relative to that directory).

# this plugin currently does nothing with href links so relative links 
# may become broken.


package HTMLPlug;

use BasPlug;
use sorttools;
use html;
use util;

use File::Basename;

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

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

    $self->{'f2d_mapping'} = undef;
    $self->{'f2d_partitioned_dirs'} = [];

    return bless $self, $class;
}

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

    return 0; # this is not a recursive plugin
}

#--
# Convert filename to lookup filename
#--
sub f2d_lookup_filename
{
    my ($self,$filename) = @_;

    my $coldir = $ENV{'GSDLCOLLECTDIR'};
    my $dirsep_re = &util::get_re_dirsep();
    my $supress_dirs = "(import|building)";

    my @part_dirs = @{$self->{'f2d_partitioned_dirs'}};

    if (scalar(@part_dirs)>0)
    {
	my $joined_part_dirs = join('|',@part_dirs);
	$supress_dirs .= "($dirsep_re)($joined_part_dirs)";
    }

    my $lookup_filename = $filename;
    $lookup_filename =~ s/^$coldir($dirsep_re)//;
    $lookup_filename =~ s/^$supress_dirs($dirsep_re)//;

    return $lookup_filename;
}


#--
# file to document mapping
#--

sub rec_f2d_mapping
{
    my ($self,$dirname) = @_;

    # read all the files in the directory
    if (!opendir(DIR, $dirname))
    {
	print STDERR "HTMLPlug: WARNING - couldn't read directory $dirname";
	print STDERR " during file to doc mapping\n";
	return;
    }
    my @dir = readdir (DIR);
    closedir (DIR);

    # process each file
    my $subfile;
    foreach $subfile (@dir) 
    {
	if ($subfile !~ /^\.\.?$/) 
	{
	    my $filename = &util::filename_cat($dirname, $subfile);

	    if (-d $filename)
	    {
		my $dirname = $filename;
		$self->rec_f2d_mapping($dirname);
	    }
	    else
	    {
		if ($subfile =~ m/\.(html?(\.gz)?)$/i && (-e $filename))
		{
		    # add mapping

		    print STDERR "HTMLPlug: Precalculating OID for $subfile\n"
			if (defined($self->{'verbosity'}));

		    my $oid = doc::_calc_OID(undef,$filename);
		    my $lookup_filename 
			= $self->f2d_lookup_filename($filename);

		    $self->{'f2d_mapping'}->{$lookup_filename} = $oid;

		    if ($subfile =~ m/index\.(html?(\.gz)?)$/i)
		    {
			# Cater for links such as "/paper/" mapping to
			#  "/paper/index.html"
			my $lookup_dirname 
			    = $self->f2d_lookup_filename($dirname);

			$self->{'f2d_mapping'}->{$lookup_dirname} = $oid;
		    }
		}
		elsif ($subfile =~ /\.(gif|jpg|jpeg|png)$/i)
		{
		    # convert to png ?

		    # Hard link in build directory		    
		    #--
		    my $coldir    = $ENV{'GSDLCOLLECTDIR'};
		    my $dirsep_re = &util::get_re_dirsep();
		    my $iorb_re   = "import|building";

		    my ($dirsep,$iorb)
			= ($filename =~ m/^$coldir($dirsep_re)($iorb_re)/);

		    my $copyname = $filename;
		    $copyname 
			=~ s/^${coldir}${dirsep}${iorb}
			    /${coldir}${dirsep}building_images${dirsep}imgsrc/x;

  	            if (!-e $copyname)
		    {
                        print STDERR "HTMLPlug: Hard linking $subfile\n"
                            if (defined($self->{'verbosity'}));
			&util::hard_link($filename,$copyname);
		    }

		}
	    }
	    
	}
    }
}

sub build_file_to_doc_mapping
{
    my ($self,$base_dir) = @_;

    $self->{'f2d_mapping'} = {};

    my $bimages_dir 
	= &util::filename_cat($ENV{'GSDLCOLLECTDIR'},"building_images","imgsrc");
    if (!-e $bimages_dir && !mkdir ($bimages_dir, 0775)) 
    {
	print STDERR "HTMLPlug:: Could not create directory $bimages_dir\n";
	return;
    }

    $self->rec_f2d_mapping($base_dir);
}

#--
#--

# 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) = @_;

    my $verbosity = $processor->{'verbosity'};
    $self->{'verbosity'} = $verbosity;

    if (!defined($self->{'f2d_mapping'}))
    {
	print STDERR "HTMLPlug: building OID mapping\n" 
	    if $processor->{'verbosity'};

	$self->build_file_to_doc_mapping($base_dir,$verbosity);
    }
    my $filename = &util::filename_cat($base_dir, $file);
    my $absdir = $filename;
    $absdir =~ s/[^\/\\]*$//;

    if ($file =~ /\.(gif|jpg|jpeg|png)$/i)
    {
	return 0;
    }

    return undef unless ($filename =~ /\.(html?(\.gz)?)$/i && (-e $filename));
    return undef if (-d $filename);

    my $gz = 0;
    if (defined $2) {
	$gz = $2;
	$gz = 1 if ($gz =~ /\.gz/i);
    }

    print STDERR "HTMLPlug: processing $file\n" if $processor->{'verbosity'};

    # create a new document
    my $doc_obj = new doc ($file, "indexed_doc");
   
    if ($gz) {
	open (FILE, "zcat $filename |") || die "HTMLPlug::read - zcat can't open $filename\n";
    } else {
	open (FILE, $filename) || die "HTMLPlug::read - can't open $filename\n";
    }
    my $cursection = $doc_obj->get_top_section();

    # look up precalculated OID
    my $lookup_filename = $self->f2d_lookup_filename($filename);
    my $precalc_oid = $self->{'f2d_mapping'}->{$lookup_filename};

    # read in HTML file
    my $text = "";
    my $line = "";
    my $donehead = 0;
    my $title = "";
    while (defined ($line = <FILE>)) {
	$text .= $line;
    }

    # remove line breaks
    $text =~ s/\s+/ /g;

    my $foundtitle = 0;
    # don't use title tag if Title has been passed
    # in from another plugin
    if (defined $metadata->{'Title'}) {
	$foundtitle = 1;
    }
    if (!$foundtitle) {
	# see if there's a <title> tag
	if ($text =~ /<title[^>]*>([^<]*)<\/title[^>]*>/i) {
	    if (defined $1) {
		my $title = $1;
		if ($title =~ /\w/) {
		    $doc_obj->add_metadata ($cursection, "Title", $title);
		    $foundtitle = 1;
		}
	    }
	}
    }
    # if no title use first 100 characters
    if (!$foundtitle) {
	my $tmptext = $text;
	$tmptext =~ s/<[^>]*>//g;
	my $title = substr ($tmptext, 0, 100);
	$doc_obj->add_metadata ($cursection, "Title", $title);
    }
    
    # remove header rubbish
    $text =~ s/^.*?<body[^>]*>//i;

    # usemap="./#index" not handled correctly => change to "#index"
    $text =~ s/(<img[^>]*?usemap\s*=\s*\"?)([^\">]+)(\"?[^>]*>)/
	&replace_usemap_links($1, $2, $3)/ige;

    # fix up the href links
    $f2d_mapping = $self->{'f2d_mapping'};

    $text =~ s/(<(a|area)\s+[^>]*?href\s*=\s*\"?)([^\">]+)(\"?[^>]*>)/
       &replace_href_links($filename,$precalc_oid,$f2d_mapping,$1,$3,$4)/ige;

    # add a newline at the beginning of each paragraph
    $text =~ s/(.)\s*<p\b/$1\n\n<p/gi;
    
    # add a newline every 80 characters at a word boundary
    # Note: this regular expression puts a line feed before
    # the last word in each section, even when it is not
    # needed.
    $text =~ s/(.{1,80})\s/$1\n/g;


    # Store URL for page as metadata
    my $web_url = "http://$lookup_filename";
    $doc_obj->add_metadata($cursection, "URL", $web_url);

    my $import_url = $filename;
    $import_url =~ s/^$ENV{'GSDLCOLLECTDIR'}/_httpcollection_/;

    # Add base tag so images can find correct location
    my $index_url = $import_url;
    $index_url =~ s/_httpcollection_\/import/_httpcollection_\/index\/imgsrc/;

    my $dirsep_re = &util::get_re_dirsep();
    my @base_url_split = split(/$dirsep_re/,$index_url);
    pop(@base_url_split);
    my $base_url = join('/',@base_url_split,""); # force / at end

    $text = "<base href=\"$base_url\">\n$text";
    $doc_obj->add_text ($cursection, $text);

    # Add metadata that has been provided externally
    #--
    foreach $field (keys(%$metadata)) {
	# $metadata->{$field} may be an array reference
	if (ref ($metadata->{$field}) eq "ARRAY") {
	    map { 
		$doc_obj->add_metadata ($cursection, $field, $_); 
	    } @{$metadata->{$field}};
	} else {
	    $doc_obj->add_metadata ($cursection, $field, $metadata->{$field}); 
	}
    }

    # Fix OID so it is the same as the pre-calculated OID
    $doc_obj->set_OID($precalc_oid);

    my $set_oid = $doc_obj->get_OID();
    if ($precalc_oid ne $set_oid) # check (for super safety!)
    {
	print STDERR "Warning: pre-calculated OID and current OID differnt:";
	print STDERR " $filename\n";
    }


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

    return 1; # processed the file
}

# support for fixing up links to work as a GSDL collection
#--

sub eval_dir_dots
{
    # evaluate any "../" to next directory up
    # evaluate any "./" as here
    #--
    my ($self,$filename) = @_;

    my $dirsep_os = &util::get_os_dirsep();
    my @dirsep = split(/$dirsep_os/,$filename);

    my @eval_dirs = ();
    foreach $d (@dirsep)
    {
	if ($d eq "..")
	{
	    pop(@eval_dirs);
	}
	elsif ($d eq ".")
	{
	    # do nothing!
	}
	else
	{
	    push(@eval_dirs,$d);
	}
    }

    return &util::filename_cat(@eval_dirs);
}

sub replace_href_links 
{
    my ($this_filename,$this_oid, $f2d_mapping, $front,$link,$back) = @_;

    return $front.$link.$back if ($link =~ m/\.(gif|jpg|jpeg|png)$/i);

    if ($link =~ m/^(http|ftp|file):/i) 
    {
        # this should really check that the link ends in .htm*
	#****
	my $http_as_filename = $link;
	$http_as_filename =~ s/^(http|ftp|file):\/\///i;

	my ($before_hash,$after_hash) 
	    = ($http_as_filename =~ m/^([^\#]*)\#?(.*)$/);
	       
	if ($link =~ m/^(http|ftp):/i)
	{
	    # Turn url (using /) into file name (possibly using \ on windows)
	    my @http_dir_split = split('/',$before_hash);
	    $http_as_filename = &util::filename_cat(@http_dir_split);
	}

	$http_as_filename = $self->eval_dir_dots($http_as_filename);
					 
	if (defined($f2d_mapping->{$http_as_filename}))
	{
	    # transform link into a local link (and then let it be
	    #   processed by later code)
	    $link = $http_as_filename;

	    $link = "_httpdocument_&cl=_cgiargcl_&d=$oid";
	    $link .= "#$after_hash" if ($after_hash ne "");
	}
	else
	{
	    # external link => set it up to pass through off-site page

	    my $link_safe = $link;
	    &html::urlsafe($link_safe);

	    $link = "_httpextlink_&href=$link_safe&d=$this_oid";

	}
    }	
    elsif ($link !~ m/^(mailto|news):/i)
    {
	my ($before_hash,$after_hash) = ($link =~ m/^([^\#]*)\#?(.*)$/);
	my $link_filename;

	if ($before_hash =~ m/^\//)
	{
	    my $dirsep_re = &util::get_re_dirsep();
	    my $lookup = $self->f2d_lookup_filename($this_filename);
	    my @lookup_split = split(/$dirsep_re/,$lookup);
	    my $domname = shift(@lookup_split);
	    $link_filename = &util::filename_cat($domname,$before_hash);
	}
	else
	{
	    # Turn relative file path into full path
	    if ($before_hash eq "") # handle links such as <a href="#x">
	    {
		$link_filename = $this_filename;
	    }
	    else
	    {
		my $dirname = &File::Basename::dirname($this_filename);
		$link_filename = &util::filename_cat($dirname,$before_hash);
	    }
	}

	$link_filename = $self->eval_dir_dots($link_filename);
	my $lookup_filename = $self->f2d_lookup_filename($link_filename);

	my $oid = $f2d_mapping->{$lookup_filename};	
	if (defined($oid))
	{
	    $link = "_httpdocument_&cl=_cgiargcl_&d=$oid";
	    $link .= "#$after_hash" if ($after_hash ne "");
	}
	else
	{
	    print STDERR "HTMLPlug WARNING:";
	    print STDERR " Could not find link: \"$lookup_filename\". ";
	    print STDERR " Deactivating link\n";
	    
	    $link = "_httpextlink_&d=$this_oid";
	}
    }
    else
    {
	if ($link !~ m/^(mailto|news):/i)
	{
	    print STDERR "HTMLPlug WARNING: Unhandled type of link, \"$link\"\n";
	}
	else
	{
	    my $link_safe = $link;
	    &html::urlsafe($link_safe);

	    $link = "_httpextlink_&href=$link_safe&d=$this_oid";
	}
    }

    my $fixed_tag = "${front}${link}${back}";

    return $fixed_tag;
}


sub replace_usemap_links 
{
    my ($front, $link, $back) = @_;

    $link =~ s/^\.\///;

    return "${front}${link}${back}";
}



1;











