#!/usr/bin/env perl

###############################################################################
 #
 #  This file is part of canu, a software program that assembles whole-genome
 #  sequencing reads into contigs.
 #
 #  This software is based on:
 #    'Celera Assembler' (http://wgs-assembler.sourceforge.net)
 #    the 'kmer package' (http://kmer.sourceforge.net)
 #  both originally distributed by Applera Corporation under the GNU General
 #  Public License, version 2.
 #
 #  Canu branched from Celera Assembler at its revision 4587.
 #  Canu branched from the kmer project at its revision 1994.
 #
 #  Modifications by:
 #
 #    Brian P. Walenz beginning on 2016-JAN-11
 #      are a 'United States Government Work', and
 #      are released in the public domain
 #
 #    Sergey Koren beginning on 2018-OCT-11
 #      are a 'United States Government Work', and
 #      are released in the public domain
 #
 #  File 'README.licenses' in the root directory of this distribution contains
 #  full conditions and disclaimers for each license.
 ##

use strict;
use File::Compare;
use Cwd qw(getcwd);

my $cwd = getcwd();

#  On release change $label to 'release', update $major and $minor.  Make the
#  release, then change label back to 'snapshot'.  This will result in the
#  released code having a version string of "Canu 1.9".

my $label    = "release";       #  If not 'release' print this in the version output.
my $major    = "2";             #  Bump before release.
my $minor    = "0";             #  Bump before release.

my $branch   = "master";
my $version  = "v$major.$minor";

my $commits  = "0";
my $hash1    = undef;          #  This from 'git describe'
my $hash2    = undef;          #  This from 'git rev-list'
my $revCount = 0;
my $dirty    = undef;
my $dirtya   = undef;
my $dirtyc   = undef;


#  If in a git repo, we can get the actual values.

if (-d "../.git") {
    $label = "snapshot";

    #  Count the number of changes since the last release.
    open(F, "git rev-list HEAD |") or die "Failed to run 'git rev-list'.\n";
    while (<F>) {
        chomp;

        $hash2 = $_  if (!defined($hash2));
        $revCount++;
    }
    close(F);

    #  Find the commit and version we're at.
    open(F, "git describe --tags --long --always --abbrev=40 |") or die "Failed to run 'git describe'.\n";
    while (<F>) {
        chomp;
        if (m/^v(\d+)\.(.+)-(\d+)-g(.{40})$/) {
            $major   = $1;
            $minor   = $2;
            $commits = $3;
            $hash1   = $4;

            $version = "v$major.$minor";
        } else {
            die "Failed to parse describe string '$_'.\n";
        }
    }
    close(F);

    #  Decide if we've got locally committed changes.
    open(F, "git status |");
    while (<F>) {
        if (m/is\s+ahead\s/) {
            $dirtya = "ahead of github";
        }
        if (m/not\s+staged\s+for\s+commit/) {
            $dirtyc = "uncommitted changes";
        }
    }
    close(F);

    if    (defined($dirtya) && defined($dirtyc)) {
        $dirty = "ahead of github w/changes";
    }
    elsif (defined($dirtya)) {
        $dirty = "ahead of github";
    }
    elsif (defined($dirtyc)) {
        $dirty = "w/changes";
    }
    else {
        $dirty = "sync'd with github";
    }


    #  But if we're on a branch, replace the version with the name of the branch.
    open(F, "git rev-parse --abbrev-ref HEAD |");
    while (<F>) {
        chomp;
        $branch  = $_;
    }

    if ($branch ne "master") {
        if ($branch =~ m/v(\d+)\.(\d+)/) {
            $major = $1;
            $minor = $2;
        }

        $label   = "branch";
        $version = $branch;
    }
}


#  If not in a git repo, we might be able to figure things out based on the directory name.

elsif ($cwd =~ m/canu-(.{40})\/src/) {
    $label   = "snapshot";
    $hash1   = $1;
    $hash2   = $1;
}

elsif ($cwd =~ m/canu-master\/src/) {
    $label   = "master-snapshot";
}


#  Report what we found.  This is really for the gmake output.

if ($commits > 0) {
    print STDERR "Building $label $version +$commits changes (r$revCount $hash1) ($dirty)\n";
    print STDERR "\n";
} else {
    print STDERR "Building $label $version\n";
    print STDERR "\n";
}

#  Dump a new file, but don't overwrite the original.

open(F, "> canu_version.H.new") or die "Failed to open 'canu-version.H.new' for writing: $!\n";
print F "//  Automagically generated by canu_version_update.pl!  Do not commit!\n";
print F "#define CANU_VERSION_LABEL     \"$label\"\n";
print F "#define CANU_VERSION_MAJOR     \"$major\"\n";
print F "#define CANU_VERSION_MINOR     \"$minor\"\n";
print F "#define CANU_VERSION_COMMITS   \"$commits\"\n";
print F "#define CANU_VERSION_REVISION  \"$revCount\"\n";
print F "#define CANU_VERSION_HASH      \"$hash1\"\n";

if      ($commits > 0) {
    print F "#define CANU_VERSION           \"Canu $label $version +$commits changes (r$revCount $hash1)\\n\"\n";
} elsif (defined($hash1)) {
    print F "#define CANU_VERSION           \"Canu snapshot ($hash1)\\n\"\n";
} elsif ($label  =~ m/release/) {
    print F "#define CANU_VERSION           \"Canu $major.$minor\\n\"\n";
} else {
    print F "#define CANU_VERSION           \"Canu $label ($version)\\n\"\n";
}

close(F);

#  If they're the same, don't replace the original.

if (compare("canu_version.H", "canu_version.H.new") == 0) {
    unlink "canu_version.H.new";
} else {
    unlink "canu_version.H";
    rename "canu_version.H.new", "canu_version.H";
}

#  That's all, folks!

exit(0);

