#!/usr/bin/perl -w
#
# Bacula Systems - Philippe Chauvat
# 27 jul 2012
# 24-apr-2019: Adding font awesome css file
#
# This script is designed to translate Bacula enterprise LaTeX2HTML
# documentation files to something more "tunable" / "adaptable" from a CSS
# point of view.
#
# $1 is an HTML file to analyze and translate
# The output is automatically send to $1.out
#
# - add some ids, class
# - re-order some piece of HTML code
#
# This script is based on HTML::Parser module
#
# args:
# -i: HTML input file
# -o: HTML output file
# -j: javascript directory
# -c: css directory
# -p: images (pictures) directory
# -n: Manual name
# -e: Request a menu extraction
# -r: Source directory (ako part of -i arg)
# -?: help / usage
# -d: debug requested
use HTML::Parser ;
use HTML::TreeBuilder ;
use HTML::PullParser ;
use Getopt::Long ;
use File::Basename ;
use Data::Dumper ;
use open qw/:std :utf8/;
$edition = 'Bacula Enterprise Edition' ;
sub usage {
    print "translatedoc.pl -i | --input html-source-file
 [ -o | --output html-destination-file ]
 [ -j | --javascript javascript-diretory ]
 [ -c | --css css-directory ]
 [ -p | --pictures pictures-directory ]
 [ -n | --name manual_name ]
 [ -e | --extract ]
 [ -d | --debug ]
 [ -r | --source-directory the_original_root_directory ]
 [ --help | -? ]\n" ;
    exit 1 ;
}
#
# Send message to output in case of debug only
# ======================
sub debugdump {
    my ($what,$msg) = @_ ;
    if ($debug) {
	print "\n===============================\nBegin of $msg\n" ;
	$what->dump ;
	print "\n===============================\nEnd of $msg\n\n" ;
    }
}
#
# Args to Vars
our($inputfile,$outputfile,$help,$debug,$mytree,$extractmenu,$picturesdir,
    $cssdir,$javascriptdir,$manualname,$sourcedir) ;
#
# Usage in case of missing arguments
usage() unless($#ARGV > -1) ;
#
# Input file / Output file
GetOptions("input|i=s" => \$inputfile,
	   "output|o=s" => \$outputfile,
	   "extract|e" => \$extractmenu,
	   "pictures|p=s" => \$picturesdir,
	   "css|c=s" => \$cssdir,
	   "source-directory|r=s" => \$sourcedir,
	   "javascript|j=s" => \$javascriptdir,
	   "name|n=s" => \$manualname,
	   "debug|d" => \$debug,
	   "help|?" => \$help) or usage() ;
usage() if ($help) ;
usage() unless (defined $inputfile) ;

die "$inputfile does not exists.\n" unless -e $inputfile ;

print "Converting file $inputfile\n" ;
$outputfile = "./" . basename($inputfile) . ".out" unless defined($outputfile) ;
$picturesdir = "../images" unless defined $picturesdir ;
$cssdir = "../css" unless defined($cssdir) ;
$javascriptdir = "../js" unless defined($javascriptdir) ;
$manualname = "main" unless defined($manualname) ;

my $MENUFILE="./wholemenu_" . $manualname . ".htm" ;
#
# Build HTML Tree of existing page
$mytree = HTML::TreeBuilder->new ;
$mytree->parse_file($inputfile) ;
debugdump($mytree,"Read tree") ;
#
# Find the beginning of the content
# Which is also a point where to put
# the menu
$beginning_of_content = $mytree->look_down('_tag','h1') ;
debugdump($beginning_of_content,"Beginning of content H1") ;
if (! $beginning_of_content) {
    $beginning_of_content = $mytree->look_down('_tag','h2') ;
    debugdump($beginning_of_content,"Beginning of content H2") ;
}
die "The only thing we could test is a <H1> / <H2> tags, which does not exist there...:$!\n"  unless($beginning_of_content) ;

#
# Look for the table of contents
# ==============================
my $thetableofcontents ;
my $dummy ;
if ($thetableofcontents = $mytree->look_down('_tag', 'div', 'id','table-of-contents')) {
    $dummy = $thetableofcontents->detach() ;
    debugdump($thetableofcontents,"Table of Contents") ;
    debugdump($dummy,"Dummy when ToC") ;
    debugdump($mytree,"MyTree when ToC") ;
    #
    # Clean up the content of table of contents
    while ($d = $thetableofcontents->look_down('_tag','br')) {
	$d->detach() ;
	$d->delete() ;
    }
    debugdump($thetableofcontents,"ToC after cleaning <br>:") ;
}
#
# The following will appear in case of "subsections"
if ($dummy = $mytree->look_down('_tag','a','name','CHILD_LINKS')) {
    debugdump($dummy,"cleaning subsections") ;
    $dummy->detach() ;
    $dummy->delete() ;
    debugdump($mytree,"MyTree after cleaning subsections") ;
}
#
# Remove the navigation part.... (next, up, previous, and so on)
if ($dummy = $mytree->look_down('_tag', 'div', 'class', 'navigation')) {
    debugdump(0,"cleaning navigation") ;
    $dummy->detach_content() ;
    $dummy->detach() ;
    $dummy->delete_content() ;
    $dummy->delete() ;
    debugdump($mytree,"MyTree after cleaning navigation") ;
}
# End removing navigation
#
#
# Remove every 'dirty' lines
# between <body> and <h1> tag
# What is "before" the <h1> tag (between <body> and <h1>) is just dropped
my @lefts = $beginning_of_content->left() ;
foreach my $l (@lefts) {
#    print $l . "\n" ;
    debugdump($l,"lefts") ;
    $l->detach_content() ;
    $l->delete_content() ;
    $l->detach() ;
    $l->delete() ;
}
debugdump($mytree,"MyTree after removing 'lefts'") ;
#
# Remove Bacula logo
if ($dummy = $beginning_of_content->look_down('_tag','img','alt','\\includegraphics{bacula-logo.eps}')) {
    $dummy->detach() ;
    $dummy->delete() ;
    debugdump($beginning_of_content,"Content after removing logo") ;
    debugdump($mytree,"MyTree after removing logo") ;
}
# End remove Bacula logo
#
# Remove 'address' tag
if ($dummy = $mytree->look_down('_tag','address')) {
    $dummy->detach() ;
    $dummy->delete() ;
    debugdump($mytree,"MyTree after removing address") ;
}
# End remove address
#
# Identifying the body part
my $thebody = $mytree->look_down('_tag','body') ;
# ... to add a javascript "onload" action
$thebody->attr('onload','menuonload(this);') ;
debugdump($thebody,"TheBody after adding the onload js action") ;
my $d ;
if ($d = $thebody->look_down('_tag','div','class','bsys_topclass')) {
    $d->detach() ;
    $d->delete() ;
}
if ($d = $thebody->look_down('_tag','div','class','bsys_breadnsearchclass')) {
    $d->detach() ;
    $d->delete() ;
}
#
# Getting the content of the body
my @content = $thebody->detach_content() ;
# End remove dirty lines
#
# What do we do with the menu?
# If the menu file exists then just use it
if (-e $MENUFILE) {
    #
    # Build the menu file
    $thetableofcontents = HTML::TreeBuilder->new ;
    $thetableofcontents->parse_file($MENUFILE) ;
    debugdump($thetableofcontents,"ToC read from $MENUFILE") ;
    $dummy = $thetableofcontents->look_down('_tag','body') ;
    $thetableofcontents = $dummy->detach_content() ;
    debugdump($thetableofcontents,"ToC content read from $MENUFILE") ;
}
else {
    debugdump($thetableofcontents,"ToC when building the menu") ;
}

#
#
# Create a div to manage the whole page
my $mainpage = HTML::Element->new_from_lol(
    ['div', { 'class' => "b_mainpageclass", 'id' => "b_mainpageid" },
     [ 'div', {'class' => 'b_topclass', 'id' => 'b_topid'},
       [ 'img', { 'src' => $picturesdir . '/blogo-header.png', 'id' => 'b_logo','alt' => 'BaculaSystems Logo' }],
       [ 'img', { 'src' => $picturesdir . '/btitle-header.png', 'id' => 'b_doctitle', 'alt' => join(' ',$edition,'Documentation text image transdoc')}]
     ],
     [ 'div', {'id' => 'b_breadnsearchid', 'class' => 'b_breadnsearchclass'},
       ['div', { 'class' => 'b_searchclass', 'id' => 'b_searchid'},
	['span','Search' , {'class' => 'b_searchtitleclass','id' => 'b_searchtitleid'}],
	[ 'input', { 'class' => 'b_searchfieldclass', 'id' => 'b_searchfieldid', 'type' => 'text', 'value' => 'Type your text here' }]
       ],
       [ 'div', { 'class' => 'b_breadcrumbsclass', 'id' => 'b_breadcrumbsid'},
	 [ 'p', { 'class' => 'b_breadcrumbscontentclass', 'id' => 'b_breadcrumbscontentid' }, 'Main' ],
       ]
     ],
     [ 'div', { 'class' => "b_pageclass", 'id' => "b_pageid"},
       [ 'div', { 'class' => "b_leftnavigationclass", 'id' => "b_leftnavigationid" },
	 $thetableofcontents,
       ],
       [ 'div', { 'class ' => 'b_contentclass', 'id' => 'b_contentid' },
	 [ map (('div', {'class' => 'b_truecontent' }), $_ ), @content ]
       ]
     ]
    ]
    ) ;
debugdump($mainpage,"Main page build") ;
$beginning_of_content = $thebody->push_content($mainpage) ;
# Remove "Contents" links
if ($dummy = $mytree->look_down('_tag','a','href','Contents.html')) {
    $dummy = $dummy->parent() ;
    $dummy->delete() ;
}
debugdump($beginning_of_content,"Content after pushing mainpage") ;
#
# Now begins the modification for navigation
# ==========================================
# We must analyze what is below <ul class="Child_Links">
# At first level, we consider each <li> as part of the main menu
# At other levels, we consider each <li> as sub(sub | ...)menus
if (my $firstul = $thetableofcontents->look_down('_tag','ul')) {
    my $firsta = $firstul->look_down('_tag','a') ;
    debugdump($firsta,'first <a> tag');
    $dummy = $firsta->parent();
    debugdump($dummy,'parent') ;
    my $topul = $dummy->parent();
    debugdump($topul,"grand parent") ;
    $topul->attr('id','childlinksid') ;
    #
    # This counter is for generating unique identifiers
    my $ulcounter = 1 ;
    my $ullevelcounter = 0 ;
    my $ulpreviousdepth = 0 ;
    #
    # Browse all the <ul name="ChildLinks"> node
    # ------------------------------------------
    foreach my $d ($dummy->descendants()) {
	#
	# Which tag are we checking ?
	my $tag = $d->tag() ;
	#
	# Nothing to do with <a> tags
	if ($tag =~ /a/) {
	    debugdump($d,'a tag') ;
	    $d->attr('onclick',"menuonclick(this);") ;
	}
	#
	# <ul>s represent "openable" menus
	elsif ($tag =~ /ul/) {
	    debugdump($d,'ul tag') ;
	    if ($d->depth() > $ulpreviousdepth) {
		$ullevelcounter++ ;
	    }
	    else {
		$ullevelcounter-- ;
	    }
	    $ulpreviousdepth=$d->depth() ;
	    #
	    # We need to identify uniquely this <ul> start tag to be able to "open" or "close" it
	    my $ullevel= 'level' . $ullevelcounter ;
	    my $idf = 'b_ul_' . $ulcounter++ ;
	    $d->attr('class',$ullevel . ' expandingMenu expandingMenuNotSelected') ;
	    $d->attr('id', $idf) ;
	    #	$d->attr('style','display: none;') ;
	    #
	    # We now are knowing the previous <li> tag is a (sub)menu header too
	    # Adding the "onclick" behavior
	    my $previoustagli = $d->look_up('_tag','li') ; # <li> just above
	    $previoustagli->attr('pct_onmouseover',"over_expandingMenuHeader(this,\'" . $idf . "\')") ;
	    $previoustagli->attr('pct_onmouseout',"out_expandingMenuHeader(this,\'" . $idf . "\')") ;
	    my $previoustaga = $d->left('_tag','a') ; # <a> just above
	    $previoustaga->attr('onclick',"menuonclick(this);") ;
	    #
	    # Do not forgot what we defined earlier...
	    my $class = $previoustagli->attr('class') ;
	    $class = $class . ' expandingMenuHeader' ;
	    $previoustagli->attr('class', $class) ;
	}
	#
	# <li>s represent at least menu items
	# and sometimes menu headers (see <ul> treatment)
	elsif ($tag =~ /li/) {
	    debugdump($d,'li tag') ;
	    #
	    # At this stage we only know <li> is a menu item.
	    # nothing more...
	    $d->attr('class', 'expandingMenuItem') ;
	}
    }
    $firstul->replace_with($topul) ;
    debugdump($thetableofcontents,"Table of contents after ul/li management") ;
}
#
# <head> treatment
# Add some stuff
my $themanual = "nothing" ;
if ($manualname eq "main") {
    $themanual = "Main Reference Manual" ;
}
elsif ($manualname eq "developers") {
    $themanual = "Developer's Guide" ;
}
elsif ($manualname eq "console") {
    $themanual = 'Command Console and Operators Guide' ;
}
elsif ($manualname eq "utility") {
    $themanual = 'Utility Programs' ;
}
elsif ($manualname eq "problems") {
    $themanual = 'Problem Resolution Guide' ;
}
elsif ($manualname eq "misc") {
    $themanual = 'Miscellaneous Guide' ;
}

if ($dummy = $mytree->look_down('_tag','meta','name','description')) {
    $dummy->attr('content',join(' ',$edition,$themanual)) ;
}
if ($dummy = $mytree->look_down('_tag','meta','name','keywords')) {
    $dummy->attr('content',join(',','BaculaSystems',$edition,$themanual)) ;
}
if ($dummy = $mytree->look_down('_tag','link','href',join('.',$manualname,'css'))) {
    $dummy->attr('href',join('',$cssdir,'/',$manualname,'.','css')) ;
    $dummy->postinsert(
	HTML::Element->new_from_lol(
	    ['link',{ 'href' => join('',$cssdir,'/','bmanual.css'), 'rel' => 'stylesheet' } ],
	    ['link',{ 'href' => join('',$cssdir,'/','font-awesome.css'), 'rel' => 'stylesheet' } ],
	    ['script',{ 'type' => 'text/javascript', 'src' => join('',$javascriptdir,'/','bmanual.js') } ],
	    ['meta',{ 'charset' => 'utf-8' } ]
	)
	) ;
}
debugdump($mytree,"MyTree after adding header information") ;
#
# Replace textregistered images with the HTML special char
my @images = $mytree->look_down('_tag','img') ;
foreach $dummy (@images) {
    my $alttext = $dummy->attr('alt') ;
    next unless $alttext ;
    if ($alttext =~ /.*registe.*/) {
	$dummy->preinsert(HTML::Element->new_from_lol(['span', {'class' => 'expochar' }, '&reg;'])) ;
	$dummy->detach() ;
	$dummy->delete() ;
    }
    if ($alttext =~ /.*bacula.*-logo.*/) {
	$dummy->detach() ;
	$dummy->delete() ;
    }
}
debugdump($mytree,"MyTree after changing registered characters") ;

@images = $mytree->look_down('_tag','img') ;
foreach $dummy (@images) {
    my $img = $dummy->attr('src') ;
    $img =~ s:^\./:$picturesdir/: ;
    $dummy->attr('src',$img) ;
}
debugdump($mytree,"MyTree after defining images path") ;
#
# Handling double quotes
my @quotes = $mytree->look_down('class','bleftquote') ;
foreach $dummy (@quotes) {
    $dummy->preinsert(HTML::Element->new_from_lol(['span', '&ldquo;'])) ;
    $dummy->detach() ;
    $dummy->delete() ;
}
@quotes = $mytree->look_down('class','brightquote') ;
foreach $dummy (@quotes) {
    $dummy->preinsert(HTML::Element->new_from_lol(['span', '&rdquo;'])) ;
    $dummy->detach() ;
    $dummy->delete() ;
}

debugdump($mytree,"MyTree after handling quotes") ;
#
# Removing <hr />
while ($dummy = $mytree->look_down('_tag','hr')) {
    debugdump($dummy,"HR block to be deleted") ;
    $dummy->detach() ;
    $dummy->delete() ;
}
debugdump($mytree,'MyTree after removing <hr />') ;

#
# I am not sure the following is still requested
# ==============================================
#
# Locate all <a name="whatever_but_SECTION...">
my @atags = $mytree->look_down('_tag','a') ;
local *AFH ;
open AFH, ">> list-of-anchors" or die "Unable to append to list-of-anchors file\n"; 
foreach $dummy (@atags) {
    my $atagname ;
    if ($atagname = $dummy->attr('name')) {
	print AFH $manualname . "\t" . basename($inputfile) . "\t" . $atagname . "\n" ;
    }
}
close AFH ;
# This li is at first level
if ($outputfile) {
    local *FH ;
    open FH, ">" . $outputfile or die "Unable to create $outputfile: $!\n" ;
    print FH $mytree->as_HTML("<>","\t",{}) ;
    close FH ;
}
else {
    print $mytree->as_HTML("","\t",{}) ;
}
if ($extractmenu) {
    local *FH ;
    open FH, ">" . $MENUFILE or die "Unable to create the menu file: $!\n" ;
    debugdump($thetableofcontents,"Before pushing it to the menu file.") ;
    print FH $thetableofcontents->as_HTML("","\t",{}) ;
    close FH ;
}

1;
