#!/usr/bin/env perl
# SPDX-License-Identifier: GPL-3.0-or-later

# Modules and pragmas {{{
use strict;
use warnings;
use autodie;
use feature qw(say);
use utf8;
use open qw(:std :utf8);
binmode STDOUT, ':encoding(UTF-8)';
use IPC::System::Simple qw(capturex);
use Getopt::Long;
use Pod::Usage;

# Get the path where this script is located {{{
my $dirpath;

BEGIN {
    use File::Basename qw(fileparse);
    use Cwd qw(abs_path);
    $dirpath = ( fileparse( abs_path($0) ) )[1];
}
# }}}

use Locale::Maketext::Simple (
    Path   => $dirpath . 'po/',
    Decode => 1,
);
# }}}

# Optional parameters {{{
my $print_author   = 0;
my $commit_count   = 1;
my $width          = 0;
my $lang           = undef;
my $version        = 0;
my $help           = 0;
my $man            = 0;
my $revision_range = 0;
my ( $git_user, $git_repo, $git_commit_address );
GetOptions(
    'author'        => \$print_author,
    'startcommit=s' => \$commit_count,
    'width=s'       => \$width,
    'git-c-add=s'   => \$git_commit_address,
    'user=s'        => \$git_user,
    'repo=s'        => \$git_repo,
    'lang=s'        => \$lang,
    'version'       => \$version,
    'help|?'        => \$help,
    'man'           => \$man,
    'range=s'       => \$revision_range,
);
pod2usage( -verbose  => 99, -sections => [ qw(Synopsis Options) ] ) if $help;
pod2usage( -verbose  => 2,  -exitstatus => 0, )        if $man;
pod2usage( -verbose  => 99, -sections   => 'Version' ) if $version;
my $git_remote = ( split /\n/xms, capturex(qw(git remote -v)) )[0];

loc_lang($lang) if ( defined $lang );
say '%% This file was generated by the script latex-git-log';
my $git_command_commit_msg = '%s';
if ( (defined $git_user and defined $git_repo) or $git_commit_address ) {
    unless ( defined $git_commit_address ) {
        $git_commit_address = "https://github.com/$+{user}/$+{repo}/commit";
    }
    $git_command_commit_msg = '%H & %s';
    say '%% Base git commit URL: ' . $git_commit_address;
}
if ($width) {
    $width = "p{${width}cm}";
}
else {
    $width = 'l';
}
# }}}

# LaTeX template {{{
say '\begin{tabular}{lp{12cm}}
  \label{tabular:legend:git-log}
  \textbf{' . loc('acronym') . '} & \textbf{' . loc('meaning') . '} \\\\
  V & \texttt{' . loc('version') . '} \\\\
  ' . loc('tag') . ' & ' . loc('\texttt{git tag}') . ' \\\\
  ' . loc('MF') . ' & ' . loc('Number of \texttt{modified files}.') . ' \\\\
  ' . loc('AL') . ' & ' . loc('Number of \texttt{added lines}.') . ' \\\\
  ' . loc('DL') . ' & ' . loc('Number of \texttt{deleted lines}.') . ' \\\\
\end{tabular}

\bigskip

\iflanguage{ngerman}{\shorthandoff{"}}{}';

if ($print_author) {
    say "\\begin{longtable}{|rllllrrr|}";
}
else {
    say "\\begin{longtable}{|rll${width}rrr|}";
}
say '\hline \multicolumn{1}{|c}{\textbf{' . loc('V') . '}} & \multicolumn{1}{c}{\textbf{' . loc('tag') . '}}';
say '& \multicolumn{1}{c}{\textbf{' . loc('author') . '}}' if $print_author;
say '& \multicolumn{1}{c}{\textbf{' . loc('date') . '}}
& \multicolumn{1}{c}{\textbf{' . loc('commit message') . '}} & \multicolumn{1}{c}{\textbf{' . loc('MF') . '}}
& \multicolumn{1}{c}{\textbf{' . loc('AL') . '}} & \multicolumn{1}{c|}{\textbf{' . loc('DL') . '}} \\\\ \hline
\endhead
';
if ($print_author) {
    say '\hline \multicolumn{8}{|r|}{\longtableendfoot} \\\\ \hline';
}
else {
    say '\hline \multicolumn{7}{|r|}{\longtableendfoot} \\\\ \hline';
}
say '\endfoot

\hline% \hline
\endlastfoot
';
# }}}

# Get version history from git log {{{
# git log --pretty=format:'%ai'
# git log --date=short --pretty=format:'%ad'
my @lines;
my @git_command = qw(git log --date=short --shortstat);
if ($print_author) {
    push( @git_command, qq(--pretty=format:%H & %an NoTinAuthorFiled& %ad & $git_command_commit_msg) );
}
else {
    push( @git_command, qq(--pretty=format:%H %ad & $git_command_commit_msg) );
}
if ($revision_range) {
    push( @git_command, qq($revision_range) );
}
@lines = reverse capturex(@git_command);
# }}}

# Get tags {{{
my @tags_commits = capturex( 'git', 'log', '--tags', '--no-walk', '--pretty=\'%H %d\'' );
my %commit_tags; # The key will be a SHA1 commit hash and the value a comma separated list of all tags.
for (@tags_commits) {
    chomp;
    m/\A'([^ ]+)\s*\(([^)]+)\)'\z/xms;
    my $c_hash = $1;
    my $tag = $2;
    $tag =~ s/tag: //g;
    if ( defined $commit_tags{$c_hash} ) {
        $commit_tags{$c_hash} .= ", $tag";
    }
    else {
        $commit_tags{$c_hash} = $tag;
    }
}
# }}}

# Helper function {{{
sub latex_escape {
    # Source: http://ommammatips.blogspot.de/2011/01/perl-function-for-latex-escape.html
    my $paragraph = shift;

    # Replace a \ with $\backslash$
    # This is made more complicated because the dollars will be escaped
    # by the subsequent replacement. Easiest to add \backslash
    # now and then add the dollars
    $paragraph =~ s/\\/\\backslash/g;

    # Must be done after escape of \ since this command adds latex escapes
    # Replace characters that can be escaped
    $paragraph =~ s/([\$\#&%_{}])/\\$1/g;

    # Replace ^ characters with \^{} so that $^F works okay
    $paragraph =~ s/(\^)/\\$1\{\}/g;

    # Replace tilde (~) with \texttt{\~{}}
    # $paragraph =~ s/~/\\texttt\{\\~\{\}\}/g;
    $paragraph =~ s/~/\\~\{\}/g;

    # Now add the dollars around each \backslash
    $paragraph =~ s/(\\backslash)/\$$1\$/g;
    return $paragraph;
} ## end sub latex_escape
# }}}

# Loop over all commits {{{
my $which_line = 0;
my @changes;
for (@lines) {
    next if /\A\Z/xms;
    chomp;
    if ( m/^([0-9a-f]{40})\s/g and !$which_line) {
        $which_line ^= 1; ## additional toggle for merge commits
        @changes = ( "-", "-", "-" );
    }
    if ($which_line) {
        s/\A([0-9a-f]{40})\s//xms or die "Did not match the commit hash\n";
        my $tags = exists $commit_tags{$1} ? $commit_tags{$1} : q();
        my $date_author = '';
        my $c_msg = '';
        if ($print_author) {
            /(?:& )(.*?)NoTinAuthorFiled(& .*? &) (.*)/;
            $date_author = latex_escape($1) . $2;
            $c_msg       = $3;
        }
        else {
            /(.*? &) (.*)/;
            $date_author = $1;
            $c_msg       = $2;
        }
        if ( defined $git_commit_address ) {
            $c_msg =~ /(.*?) & (.*)/;
            $c_msg = sprintf '\\href{%s/%s}{%s}', $git_commit_address, $1, latex_escape($2);
        }
        else {
            $c_msg = latex_escape($c_msg);
        }
        say "\\hline $commit_count & $tags & $date_author $c_msg & " . join( ' & ', @changes ) . ' \\\\';
        $commit_count++;
    } ## end if ($which_line)
    else {
        @changes = ( 0, 0, 0 );
        /(\d+) files? changed/ and $changes[0] = $1;
        /(\d+) insertions?/    and $changes[1] = $1;
        /(\d+) deletions?/     and $changes[2] = $1;
    }
    $which_line ^= 1; ## toggle bit
} ## end for (@lines)
say '\end{longtable}';
# }}}

__END__
# Documentation {{{

=encoding UTF-8
=head1 NAME

latex-git-log - Generates the version history of a git project as LaTeX source code.

=head1 Synopsis

latex-git-log [options]

Options:

  --author          set this if you want the author included
  --startcommit     set the start value of count commit
  --width           set the width in cm of the commit message field in the LaTeX table
  --git-c-add       set an base URL to link to a commit
  --user            set a github user to derive the base URL
  --repo            set a github repository to derive the base URL
  --lang            language of the legend and all strings in the output
  --version, -v     print version of this script
  --help            brief help message
  --man             full documentation
  --range           specify a git revision range

=head1 Options

=over 8

=item B<--width>

Set the width in cm of the commit message field in the LaTeX table. If this
parameter is not set then the table is not vertically limited. That means that
if you have a very long commit message then the table will probably not fit on
the page and you will get a "Overfull" error message from TeX.

In this case you should specify the width of the column containing the commit messages.
I normally use something like --width=14 for DIN A4 in landscape.

=item B<--git-c-add>

Set an base URL to link to a commit.
This script will automatically try to use the base URL for github.

=item B<--lang>

Set the language of the legend and all strings in the output. By default the
language of your system is used but you can overwrite this with this parameter.

Currently this script only supports English and German. If you need a translation
to another language then you can either create a .po file or I can register
this project on one of those websites for online translation.

=back

=head1 Example

You can use it like this:

    latex-git-log --width=6 --lang=en > example-output.tex

=head1 Dependencies

=head2 Of this script

This module requires these other modules and libraries:

    IPC::System::Simple
    Locale::Maketext::Simple

Everything else should already be installed.

=head2 To compile the output

The table is using the B<longtable> package and the links to a web resource for
each commit use the \href macro from B<hyperref>. So these two packages have to
be loaded.

Furthermore you need to defined the macro B<\longtableendfoot> which will be
expanded on the bottom of every page if the table will be continued on the next
page. You can defined it to a localized message to inform the reader that this
table is not complete and will be continued.

=head1 Description

B<This program> will output the entire version history as table written in
LaTeX if it is executed within a git repository.

It is intended that you redirect the standard output of this script to a file
which can then be included from your main LaTeX document.

Because the table can be very large you might want to put the thing on a
landscape page.

=head1 Version

1.0.0

=head1 Author

Robin Schneider <ypid@riseup.net>

=head1 Development

CTAN: https://ctan.org/pkg/latex-git-log

Source code repository: https://github.com/ypid/typesetting/tree/master/scripts/latex-git-log

Please report bugs and feature requests at https://github.com/ypid/typesetting/issues

=head1 License and Copyright

Copyright (C) 2012-2013,2017,2019 Robin Schneider <ypid@riseup.net>

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 3 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, see <http://www.gnu.org/licenses/>.

Dieses Programm ist Freie Software: Sie können es unter den Bedingungen
der GNU General Public License, wie von der Free Software Foundation,
Version 3 der Lizenz oder (nach Ihrer Option) jeder späteren
veröffentlichten Version, weiterverbreiten und/oder modifizieren.

Dieses Programm wird in der Hoffnung, dass es nützlich sein wird, aber
OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
Siehe die GNU General Public License für weitere Details.

Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>.

=cut
# }}}
