#!/bin/sh
############################################################ IDENT(1)
#
# $Title: Script to count perforce user commits by-year $
#
############################################################ GLOBALS

pgm="${0##*/}" # Program basename

#
# Global exit status
#
SUCCESS=0
FAILURE=1

#
# Command-line options
#
NOHEADER=	# -H
ONELINE=	# -1
PAST365=	# -Y
REVERSE=	# -r
VERBOSE=	# -v
YEARS=		# -y years

#
# Miscellaneous
#
NEWESTYEAR=
OLDESTYEAR=
USER_YEARS=
THIS_YEAR=$( date +%Y )
RANGE365=$( date +"@%m/%d/$(( $THIS_YEAR - 1 )),@now" )

############################################################ FUNCTIONS

usage()
{
	local fmt="$1"
	exec >&2
	if [ "$fmt" ]; then
		shift 1 # fmt
		printf "$pgm: $fmt\n" "$@"
	fi
	local optfmt="\t%-9s %s\n"
	printf "Usage: %s [OPTIONS] [user ...]\n" "$pgm"
	printf "OPTIONS:\n"
	printf "$optfmt" -1 "Output data as a single-line for each user/year."
	printf "$optfmt" -H "Don't print a header when \`-Y' is given."
	printf "$optfmt" -r "List years in reverse order (oldest first)."
	printf "$optfmt" -v "Verbose. Show years where user made zero commits."
	printf "$optfmt" "-y year" \
		"Specify years to list stats for (default all years)."
	printf "$optfmt" "" "This option can be specified multiple times."
	printf "$optfmt" -Y "Report number of commits made in past 365 days."
	printf "$optfmt" "" "Mutually exclusive to \`-y year'."
	exit $FAILURE
}

get_user_years()
{
	local user="$1" user_newest user_oldest date year

	USER_YEARS=
	NEWESTYEAR=
	OLDESTYEAR=

	# Base years off of actual user commit range
	user_newest=$( p4 changes -u "$user" | awk 'NR==1' )
	user_oldest=$( p4 changes -u "$user" | awk 'END{print}' )
	if [ ! "$user_newest" ]; then
		# No changes by user
		if [ "$VERBOSE" ]; then
			NEWESTYEAR=$THIS_YEAR
			OLDESTYEAR=$THIS_YEAR
			USER_YEARS=$THIS_YEAR
		fi
	elif [ "$user_newest" = "$user_oldest" ]; then
		# User has made exactly one change
		date="${user_newest#* on }"
		NEWESTYEAR="${date%%/*}"
		OLDESTYEAR=$NEWESTYEAR
		USER_YEARS=$NEWESTYEAR
	else
		# User has made multiple changes
		if [ "$VERBOSE" ]; then
			date="${user_newest#* on }"
			NEWESTYEAR="${date%%/*}"
			date="${user_oldest#* on }"
			OLDESTYEAR="${date%%/*}"
			if [ "$REVERSE" ]; then
				USER_YEARS=$( seq $OLDESTYEAR 1 $NEWESTYEAR )
			else
				USER_YEARS=$( seq $NEWESTYEAR -1 $OLDESTYEAR )
			fi
		else
			if [ "$REVERSE" ]; then
				USER_YEARS=$( p4 changes -u "$user" |
					awk '$0=substr($4,1,4)' | sort -un )
				OLDESTYEAR=$( echo $USER_YEARS | head -1 )
				NEWESTYEAR=$( echo $USER_YEARS | tail -1 )
			else
				USER_YEARS=$( p4 changes -u "$user" |
					awk '$0=substr($4,1,4)' | sort -unr )
				NEWESTYEAR=$( echo $USER_YEARS | head -1 )
				OLDESTYEAR=$( echo $USER_YEARS | tail -1 )
			fi
		fi
	fi
	if [ "$VERBOSE" ]; then
		year=$NEWESTYEAR
		while [ $year -lt $THIS_YEAR ]; do
			year=$(( $year + 1 ))
			if [ "$REVERSE" ]; then
				USER_YEARS="$USER_YEARS $year"
			else
				USER_YEARS="$year $USER_YEARS"
			fi
		done
	fi
	[ "$USER_YEARS" ] # return status
}

type tac > /dev/null 2>&1 || tac(){ tail -r "$@"; }

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

#
# Command-line options
#
while getopts 1Hrvy:Y flag; do
	case "$flag" in
	1) ONELINE=1 ;;
	H) NOHEADER=1 ;;
	r) REVERSE=1 ;;
	v) VERBOSE=1 ;;
	y) [ "$PAST365" ] && usage "cannot specify both \`-y year' and \`-Y'"
	   [ "$OPTARG" = "${OPTARG%[!0-9]*}" -a ${#OPTARG} -eq 4 ] ||
	   	usage "invalid \`-y' argument -- %s" "$OPTARG"
	   YEARS="$YEARS $OPTARG" ;;
	Y) [ "$YEARS" ] && usage "cannot specify both \`-y year' and \`-Y'"
	   PAST365=1 ;;
	*) usage
	esac
done
shift $(( $OPTIND - 1 ))

#
# Validate command-line arguments
#
for user in "$@"; do
	case "$user" in
	    "") usage "invalid (NULL) user argument" ;;
	*[#@]*) usage "Revision chars (@, #) not allowed in '%s'." "$user" ;;
	 *[*]*) usage "Wildcard (*) not allowed in user '%s'." "$user" ;;
	*[^\ !\"#$%\&\'\(\)+,./0-9:\;\<=\>?A-Z\[\\\]^_\`a-z{\|}~-]*)
		usage "Non-printable characters not allowed in '%s'." "$pgm" ;;
	esac
done

#
# Reverse `-y year' arguments if `-r' is given
#
[ "$REVERSE" -a "$YEARS" ] && YEARS=$( echo $YEARS | xargs -n1 | tac )

# NOTREACHED unless all users and all years are valid

#
# Report commits by-year for each user argument
#
[ "$PAST365" -a ! "$NOHEADER" ] &&
	echo "+++ Per-user statistics for: p4 changes $RANGE365" >&2
[ $# -gt 0 ] || set -- $( p4 users | awk '$0=$1' | sort )
for user in "$@"; do
	USER_YEARS="$YEARS"
	[ "$PAST365" -o "$USER_YEARS" ] || get_user_years "$user" || continue
	[ "$ONELINE" ] || print_header=1
	for year in $USER_YEARS; do
		# NOTREACHED if `-Y' given
		ncommits=$( p4 changes -u "$user" @01/01/$year,@12/31/$year |
			awk 'END{print NR}' )
		[ $ncommits -gt 0 -o "$VERBOSE" ] || continue
		if [ "$print_header" ]; then
			print_header=
			printf "+++ Commits by %s:\n" "$user"
		fi
		if [ "$ONELINE" ]; then
			printf "%8u y%4u %s\n" $ncommits $year "$user"
		else
			printf "\t... in the year %u: %u\n" $year $ncommits
		fi
	done
	if [ "$PAST365" ]; then
		# NOTREACHED if `-y year' given
		ncommits=$( p4 changes -u "$user" "$RANGE365" |
			awk 'END{print NR}' )
		[ $ncommits -gt 0 -o "$VERBOSE" ] &&
			printf "%8u %s\n" $ncommits "$user"
	fi
done

exit $SUCCESS

################################################################################
# END
################################################################################
#
# $Copyright: 2015 Devin Teske. All rights reserved. $
#
# $Header: //guest/freebsdfrau/p4t/libexec/year#2 $
#
################################################################################