#!/usr/bin/perl
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

# This script compiles all the documentation.
#
# Required software:
#
# 1) Sphinx documentation builder (python-sphinx package on Debian/Ubuntu)
#
# 2a) rst2pdf
# or
# 2b) pdflatex, which means the following Debian/Ubuntu packages:
#     * texlive-latex-base
#     * texlive-latex-recommended
#     * texlive-latex-extra
#     * texlive-fonts-recommended
#
# All these TeX packages together are close to a gig :-| But after you've
# installed them, you can remove texlive-latex-extra-doc to save 400MB.

use 5.14.0;
use strict;
use warnings;

use File::Basename;
BEGIN { chdir dirname($0); }

use lib qw(.. ../lib lib ../local/lib/perl5);
use open ':encoding(utf8)';

use Cwd;
use File::Copy::Recursive qw(rcopy);
use File::Path qw(rmtree make_path);
use File::Which qw(which);
use Pod::Simple;
use Pod::ParseLink;

use Bugzilla::Constants qw(BUGZILLA_VERSION bz_locations);
use Pod::Simple::Search;
use Pod::POM::View::Restructured;
use Tie::File;
use File::Spec::Functions qw(:ALL);

###############################################################################
# Subs
###############################################################################

my $error_found = 0;

sub MakeDocs {
    my ($name, $cmdline) = @_;

    say "Creating $name documentation ..." if defined $name;
    system('make', $cmdline) == 0
        or $error_found = 1;
    print "\n";
}

# Compile all the POD and make it part of the API docs
# Also duplicate some Extension docs into the User section, Admin section, and
# WebService docs
sub pod2rst {
    my $path = shift;

    say "Converting POD to RST...";
    my $name2path = Pod::Simple::Search->new->inc(0)->verbose(0)
        ->survey(@{ ['../'] });

    my $ind_path = catdir($path, 'rst', 'integrating', 'internals');
    rmtree($ind_path);
    make_path($ind_path);

    my $FILE;
    open($FILE, '>', catfile($ind_path, 'index.rst'))
        || die("Can't open rst file");
    print($FILE <<'EOI');
.. highlight:: perl

.. _developer

===================================
Developer Documentation
===================================

This section exposes the POD for all modules and scripts, including extensions,
in Bugzilla.

.. toctree::
   :maxdepth: 2

EOI

    foreach my $mod (sort keys %$name2path) {
        # Ignoring library files;
        if ($mod =~ /^b?(lib::|local)/) {
            delete $name2path->{$mod};
            next;
        }

        my $title = $mod;

        my $ext = $title =~ s/^extensions:://;
        $title =~ s/lib:://;
        $title =~ s/::Extension$// if ($ext);
        my $header = "=" x length($title);

        my $abs_path = $name2path->{$mod};
        my $fpath = abs2rel($abs_path, '..');
        my ($volume, $directories, $file) = splitpath($fpath);
        $directories =~ s/lib\///;
        $directories = '.' if ($directories eq '');

        my $dir_path = catdir($ind_path, $directories);
        make_path($dir_path);
        $file =~ s/\.[^.]*$//;
        my $out_file = $file . '.rst';
        my $full_out_file = catfile($dir_path, $out_file);

        my $callbacks = {
            link => sub {
                my $text = shift;

                my @results = parselink($text);

                # A link to another page.
                if (defined($results[1]) && defined($results[2])) {
                    if ($results[2] =~ /^http/) {
                        return ($results[2], $results[1]);
                    }
                    elsif ($results[2] =~ m/Bugzilla/) {
                        my $depth = scalar(split('/', $directories));
                        my @split = split('::', $results[2]);
                        my $base  = '../' x $depth;
                        my $url   = $base . join('/', @split);
                        $url .= '.html';
                        $url .= '#' . $results[3] if defined $results[3];
                        return ($url, $results[1]);
                    }
                    else {
                        # Not a Bugzilla package, link to CPAN.
                        my $url
                            = 'http://search.cpan.org/search?query='
                            . $results[2]
                            . '&mode=all';
                        return ($url, $results[1]);
                    }
                }

                # A Link within a page
                elsif (defined($results[1]) && defined($results[4])) {
                    my $anchor = $results[1];
                    $anchor =~ s/"//g;
                    return ("#$anchor", $anchor);
                }
                else {
                    die "Don't know how to parse $text";
                }
            }
        };
        my $conv = Pod::POM::View::Restructured->new(
                             { namespace => $title, callbacks => $callbacks });
        my $rv = $conv->convert_file($abs_path, $title, $full_out_file,
                                                                   $callbacks);
        print($FILE "   ", catfile($directories, $out_file), "\n");

        if ($ext) {
            my $api_path = catdir($path, 'rst', $directories);
            my $adminfile = catfile($api_path, "index-admin.rst");
            my $userfile  = catfile($api_path, "index-user.rst");
            my $apifile   = catfile($api_path, 'api', 'v1', 'index.rst');
            make_path($api_path) unless -d $api_path;

            # Add Core doc to User & Admin guides
            if ($file eq 'Extension') {
                my $FH;
                open($FH, '<', $full_out_file) || die "$full_out_file: $!";
                my @lines = <$FH>;
                close $FH;

                my @array;

                # ensure out of order docs are at the top of the page without
                # playing with file handles
                tie @array, 'Tie::File', $adminfile or die "$adminfile: $!";
                unshift @array, @lines, "", "", ".. toctree::", "", "";
                untie @array;

                tie @array, 'Tie::File', $userfile or die "$userfile: $!";
                unshift @array, @lines, "", "", ".. toctree::", "", "";
                untie @array;

            }

            # Add Config doc to Admin guide
            elsif ($file eq 'Config') {
                rcopy($full_out_file, $api_path);
                `perl -E 'say "   $file.rst"' >> "$adminfile"`;
            }

            # Add WebServices doc to API docs
            elsif ($file eq 'WebService') {
                my $apidir = catdir($api_path, 'api', 'v1');
                make_path($apidir)
                    unless -d $apidir;
                `perl -E 'say ".. toctree::\n\n"' >> $apifile`
                    unless -f $apifile;

                my $FH;
                open($FH, '<', $full_out_file) || die "$full_out_file: $!";
                my @lines = <$FH>;
                close $FH;

                my @array;

                tie @array, 'Tie::File', $apifile or die "$apifile: $!";
                unshift @array, @lines;
                untie @array;
            }
            elsif ($api_path =~ /WebService\/$/) {
                my $apidir = catdir($api_path, '..', 'api', 'v1');
                $apifile = catfile($apidir, 'index.rst');
                make_path($apidir) unless -d $apidir;
                rcopy($full_out_file, $apidir);
                `perl -E 'say ".. toctree::\n\n"' >> $apifile`
                    unless -f $apifile;
                `perl -E 'say "   $file.rst"' >> $apifile`;
            }
        }
    }

    close($FILE);
}

###############################################################################
# Make the docs ...
###############################################################################

my @langs;

# search for sub directories which have a 'rst' sub-directory
opendir(LANGS, './');
foreach my $dir (readdir(LANGS)) {
    next if (($dir eq '.') || ($dir eq '..') || (!-d $dir));
    if (-d "$dir/rst") {
        push(@langs, $dir);
    }
}
closedir(LANGS);

my $docparent = getcwd();
foreach my $lang (@langs) {

    rmtree("$lang/html", 0, 1);
    rmtree("$lang/txt",  0, 1);

    my @sub_dirs = grep {-d} glob("$lang/rst/extensions/*");
    rmtree(@sub_dirs, { verbose => 0, safe => 1 });

    pod2rst("$docparent/$lang");

    next if grep { $_ eq '--pod-only' } @ARGV;

    chdir "$docparent/$lang";

    MakeDocs('HTML', 'html');
    MakeDocs('TXT',  'text');

    if (grep { $_ eq '--with-pdf' } @ARGV) {
        if (which('pdflatex')) {
            MakeDocs('PDF', 'latexpdf');
        }
        elsif (which('rst2pdf')) {
            rmtree('pdf', 0, 1);
            MakeDocs('PDF', 'pdf');
        }
        else {
            say 'pdflatex or rst2pdf not found. Skipping PDF file creation';
        }
    }

    rmtree('doctrees', 0, 1);
}

die "Error occurred building the documentation\n" if $error_found;
