#!/bin/bash
basedir=$(cd $(dirname $0); pwd)
chroot=$basedir/chroot

if [ `whoami` == 'root' ]; then
   echo "Must not be run as root."
   exit 1;
fi

exe() { echo "\$ $@" ; "$@" ; }

create_chroot() {
    (
	set -e
	if [ ! -d $chroot ]; then
	    echo "## Creating arch chroot ..."
	    exe mkdir $chroot
	    exe mkarchroot $chroot/root base-devel
	    # install cower:
	    gpg --recv-keys --keyserver hkp://pgp.mit.edu 1EB2638FF56C0C53
    	    TMP_BUILD=`mktemp -d`
    	    exe cd $TMP_BUILD
	    curl https://aur.archlinux.org/cgit/aur.git/snapshot/cower.tar.gz | tar xz
	    exe cd cower
	    exe makechrootpkg -r $chroot -l root -- -i
	    exe rm -rf $TMP_BUILD
	    echo "## Root chroot build complete."
	fi
    )
}

install_system_deps() {
    (
	set -e
	pkg_dep() {
	    if !(pacman -Q $1 > /dev/null 2>&1); then
		echo "Installing $1..."
		exe sudo pacman -S --noconfirm $1
	    fi
	}
	pkg_dep devtools
	pkg_dep git
	pkg_dep findutils
    )
}

package_build() {
    # Build a package in a container. $1=container $2=package
    # PKGBUILD must already exist in src/
    # If package has dependencies we also maintain in src/, recursively build those first.
    (
	set -e
	if [ "$#" -ne 2 ]; then
	    echo "package_build takes two args: repo_name and package_name"
	    return 1
	fi
	repo_name=$1
	package_name=$2
	setup
	pkgdir=$basedir/src/$package_name
	if [ ! -d $pkgdir ]; then
	    echo "Package $package_name not found in $pkgdir"
	    echo "Try running: package_add $package_name"
	    return 1
	fi
	if [ ! -f $pkgdir/PKGBUILD ]; then
	    echo "Cannot find PKGBUILD in $pkgdir"
	    return 1
	fi
	if (arch-nspawn $chroot/$repo_name pacman -Q $package_name > /dev/null 2>&1); then
	    echo "Package $package_name already built"
	    return
	fi
	# Find and build dependencies:
	find_deps $package_name | while read dep; do
	    # Build the dep if we haven't already yet:
	    if !(arch-nspawn $chroot/$repo_name pacman -Q $dep > /dev/null 2>&1); then
		package_build $repo_name $dep
	    fi
	done
	# Build package:
	cd $pkgdir
	rm -f *.pkg.tar.xz
	mkdir -p $basedir/$repo_name
	exe makechrootpkg -r $chroot -l $repo_name -- -i
	exe mv *.pkg.tar.xz $basedir/$repo_name
    )
}

sign_packages() {
    if [ "$#" -lt 1 ]; then
	echo "sign_packages requires specifying the list of packages to sign"
	return 1
    fi
    (
	set -e
	cd $(dirname $1)
	for pkg in "$@"; do
		gpg -v "${pkg}.sig" 2>/dev/null || (rm -f "${pkg}.sig"; gpg -u 'ECB9B8CBAAC68C03!' --detach-sign --use-agent "$pkg")
	done
    )
}

find_deps() {
    # Inspect package srcinfo and pull out the dependencies that we maintain in src
    (
	set -e
	setup
	cat $basedir/src/$1/.SRCINFO | sed -nr 's/^\W*depends = ([-a-zA-Z0-9]+).*$/\1/p' | while read dep; do
	    if [ -d $basedir/src/$dep ]; then
		echo $dep
	    fi
	done
    )
}

add() {
    # Add packages and their AUR dependencies to src
    (
	set -e
	if [ "$#" -lt 1 ]; then
	    echo "Must specify package(s) to add"
	    return 1
	fi
	setup
	if [ ! -d $basedir/src ]; then
	    mkdir $basedir/src
	fi
	for pkg in "$@"; do
	    arch-nspawn $chroot/root --bind=$basedir/src:/src cower -q -t /src -f -d -d $pkg
	done
	sudo chown $USER -R $basedir/src
	echo "## All requested packages added"
   )
}

update() {
    # Update a package and it's dependencies from the AUR
    (
	#set -e # breaks updating all if one fails, ignore errors
	if [ "$#" -eq 0 ]; then
	    cd $basedir/src
	    update *
	    return
	fi
	for pkg in "$@"; do
	    if [ ! -d $basedir/src/$pkg ]; then
		echo "No package called $pkg found in src"
		return 1
	    fi
	done
	for pkg in "$@"; do
	    check_in_git() {
		(
		    cd $basedir
		    if (! git ls-files $1 --error-unmatch >/dev/null 2>&1); then
			echo "$1 is not under git control yet. Add and commit it before updating."
			return 1
		    fi
		)
	    }
	    check_in_git $basedir/src
	    check_in_git $basedir/src/$pkg
	    find_deps $pkg | while read dep; do
		check_in_git $basedir/src/$dep
	    done
	    # Check if package has uncommited changes
	    if [ $(git -C $basedir diff HEAD $basedir/src/$pkg | wc -l) -gt 0 ]; then
		echo "warning: Package $pkg has uncommited changes. Commit or discard those changes first."
		#return 1
	    fi
	    # Use a temporary dir to download all PKGBUILDS atomically
            [ -z "$tmp_dl" ] && tmp_dl=`mktemp -d`
            cd $tmp_dl
            arch-nspawn $chroot/root --bind=$tmp_dl:/src cower -q -t /src -d -d $pkg >/dev/null 2>&1
	    cp -a * $basedir/src
	done
	git -C $basedir diff --name-status $basedir/src/
    )
}


list() {
    if [ "$#" -ne 1 ]; then
	echo "Must specify repository name to list"
	return 1
    fi
    setup
    arch-nspawn $chroot/$1 pacman -Qm
}

setup() {
    mkdir -p $basedir/src
    install_system_deps
    create_chroot
}

clean() {
    (
        set -e
        if [ "$#" -lt 1 ]; then
            echo "Must specify repository names to clean"
            return 1
        fi
        for repo in "$@"; do
            # Clean repository and chroot
            if [ -d $basedir/chroot/$repo ]; then
            # Test if chroot uses BTRFS and remove using btrfs subvolume delete
                if [ `stat --format=%i $basedir/chroot/$repo` -eq 256 ] && [ `stat -f --format=%T $basedir/chroot/$repo` ] ; then
                    exe sudo btrfs subvolume delete $basedir/chroot/$repo
                    exe sudo rm $basedir/chroot/$repo.lock
            # Otherwise delete using rm
                else
                    exe sudo rm -rf $basedir/chroot/$repo $basedir/chroot/$repo.lock
                fi
            fi
            if [ -d $basedir/$repo ]; then
                exe rm -rf $basedir/$repo
            fi
        done
    )
}

build() {
    (
	set -e
	if [ "$#" -lt 1 ]; then
	    echo "Must specify repository name to build (and optional package list to include)"
	    return 1
	fi
	setup
	repo_name=$1
	mkdir -p $basedir/$repo_name
	shift
	if [ "$#" -gt 0 ]; then
	    # Build only requested packages
	    for pkg in "$@"; do
		package_build $repo_name $pkg
	    done
	    echo "## All requested packages built"
	else
	    # Build all packages:
	    cd $basedir/src
	    find -type d | sed 's/\.\///' | tail -n +2 | while read pkg; do
		package_build $repo_name $pkg
	    done
	    echo "## All packages built"
	fi
	echo "## Updating repository database"
	cd $basedir/$repo_name
	if [ `ls *.pkg.tar.xz 2> /dev/null | wc -l` -lt 1 ]; then
	    echo "No packages found in $basedir/$repo_name"
	    return 1;
	fi
	sign_packages *.pkg.tar.xz
	repose -f -z $repo_name
	sign_packages $repo_name.db $repo_name.files
    )
}

rebuild() {
		(
	set -e
	if [ "$#" -lt 1 ]; then
	    echo "Must specify repository name to build (and optional package list to include)"
	    return 1
	fi
	setup
	repo_name=$1
		mkdir -p $basedir/$repo_name
		shift
		arch-nspawn $chroot/$repo_name pacman -Rscn --noconfirm "$@" || true
		build "$repo_name" "$@"
		)
}

build_changed() {
    (
    # git diff-tree --no-commit-id --name-status -r "$commit" | grep -v ^D | grep PKGBUILD$ | sed -e 's@/PKGBUILD$@@' -e 's@.*/@@' | xargs -n1 arch-nspawn chroot/aur pacman -Rscn --noconfirm
    # git diff-tree --no-commit-id --name-status -r "$commit" | grep -v ^D | grep PKGBUILD$ | sed -e 's@/PKGBUILD$@@' -e 's@.*/@@' | xargs -n1 ./arch-ppa build aur
	set -e
	if [ "$#" -lt 1 ]; then
	    echo "Must specify repository name to build (and optional package list to include)"
	    return 1
	fi
	setup
	repo_name=$1
		mkdir -p $basedir/$repo_name
		shift
		commit="$1"
		[ "$commit" == "" ] && commit=HEAD
    changed_pkgbuilds="$(git diff-tree --no-commit-id --name-status -r "$commit" | grep -v ^D | grep PKGBUILD$ | sed -e 's@/PKGBUILD$@@' -e 's@.*/@@' | tr '\n' ' ')"
    # remove them    
    arch-nspawn $chroot/$repo_name pacman -Rscn --noconfirm $changed_pkgbuilds || true
		build "$repo_name" $changed_pkgbuilds
    )
}

if [ "$#" -gt 0 ]; then
    $*
else
    echo "Must specify a command, eg: add, build, list, clean"
    exit 1
fi
