#!/usr/bin/perl
    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
        if $running_under_some_shell;
#!/usr/bin/perl

use strict;

my $main_repo="build";
my @pieces=("artwork", "base", "bootstrap", "calc", "components",
            "extensions", "extras", "filters", "help", "impress",
            "libs-core", "libs-extern", "libs-extern-sys", "libs-gui",
            "l10n", "postprocess", "sdk", "testing", "ure", "writer");

sub search_bugs($$$$)
{
    my ($pdata, $piece, $commit_id, $line) = @_;

    my $bug = "";
    my $bug_orig;
    while (defined $bug) {

        # match fdo#123, rhz#123, i#123
        if ( $line =~ m/(\w*\#+\d+)/ ) {
            $bug_orig = $1;
            $bug = $1;
        # match #i123#
        } elsif ( $line =~ m/(\#i)(\d+)(\#)/ ) {
            $bug_orig = $1 . $2 . $3;
            $bug = "i#$2";
        } else {
            $bug = undef;
            next;
        }

#        print "   found $bug\n";
        # remove bug number from the comment; it will be added later a standardized way
        $bug_orig =~ s/\#/\\#/;
        $line =~ s/[Rr]esolves:\s*$bug_orig\s*//;
        $line =~ s/\s*-\s*$bug_orig\s*//;
        $line =~ s/\(?$bug_orig\)?[:,]?\s*//;

        # bnc# is prefered over n# for novell bugs
        $bug =~ s/^n\#/bnc#/;
        # save the bug number
        %{$pdata->{$piece}{$commit_id}{'bugs'}} = () if (! defined %{$pdata->{$piece}{$commit_id}{'bugs'}});
        $pdata->{$piece}{$commit_id}{'bugs'}{$bug} = 1;
        $pdata->{$piece}{$commit_id}{'flags'}{'bug'} = 1;
    }

    return $line;
}

sub standardize_summary($)
{
    my $line = shift;

    $line =~ s/^\s*//;
    $line =~ s/\s*$//;

    # lower first letter
    $line =~ m/(^.)/;
    my $first_char = lc($1);
    $line =~ s/^./$first_char/;

    # FIXME: remove do at the end of line
    #        remove bug numbers
    return $line;
}

sub load_git_log($$$$) 
{
    my ($pdata, $repo_dir, $piece, $pgit_args) = @_;

    my $cmd = "cd $repo_dir && git log " . join ' ', @{$pgit_args};
    my $commit_id;
    my $summary;

    print STDERR "Analyzing log from the git repo: $piece...\n";

    open (GIT, "$cmd 2>&1|") || die "Can't run $cmd: $!";
    %{$pdata->{$piece}} = ();

    while (my $line = <GIT>) {
        chomp $line;

        if ( $line =~ m/^commit ([0-9a-z]{20})/ ) {
            $commit_id = "$1";
            $summary=undef;
            %{$pdata->{$piece}{"$commit_id"}} = ();
            %{$pdata->{$piece}{"$commit_id"}{'flags'}} = ();
            next;
        }

        if ( $line =~ /^Author:\s*([^\<]*)\<([^\>]*)>/ ) {
            # get rid of extra empty spaces;
            my $name = "$1";
            $name =~ s/\s+$//;
            die "Error: Author already defined for the commit {$commit_id}\n" if defined ($pdata->{$piece}{$commit_id}{'author'});
            %{$pdata->{$piece}{$commit_id}{'author'}} = ();
            $pdata->{$piece}{$commit_id}{'author'}{'name'} = "$name";
            $pdata->{$piece}{$commit_id}{'author'}{'email'} = "$2";
            next;
        }

        if ( $line =~ /^Date:\s+/ ) {
            # ignore date line
            next;
        }

        if ( $line =~ /^\s*$/ ) {
            # ignore empty line
            next;
        }

        $line = search_bugs($pdata, $piece, $commit_id, $line);
        # FIXME: need to be implemeted
#        search_keywords($pdata, $line);

        unless (defined $pdata->{$piece}{$commit_id}{'summary'}) {
            $summary = standardize_summary($line);
            $pdata->{$piece}{$commit_id}{'summary'} = $summary;
        }
    }

    close GIT;
}

sub get_repo_name($)
{
    my $repo_dir = shift;
    
    open (GIT_CONFIG, "$repo_dir/.git/config") ||
        die "can't open \"$$repo_dir/.git/config\" for reading: $!\n";

    while (my $line = <GIT_CONFIG>) {
        chomp $line;

        if ( $line =~ /^\s*url\s*=\s*(\S+)$/ ) {
            my $repo_name = "$1";
            $repo_name = s/.*\///g;
            return "$repo_name";
        }
    }
    die "Error: can't find repo name in \"$$repo_dir/.git/config\"\n";
}

sub load_data($$$$)
{
    my ($pdata, $top_dir, $piece, $pgit_args) = @_;

    if (defined $piece) {
        my $piece_dir;
        if ("$piece" eq "$main_repo") {
            $piece_dir = "$top_dir";
        } else {
            $piece_dir = "$top_dir/clone/$piece";
        }
        load_git_log($pdata, $piece_dir, $piece, $pgit_args);
    } else {
        load_git_log($pdata, $top_dir, $main_repo, $pgit_args);
        foreach my $piece (@pieces) {
            load_git_log($pdata, "$top_dir/clone/$piece", $piece, $pgit_args);
        }
    }
}

sub print_summary_in_stat($$$$$$$)
{
    my ($summary, $pprint_filters, $ppiece_title, $pflags, $pbugs, $pauthors, $prefix) = @_;

    return if ( $summary eq "" );
    
    # do we want to print this summary at all?
    my $print;
    if (%{$pprint_filters}) {
        foreach my $flag (keys %{$pprint_filters}) {
            $print = 1 if (defined $pflags->{$flag});
        }
    } else {
        $print = 1;
    }
    return unless (defined $print);

    # print piece title if not done yet
    if (defined ${$ppiece_title}) {
        print "${$ppiece_title}\n";
        ${$ppiece_title} = undef;
    }

    # finally print the summary line
    my $bugs = "";
    if ( %{$pbugs} ) {
        $bugs = " (" . join (", ", keys %{$pbugs}) . ")";
    }

    my $authors = "";
    if ( %{$pauthors} ) {
        $authors = " [" . join (", ", keys %{$pauthors}) . "]";
    }

    print $prefix . $summary . $bugs . $authors . "\n";
}

sub print_weekly_stat($$)
{
    my ($pdata, $pprint_filters) = @_;

    foreach my $piece ( sort { $a cmp $b } keys %{$pdata}) {
        # check if this peice has any entries at all
        my $piece_title = "+ $piece";
        if ( %{$pdata->{$piece}} ) {
            my $old_summary="";
            my %authors = ();
            my %bugs = ();
            my %flags = ();
            foreach my $id ( sort { $pdata->{$piece}{$a}{'summary'} cmp $pdata->{$piece}{$b}{'summary'} } keys %{$pdata->{$piece}}) {
                my $summary = $pdata->{$piece}{$id}{'summary'};
                if ($summary ne $old_summary) {
                    print_summary_in_stat($old_summary, $pprint_filters, \$piece_title, \%flags, \%bugs, \%authors, "    + ");
                    $old_summary = $summary;
                    %authors = ();
                    %bugs = ();
                    %flags = ();
                }
                # collect bug numbers
                if (defined $pdata->{$piece}{$id}{'bugs'}) {
                    foreach my $bug (keys %{$pdata->{$piece}{$id}{'bugs'}}) {
                        $bugs{$bug} = 1;
                    }
                }
                # collect author names
                my $author = $pdata->{$piece}{$id}{'author'}{'name'};
                $authors{$author} = 1;
                # collect flags
                foreach my $flag ( keys %{$pdata->{$piece}{$id}{'flags'}} ) {
                    $flags{$flag} = 1;
                }
            }
            print_summary_in_stat($old_summary, $pprint_filters, \$piece_title, \%flags, \%bugs, \%authors, "    + ");
        }
    }
}

########################################################################
# help

sub usage()
{
    print "This script generates LO git commit summary\n\n" .
          
          "Usage: lo-commit-stat [--help] [--no-pieces] [--piece=<piece>] topdir [git_log_param...]\n\n" .

          "Options:\n" .
          "     --help          print this help\n" .
          "     --no-pieces     read changes just from the main repository, ignore other cloned repos\n" .
          "     --piece=<piece> summarize just chnages from the given piece\n" .
          "     --bugs          print just bug fixes\n" .
          "      topdir         directory with the libreoffice/bootstrap clone; the piece repos\n" .
          "                     must be cloned in the main-repo-root/clone/<piece> subdirectories\n" .
          "      git_log_param  extra parameters passed to the git log command to define\n" .
          "                     the area of interest , e.g. --after=\"2010-09-27\" or\n" .
          "                     TAG..HEAD";
}


#######################################################################
#######################################################################
# MAIN
#######################################################################
#######################################################################


my $piece;
my $top_dir;
my @git_args;
my %data;
my %print_filters = ();

foreach my $arg (@ARGV) {
    if ($arg eq '--help') {
        usage();
        exit;
    } elsif ($arg eq '--no-pieces') {
        $piece = "bootstrap";
    } elsif ($arg =~ m/--piece=(.*)/) {
	$piece = $1;
    } elsif ($arg eq '--bugs') {
        $print_filters{'bug'} = 1;
    } else {
        if (! defined $top_dir) {
            $top_dir=$arg;
        } else {
            push @git_args, $arg;
        }
    }
}

(defined $top_dir) || die "Error: top direcotry is not defined\n";
(-d "$top_dir") || die "Error: not a directory: $top_dir\n";
(-f "$top_dir/.git/config") || die "Error: can't find $top_dir/.git/config\n";

load_data(\%data, $top_dir,$piece, \@git_args);
print_weekly_stat(\%data, \%print_filters);
