#!/usr/bin/perl
# vim:ft=perl:cindent:ts=8
#
#    dwww-build-menu - create Debian Documentation Menu
#    Copyright (C) 2003 Robert Luberda <robert@debian.org>
#
#    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#  
#
# $Id: dwww-build-menu,v 1.14 2003/05/16 17:51:15 robert Exp $
#

use strict;

use Debian::Dwww::Utils;
use Debian::Dwww::DocBase;
use Debian::Dwww::Initialize;

my $dwwwconf 	= &DwwwInitialize("/etc/dwww/dwww.conf");

my $out_dir    	= $dwwwconf->{'DWWW_HTMLDIR'} . "/menu";
my $out_dir_tmp	= "$out_dir.temp.$$";
my $title    	= $dwwwconf->{'DWWW_TITLE'};
undef %{$dwwwconf};

my $templates_dir 		= "/usr/share/dwww";
my $template_single_start 	= "$templates_dir/menu.single.start";
my $template_single_end 	= "$templates_dir/menu.end";
my $template_all_start 		= "$templates_dir/menu.all.start";
my $template_all_end 		= "$templates_dir/menu.end";
my $template_error_start 	= "$templates_dir/menu.errors.start";
my $template_error_end 		= "$templates_dir/menu.errors.end";

my $single_index = 'one';
my $all_index    = 'all';

my @known_formats=(
	'html',
	'info',
	'text',
	'rtf',
	'debiandoc-sgml',
	'docbook-xml',
	'dvi',
	'latex',
	'linuxdoc-sgml',
	'pdf',
	'postscript',
	'ps',
	'sgml',
	'tar',
	'texinfo',
	'dwww-url' # internal dwww's format
	);
my $dwww_url = "/cgi-bin/dwww";
my %href  = 
	(
	'debiandoc-sgml'=>	 "$dwww_url?type=application/sgml&amp;location=",
	'docbook-xml'	=>	 "$dwww_url?type=text/xml&amp;location=",
	'dvi'		=>	 "$dwww_url?type=application/x-dvi&amp;location=",
	'html'		=>	 "$dwww_url?type=html&amp;location=",
	'info'		=>	 "/cgi-bin/info2www?file=",
	'latex'		=>	 "$dwww_url?type=application/x-latex&amp;location=",
	'linuxdoc-sgml'	=>	 "$dwww_url?type=application/sgml&amp;location=",
	'pdf'		=>	 "$dwww_url?type=application/pdf&amp;location=",
	'postscript'	=>	 "$dwww_url?type=application/postscript&amp;location=",
	'ps'		=>	 "$dwww_url?type=application/postscriptps&amp;location=",
	'rtf'		=>	 "$dwww_url?type=text/rtf&amp;location=",
	'sgml'		=>	 "$dwww_url?type=application/sgml&amp;location=",
	'tar'		=>	 "$dwww_url?type=application/tar&amp;location=",
	'texinfo'	=>	 "$dwww_url?type=application/x-texinfo&amp;location=",
	'dwww-url'	=>	 "",
	'text'		=>	 "$dwww_url?type=text/plain&amp;location="
	);

		
my %sections =  ();
my %section_names = (); # real names of sections
my %node_section  = (); # is node section ?
my %map_section   = (); # for section without docs, the nearest section with docs
my %doc_list      = (); # list of documents 
my %files         = (); # list of files


my $opt_verbose = 0;
my $opt_debug   = 0;


mkdir($out_dir_tmp, 0755) or die "Can't create directory $out_dir_tmp: $!\n";

open ERRFILE , ">$out_dir_tmp/errors.html" or &CleanAndDie("Can't create file errros.html: $!");
print ERRFILE &TemplateFile($template_error_start, { 'TITLE'       => &HTMLEncode($title) });
$ErrorProc = \&ErrorHandle;

&ParseDocDir("/usr/share/doc-base");
&ParseDocDir("/var/lib/dwww/menu-method");

&FindMapSections;

&BuildMenus;

link("$out_dir_tmp/" . &SectionToFileName((sort keys %sections)[0], $single_index), 
     "$out_dir_tmp/index.html") or
	print STDERR "Can't create index.html link: $!\n";
system("rm -f \"$out_dir\"/*; rmdir \"$out_dir\"") if (-d "$out_dir");
rename("$out_dir_tmp", "$out_dir") or &CleanAndDie("Can't rename $out_dir_tmp to $out_dir: $!");

print ERRFILE &TemplateFile($template_error_end, {});
close ERRFILE;
exit (0);

#########################################################################
#
# Local functions
#
sub CleanAndDie {
	print STDERR "$_\n";
	system("rm -f \"$out_dir_tmp\"/*; rmdir \"$out_dir_tmp\"") if (-d "$out_dir_tmp");
	exit(1);
}

my $last_err_file = '';
## ErrorHandle($file, $message)
sub ErrorHandle {
	my $file = shift;
	my $msg  = shift;

	print ERRFILE "<dt><br></dt>\n" unless $last_err_file eq $file;
	print ERRFILE "<dt><a href=\"file://" . &URLEncode($file)  . "\">" 
			. &HTMLEncode($file) . "</a></dt>\n";
	print ERRFILE "<dd>" . &HTMLEncode($msg) . "</dd>\n";
	$last_err_file = $file;
}


sub ParseDocDir {
        my $dir = shift;

        if (not (opendir DOCBASEDIR, $dir)) {
                print STDERR "Can't open directory $dir: $!\n" if $opt_verbose;
                return;
        }
        
        while (my $f = readdir(DOCBASEDIR))
        {
		next if $f =~ /^\./;
		next if -d $f;
		
                if (defined (my $entry = &ParseDocBaseFile("$dir/$f"))) {
                        &AddNewEntry("$dir/$f", $entry);
			undef %{$entry};
                }
        }
                        
}


# Adds %entry to @documents
sub AddNewEntry() {
        my $file = shift;
	my $entry = shift;
	my $doc_entry = { };
        my $known = 0;
	my $sec   = undef;
        
	if (exists $entry->{'dwww-section'}) {
		&DwwwSection2Section($entry);
	}
	
        if (defined $entry->{'document'}) {
                $doc_entry->{'document'} = $entry->{'document'};
	}
	
        if ((not defined $entry->{'title'}) or $entry->{'title'} =~ m/^\s*$/) {
                &ErrorHandle($file,"No (or empty) title!");
                $doc_entry->{'title'} = "dwww error: No (or empty) title found in $file";
        } else {
		$doc_entry->{'title'} = $entry->{'title'};
	}
			

        if ((not defined $entry->{'abstract'}) or $entry->{'abstract'} =~ m/^\s*$/) {
                &ErrorHandle($file, "Warning: no (or empty) abstract!");
                $doc_entry->{'abstract'} = "dwww warning: No (or empty) abstract found in $file";
        } else {
		$doc_entry->{'abstract'} = $entry->{'abstract'}
	}
        
        if ((not defined ($sec = $entry->{'section'})) or $sec =~ m/^\s*$/)
        {
                &ErrorHandle($file, "No (or empty) section!");
                $sec = 'unknown';
        }

        foreach my $f (@known_formats) {
		my $w;
                if (defined ( $w = $entry->{'formats'}->{$f}->{'index'})
		     or defined ($w = $entry->{'formats'}->{$f}->{'files'})) {
                        if (exists $files{$w}) 
                        {
                		&ErrorHandle($file, "File already known: \"$w\"");
                                return;
                        }
                                
                        if ( $f eq 'dwww-url' or -r $w) {
                                $files{$w} = '1';
				$doc_entry->{'frm_name_' . $known} = $f;
				$doc_entry->{'frm_file_' . $known} = $w;
                                $known++;
                        } else {
                		&ErrorHandle($file, "Can't read \"$w\": $!");
                        }

                }
        }
        if ($known == 0 ) {
                &ErrorHandle($file, "No known formats!");
                return;
        }
	
	$sec = &AddCanonSection($sec);
	$doc_entry->{'section'} = $sec;
        $doc_entry->{'frm_count'} = $known; 
        
	push(@{$doc_list{$sec}}, $doc_entry);

}


sub sort_documents {
	my $res;

        $res = $a->{'section'} cmp $b->{'section'};
        
        $res = lc $a->{'title'} cmp lc $b->{'title'} if $res == 0;

        return $res;
}

sub AddCanonSection ($) {
        my $arg  = shift;
        my $sec  = '';
        my $nsec = '';
	my $prev = '';

        foreach my $s (split /\/+/, $arg) {
                $sec 	.= '/' unless $sec eq '';
                $nsec 	.= '/' unless $nsec eq '';
                $sec 	.= lc $s;
		$nsec 	.= ucfirst $s;
                $sections{$sec} 	= 0 unless exists $sections{$sec};
		$section_names{$sec} 	= $nsec;
		$node_section{$prev}    = 1 unless $prev eq '';
		$prev    = $sec;
        }
                
        $sections{$sec}++;
        return $sec;
        
}


sub FindMapSections() {
	my @ss = sort keys %sections;
	undef %map_section;

        for (my $i=$#ss; $i >= 0; $i--) {
		my $sec = $ss[$i];
		if ($sections{$ss[$i]} == 0)
		{
			for (my $j = $i; $j <= $#ss; $j++){
				if( exists $map_section{$ss[$j]})
				{
					$map_section{$sec} = $map_section{$ss[$j]};
					last;
				}
			}
		}
		else
		{
			$map_section{$sec} = $sec;
		}
	}
}

sub SectionToFileName() {
	$_    = shift;
	my $type = shift; 
	
	$_ = $map_section{$_};
	s/\//_/g;
	return "s$_.html" if ($type eq $single_index);
	return "$_";
}
	

sub SectionsToHTML() {
        my $cur_sec = shift;
	my $type  = shift;
        my $depth = 0;
        my $pr_depth = -1;
	my $res = '';
	my $node='';
	my $cur_depth = ($cur_sec =~ s;/;/;g) + 0;

        foreach my $s (sort keys %sections) {		
                $depth = ($s =~ s;/;/;g) + 0;
		my $ps = $s;
		$ps =~ s#[^/]+$##; 

		next unless ($type eq $all_index or $ps eq '' or index($cur_sec .'/', $ps) == 0);
		
                &CleanAndDie('Internal error') if ($depth > $pr_depth + 1);
                $res .= "<ul>\n" if ($depth > $pr_depth);
                
                while ($pr_depth > $depth) {
                        $res .= "</ul>\n";
                        $pr_depth--;
                }
                $pr_depth = $depth;
		my $ns = $section_names{$s};
		$node = exists $node_section{$s} ? ' ...' : '';
		$ns =~ s|^.*/||g;
		$ns .= $node;
                
                if ($s eq $cur_sec) {
                        $res .= "<li><strong>$ns</strong>\n";
                } elsif ($type eq $single_index and ($sections{$s} > 0 or index($cur_sec, $s .'/') != 0 )){
                        $res .= "<li><a href=\"" . &SectionToFileName($s, $type) . "\">$ns</a>\n";
                } elsif ($sections{$s} > 0 and $type eq $all_index) {
                        $res .= "<li><a href=\"#" . &SectionToFileName($s, $type) . "\">$ns</a>\n";
                } else {
                        $res .= "<li>$ns\n";
                }
                        
        }
        
        while ($pr_depth > -1) {
                $res .= "</ul>\n";
                $pr_depth--;
        }

	return $res;
                
}


sub GetURL{
	my $format_name = shift;
	my $format_url  = shift;

	$format_url = &URLEncode($format_url) unless ($format_name eq 'dwww-url');

	return "\"$href{$format_name}" . $format_url . "\"";
	
	
}


	
sub DocumentToHTML($) {
	my $doc = shift; # reference to hash 
	my $res = '';	

	$res .= "<dt>";
	$res .= "<a href=" . &GetURL($doc->{'frm_name_0'}, $doc->{'frm_file_0'}); 
	$res .= " name=\"" . &HTMLEncode($doc->{'document'}) . "\""
	       if defined ($doc->{'document'});
	$res .= ">" . &HTMLEncode($doc->{'title'}) . "</a></dt>\n";
	$res .= "<dd>" . &HTMLEncodeAbstract($doc->{'abstract'});
	if ($doc->{'frm_count'} > 1) {
		$res .= "\n<br><b>Formats:</b> ";
		for (my $i=0; $i < $doc->{'frm_count'}; $i++) {
			my $f="frm_name_$i";
			my $g="frm_file_$i";
			$res .= "[<a href=" . &GetURL($doc->{$f}, $doc->{$g});
			$res .= ">" . $doc->{$f} . "</a>] ";
		}
	
	}
	$res .= "</dd>\n";
	$res .= "<dt><br></dt>\n";
	return $res;
}	

		
sub BuildMenus {
        my $res      = '';
        my $last_sec = '';
	my $need_close = undef;

	open ALL_INDEX, ">$out_dir_tmp/all.html" or &CleanAndDie("Can't create all.html file: $!");
	print ALL_INDEX &TemplateFile($template_all_start, 
					{ 'TITLE'       => &HTMLEncode($title),
					  'SECTIONLIST' => &SectionsToHTML('', 'all') });

        foreach my $sec (sort keys %doc_list) {
		my $fname = &SectionToFileName($sec, $single_index);
		open SINGLE_INDEX, ">$out_dir_tmp/" . $fname
				or &CleanAndDie("Can't create $fname: $!");
		
		print SINGLE_INDEX  &TemplateFile($template_single_start,
						  { 'TITLE'       => &HTMLEncode($title),
						    'SECTION' 	  => &HTMLEncode($section_names{$sec}),
						    'SECTIONLIST' => &SectionsToHTML($sec, $single_index)});
		print SINGLE_INDEX "<dl>\n";

		print ALL_INDEX "<h2 align=\"center\"><a name=\"" . 
				&SectionToFileName($sec, $all_index) . 
				"\"><strong> Section: ". &HTMLEncode($section_names{$sec}) . "</strong></a></h2>\n<dl>";
		
		print SINGLE_INDEX "\n<!-- Section: " . &HTMLEncode($section_names{$sec}) . " -->\n";
		foreach my $d (sort sort_documents @{$doc_list{$sec}}) {
			$res = &DocumentToHTML($d);
			print SINGLE_INDEX "<!-- begin entry -->\n";
			print SINGLE_INDEX $res;
			print SINGLE_INDEX "<!-- end entry -->\n";
			print ALL_INDEX $res;
		}
		
		print SINGLE_INDEX "</dl>\n";
		print ALL_INDEX "</dl>\n";
		print SINGLE_INDEX  &TemplateFile($template_single_end, {});
		close SINGLE_INDEX;
	}
	
	print ALL_INDEX &TemplateFile($template_all_end, {});
	close ALL_INDEX;
	
}
