#!/bin/sh 
# Apply script for managing patches for debian builts
# Taken from debian kernel-source-2.4.27 package
# Original Author: Herbert Xu

set -e

length=50

die() {
	echo "E: $@" >&2
	exit 1
}

warn() {
	echo "W: $@" >&2
}

uncompress_patch() {
	patch=$1
	case "$patch" in
		*.bz2) bzcat $patch ;;
		*.gz) zcat $patch ;;
		*) cat $patch ;;
	esac
}

find_patch() {
	patch=$1

	if [ -f "$patch" ]; then
		echo "$patch"
	elif [ -f "$patch.bz2" ]; then
		echo "$patch.bz2"
	elif [ -f "$patch.gz" ]; then
		echo "$patch.gz"
	else
		die "$patch is in the series, but doesn't exist!"
	fi
}
		
apply_patch() {
	patch=$(find_patch $home/$1)
	base=$1
	if uncompress_patch "$patch" | patch -p1 -s -t --no-backup-if-mismatch; then
		printf "%-${length}s\tOK (+)\n" "$base"
	else
		printf "%-${length}s\tFAIL (+)\n" "$base"
		exit 1
	fi
}

deapply_patch() {
	patch=$(find_patch $home/$1)
	base=$1
	if uncompress_patch "$patch" | patch -p1 -s -t -R --no-backup-if-mismatch; then
		printf "%-${length}s\tOK (-)\n" "$base"
	else
		printf "%-${length}s\tFAIL (-)\n" "$base"
		exit 1
	fi
}

unpatch_series() {
	series=$1
	[ -f "$series" ] || die "I wasn't passed a series!"

	tac $series | while read action patch; do
		case "$action" in
			+) deapply_patch $patch ;;
			-) apply_patch $patch ;;
			X)
				bakfile="$(dirname $patch)/.$(basename $patch).bak"
				if [ -f "$bakfile" ]; then
					mv -f "$bakfile" "$patch"
					printf "%-${length}s\tRESTORED (X)\n" "$patch"
				else
					printf "%-${length}s\tNO BACKUP (X)\n" "$patch"
				fi
			;;
		esac
	done 
	echo "--> $(basename $series) fully unapplied."
}

patch_series() {
	series=$1
	[ -f "$series" ] || die "I wasn't passed a series!"

	while read action patch; do
		case "$action" in
			+) apply_patch $patch ;;
			-) deapply_patch $patch ;;
			X)
				bakfile="$(dirname $patch)/.$(basename $patch).bak"
				if [ -f "$patch" ]; then
					mv -f "$patch" "$bakfile"
					printf "%-${length}s\tREMOVED (X)\n" "$patch"
				else
					printf "%-${length}s\tNO FILE (X)\n" "$patch"
				fi
			;;
		esac
	done < $series
	echo "--> $(basename $series) fully applied."
}

bubble_sort ()
{
	DIR=$1
	shift
	SORTED=$@

	SWAPED=1
	while [ $SWAPED = 1 ]; do
		X=0
		A=""
		SWAPED=0
		NEW=""
		for i in $SORTED; do
			if [ -z "$A" ]; then
				A="$i"
				continue
			fi
			B="$i"
		
			if dpkg --compare-versions "$A" "$DIR" "$B"; then
				SWAPED=1
				NEW="$NEW $B"
			else
				NEW="$NEW $A"
				A="$B"
			fi

			X=$(($X + 1))
		done
		SORTED="$NEW $A"
	done

	echo $SORTED
}

bubble_sort_fwd ()
{
	bubble_sort "lt" $@
}

bubble_sort_rev ()
{
	bubble_sort "gt" $@
}


if ! [ -d heartbeat ]; then
	die 'Not in heartbeat top level directory.  Exiting'
	exit 1
fi

# for THIS particular version of the source package
#deb_version=$(sed -ne "/^heartbeat/ { s/.*(\([^)]*\)).*/\1/ p" -e "q}" \
#                ${PWD}/debian/changelog)
deb_version=$(dpkg-parsechangelog | awk '/^Version: / { print $2 }')
version=${override_version:-$deb_version}
upstream=${version%-*}
revision=${version#*-}

home="$PWD/debian/patches"


if [ -f version.Debian ]; then
	current=$(cat version.Debian)
	current_rev=${current#*-}
	current_up=${current%-*}

	if [ "$current" = "$upstream" ]; then
		current_rev=0
	fi
else
	warn "No version.Debian file, assuming pristine heartbeat $upstream"
	current=$upstream
	current_rev=0
fi

target=${1:-$version}

target_rev=${target#*-}
target_up=${target%-*}

# Sanity checks
if [ "$target_up" != "$upstream" ]; then
	die "Upstream $target_up doesn't match $upstream!"
# We don't have that version out yet!
elif [ ! -n "$target_rev" ] || ( [ "$target_rev" != "$target" ] && dpkg --compare-versions $target_rev gt $revision ); then
	year=$(($(date +%Y) + 1))
	die "Can't patch to nonexistent revision $target_rev (wait until $year)"
fi

# At this point, we must handle three cases.
# 1. $target_rev is greater than $current_rev. We must patch forward for this.
# 2. $target_rev is less than $current_rev. We must reverse the list of series,
#    reverse each used series (tac) and unapply applied patches and vice versa.
# 3. $target_rev is undefined, and $target is $upstream.

# Revert to upstream.
if [ "$target_rev" = "$target" ]; then
	# already reverted
	if [ "$current" = "$target" ]; then 
		echo "Nothing to do, exiting."
		exit 0
	fi

	for base in $(cd $home/series/ && bubble_sort_fwd $(ls -d *-*)); do
		srev=${base#*-}
		if [ -n "$srev" ]; then
			if dpkg --compare-versions $srev le $current_rev; then
				unpatch_series $home/series/$base
			fi
		else
			die "Series doesn't have a revision!"
		fi
	done
elif [ "$current_rev" = "$upstream" ] || dpkg --compare-versions $target_rev gt $current_rev; then
	for base in $(cd $home/series/ && bubble_sort_rev $(ls -d *-*)); do
		srev=${base#*-}
		if [ -n "$srev" ]; then
			if dpkg --compare-versions $srev gt $current_rev && dpkg --compare-versions $srev le $target_rev; then
				patch_series $home/series/$base
			fi
		else
			die "Series doesn't have a revision!"
		fi
	done
elif [ $target_rev = $current_rev ]; then
	echo "Nothing to do, exiting."
	exit 0
elif dpkg --compare-versions $target_rev lt $current_rev; then
        for base in $(cd $home/series/ && bubble_sort_fwd $(ls -d *-*)); do
		srev=${base#*-}
		if [ -n "$srev" ]; then
			# gt because you don't want to unapply the target series
			if dpkg --compare-versions $srev le $current_rev && dpkg --compare-versions $srev gt $target_rev; then
				unpatch_series $home/series/$base
			fi
		else
			die "Series doesn't have a revision!"
		fi
	done
fi

echo $target > version.Debian
