#!/bin/bash
#set -x

export LC_ALL=C

my_name="$( basename "${0}" )"

usage() {
    cat <<-_EOF_
		Usage:
		    ${my_name} <repos_dir> <M.m.p>
	_EOF_
}

repos="${1}"
version="${2}"

[ -n "${repos}"   ] || { usage; exit 1; }
[ -d "${repos}"   ] || { printf "${my_name}: ${repos}: no such file or directory\n"; exit 1; }
[ -n "${version}" ] || { usage; exit 1; }

_hg() {
    HGPLAIN=1 hg --config progress.disabled=true "$@"
}

gen_bound_revs() {
    r1=$( _hg log   \
          |awk 'BEGIN {
                  found=0;
                }
                $1=="'"${label}"':" {
                  split($2,a,":"); rev=a[1];
                }
                $0~/^summary:[[:space:]]*[[:digit:]]+\.[[:digit:]]+: '"${msg}"'/ \
                && found==0 {
                  printf( "%d\n", rev ); found=1;
                }'
        )

    r2=$( _hg log -b "${branch}" \
          |awk '$1=="changeset:" {
                  split($2,a,":");
                  printf( "%d\n", a[1] );
                  nextfile;
                }'
        )

    r1_log=$((r1+log_offset))
    if [ ${#r1_log} -gt ${#r2} ]; then
        rev_w=${#r1_log}
    else
        rev_w=${#r2}
    fi
}

print_intro_mail() {
    cat <<-_EOF_
		Hello all!
		
		I'm pleased to announce the release of crosstool-NG ${version}!
		
		As usual, there has been quite a number of improvements, new features,
		and bug fixes all around. The most notable changes are listed below:
		
		YEM:
		YEM: PUT YOUR MESSAGE HERE
		YEM:
		
		This marks the beginning of the ${ver_M}.${ver_m} maintenance branch, and the end of
		the previous maintenance branch. As always, comments and suggestions
		are most welcome!
		
		The release can be found at the following URLs:
		Changelog: http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-${version}.changelog
		Tarball:   http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-${version}.tar.bz2
		Patches:   http://crosstool-ng.org/download/crosstool-ng/01-fixes/${version}/
		
		As a reminder, the home for crosstool-NG is:
		http://crosstool-ng.org/
		
		Crosstool-NG also has a Freshmeat page:
		http://freshmeat.net/projects/crosstool-ng
	_EOF_
}

print_intro_changelog_full_release() {
    cat <<-_EOF_
		crosstool-NG ${version} -- ${date}
		
		This is a feature-release. Significant changes are:
		
		YEM:
		YEM: PUT YOUR MESSAGE HERE
		YEM:
	_EOF_
}

print_intro_changelog_bug_fix() {
    cat <<-_EOF_
		crosstool-NG ${version} -- ${date}
		
		This is a bug-fix-only release.
	_EOF_
}

print_author_stats() {
    printf "\nMany thanks to the people who contributed to this release:\n\n"
    prev_author=""
    template='{author|person}\n'
    _hg log -b "${branch}" -r "${r1_log}:${r2}"                 \
           --template "${template}"                             \
    |sed -r -e 's/"//g;'                                        \
    |awk -F '' '{
                  nb[$0]++;
                }
                END {
                  for( author in nb ) {
                    printf( "   %4d  %s\n", nb[author], author );
                  }
                }'                                              \
    |sort -s -k1nr -k2
}

print_author_shortlog() {
    printf "\nHere is the per-author shortlog:\n"
    prev_author=""
    template='{author|person}|{rev}|{branches}|{desc|firstline}\n'
    _hg log -b "${branch}" -r "${r1_log}:${r2}"             \
           --template "${template}"                         \
    |awk -F '' '{
                  n=split( $0,a,"|" );
                  printf( "%s", gensub("\"","","g",a[1]) );
                  printf( "|%0*d", '${rev_w}', a[2] );
                  for(i=3;i<=n;i++) {
                    printf( "|%s", a[i] );
                  }
                  printf( "\n" );
                }'                                          \
    |sort                                                   \
    |while read line; do
        author="$( echo "${line}" |cut -d \| -f 1 )"
        rev="$( echo "${line}" |cut -d \| -f 2 )"
        br="$( echo "${line}" |cut -d \| -f 3 )"
        desc="$( echo "${line}" |cut -d \| -f 4- )"

        case "${br}" in
            ${branch})  ;;
            [0-9]*.*)    continue;;
            *) ;;
        esac

        case "${desc}" in
            Merge.)                 continue;;
            *": close "*" branch"*) continue;;
#           *\(merged\))            continue;;
        esac

        author="$( echo "${author}" |sed -r -e 's/"//g;' )"

        if [ ! "${prev_author}" = "${author}" ]; then
            printf "\n"
            printf "    ${author}:\n"
            prev_author="${author}"
        fi
        rev="$( echo "${rev}" |sed -r -e 's/^0*//;' )"

        printf "%s\n" "${desc}"     \
        |fmt -w 65                  \
        |(first=1; while read l; do
            if [ -n "${first}" ]; then
                printf "        [%*d] %s\n" ${rev_w} ${rev} "${l}"
                first=
            else
                printf "         %*.*s  %s\n" ${rev_w} ${rev_w} '' "${l}"
            fi
        done)
    done
}

print_diffstat() {
    printf "\nThe diffstat follows:\n\n"
    _hg diff -r "${r1}:${r2}" --color=never \
    |diffstat -r 2 -p 1 -w 10               \
    |tail -n 1                              \
    |sed -r -e 's/^ */    /;'

    _hg diff -r "${r1}:${r2}" --color=never \
    |diffstat -f 1 -r 2 -p 1 -w 10          \
    |head -n -1                             \
    |while read file line; do
        if [ ${#file} -gt 57 ]; then
            file="$( echo "${file}" |sed -r -e 's/^(.{,24}).*(.{28})$/\1.....\2/;' )"
        fi
        printf "    %-57s %s\n" "${file}" "${line}"
    done
}

print_short_diffstat() {
    printf "\nThe short diffstat follows:\n\n"

    eval total=$(( $(
        _hg diff -r "${r1}:${r2}" --color=never "${i}"                              \
        |diffstat -r 2 -p 1 -w 10                                                   \
        |tail -n 1                                                                  \
        |sed -r -e 's/^[[:space:]]*[[:digit:]]+ files? changed(,[[:space:]]+|$)//;' \
                -e 's/([[:digit:]]+)[^-\+]+\((-|\+)\)/\1/g;'                        \
                -e 's/,//g; s/ /+/; s/^$/0/;'
    ) ))
    printf "    %-24.24s %5d(+/-)\n" "Total" ${total}
    others=${total}

    { for i in              \
        kconfig/            \
        patches/            \
        config/*/           \
        scripts/build/*/    \
        samples/            \
        ; do
        eval val=$(( $(
            _hg diff -r "${r1}:${r2}" --color=never "${i}"                              \
            |diffstat -r 2 -p 1 -w 10                                                   \
            |tail -n 1                                                                  \
            |sed -r -e 's/^[[:space:]]*[[:digit:]]+ files? changed(,[[:space:]]+|$)//;' \
                    -e 's/([[:digit:]]+)[^-\+]+\((-|\+)\)/\1/g;'                        \
                    -e 's/,//g; s/ /+/; s/^$/0/;'
        ) ))
        if [ ${val} -gt $((total/100)) ]; then
            printf "%d %s\n" $(((1000*val)/total)) "${i}"
            others=$((others-val))
        fi
    done; printf "%d Others\n" $(((1000*others)/total)); }  \
    |sort -nr                                               \
    |{ while read v i; do
        if [ "${i}" = "Others" ]; then
            others=${v}
        else
            printf "    %-24.24s %3d.%d%%\n" "${i}" $((v/10)) $((v%10))
        fi
       done; printf "    %-24.24s %3d.%d%%\n" "Others" $((others/10)) $((others%10)); }
}

ver_M="$( printf "${version}" |cut -d . -f 1 )"
ver_m="$( printf "${version}" |cut -d . -f 2 )"
ver_p="$( printf "${version}" |cut -d . -f 3 )"

sob_line="$( printf "Signed-off-by: "; _hg debugconfig ui.username )"
prefix="$(pwd)/crosstool-ng-${version}"
pushd "${repos}" >/dev/null 2>&1

printf "Checking for existing tag: "
if _hg tags |grep -E '^'"crosstool-ng-${version}"'\>' >/dev/null; then
    printf "already tagged\n"
    exit 1
fi
printf "no\n"

if [ ${ver_p} -eq 0 ]; then
    print_mail="yes"
    print_intro_changelog="print_intro_changelog_full_release"
    label="parent"
    msg="create maintenance branch, (update|bump) version to [[:digit:]]+"'\'".[[:digit:]]+"'\'".0"'$'
    branch="default"
    log_offset=0
else
    print_mail="no"
    print_intro_changelog="print_intro_changelog_bug_fix"
    label="changeset"
    msg="(update|bump) version to ${ver_M}"'\'".${ver_m}"'\'".$((ver_p-1))"'\+hg$'
    branch="${ver_M}.${ver_m}"
    log_offset=1
fi

printf "Computing boundary revisions:"
gen_bound_revs
printf " %d:%d\n" ${r1} ${r2}

printf "Tagging release:"
_hg up "${branch}" >/dev/null
if [ ${ver_p} -eq 0 ]; then
    printf " update version"
    _hg branch "${ver_M}.${ver_m}" >/dev/null
    echo "${version}" >".version"
    _msg="$( printf "%s.%s: create maintenance branch, update version to %s\n\n%s" \
                    "${ver_M}" "${ver_m}" "${version}" "${sob_line}"
           )"
    _hg ci -m "${_msg}"
else
    printf " update version"
    echo "${version}" >".version"
    _msg="$( printf "%s.%s: update version to %s\n\n%s" \
                    "${ver_M}" "${ver_m}" "${version}" "${sob_line}"
           )"
    _hg ci -m "${_msg}"
fi

printf ", tag"
_msg="$( printf "Tagging release %s\n\n%s" "${version}" "${sob_line}" )"
_hg tag -m "${_msg}" crosstool-ng-${version}

printf ", update version"
echo "${version}+hg" >".version"
_msg="$( printf "%s.%s: update version to %s+hg\n\n%s" \
                "${ver_M}" "${ver_m}" "${version}" "${sob_line}"
       )"
_hg ci -m "${_msg}"

printf ", date"
date="$( _hg log -r crosstool-ng-${version} --template '{date|isodate}\n'    \
         |sed -r -e 's/-|://g; s/ /./; s/ //;'                              \
       )"
printf ", done.\n"

if [ ${ver_p} -eq 0 ]; then
    printf "Generating release mail:"
    printf " intro"
    print_intro_mail        > "${prefix}.mail"
    printf ", stats"
    print_author_stats      >>"${prefix}.mail"
    printf ", shortlog"
    print_author_shortlog   >>"${prefix}.mail"
    printf ", diffstat"
    print_short_diffstat    >>"${prefix}.mail"
    printf ", done.\n"
fi

printf "Generating release changelog:"
printf " intro"
${print_intro_changelog}    > "${prefix}.changelog"
printf ", stats"
print_author_stats          >>"${prefix}.changelog"
printf ", shortlog"
print_author_shortlog       >>"${prefix}.changelog"
printf ", diffstat"
print_diffstat              >>"${prefix}.changelog"
printf ", done.\n"

popd >/dev/null 2>&1

printf "Creating tarball:"
prefix="crosstool-ng-${version}"
printf " extract"
date="$( _hg log -R "${repos}" -r "${prefix}" --template '{date|rfc822date}\n' )"
tmpdir="$( mktemp -d --tmpdir XXXXXX )"
_hg archive --cwd "${repos}" -r "${prefix}" -X '.hg*' --type files "${tmpdir}/${prefix}"
printf ", bootstrap"
pushd "${tmpdir}/${prefix}" >/dev/null 2>&1
./bootstrap >/dev/null
rm -rf autom4te.cache
popd >/dev/null 2>&1
printf ", tarball"
tar cjf "$(pwd)/${prefix}.tar.bz2" -C "${tmpdir}" "${prefix}"
rm -rf "${tmpdir}"
printf ", sum"
for s in md5 sha1 sha512; do
    ${s}sum "${prefix}.tar.bz2" >"${prefix}.tar.bz2.${s}"
done
printf ", touch"
touch -d "${date}" "${prefix}"*
printf ", done.\n"

if [ ${ver_p} -eq 0 ]; then
    printf "\nAn editor will be launched for you to edit the mail.\n"
    read -p "Press enter when ready..." foo
    cp "${prefix}.mail"{,.orig}
    vi "${prefix}.mail"
    diff -du -U 1 "${prefix}.mail"{.orig,} |patch -p0 "${prefix}.changelog" >/dev/null
    rm -f "${prefix}".{mail,changelog}.orig
fi

printf "\nAn editor will be launched for you to review the changelog.\n"
read -p "Press enter when ready..." foo
vi "${prefix}.changelog"

printf "\nNow, you can push the changes with:   hg push -R '${repos}'\n"
