#!/usr/bin/env bash

# patch-tool: A tool for maintaining the head.hackage patchset

set -e

patches_dir=$(realpath $(dirname $0)/../patches)

split_pkg_version() {
    package=$(echo $1 | sed 's/\(.\+\)-\([0-9]\+\(\.[0-9]\+\)*\)/\1/')
    version=$(echo $1 | sed 's/\(.\+\)-\([0-9]\+\(\.[0-9]\+\)*\)/\2/')
}

# Return the version number of the most recent release of the given package
latest_version() {
    pkg=$1
    curl -s -H "Accept: application/json" -L -X GET http://hackage.haskell.org/package/$pkg/preferred | jq '.["normal-version"] | .[0]' -r
}

drop_old() {
    for patch in patches/*.patch; do
        split_pkg_version $(basename $patch .patch)
        latest=$(latest_version $package)
        #echo "$patch $package $version $latest "
        if [ "$latest" != "$version" ]; then
            echo "$patch is obsolete (latest version of $package is $latest), removed"
            git rm -q $patch
        fi
    done
}

add_pkg_dirs() {
    local dirs=$@
    echo "optional-packages: $dirs" >> cabal.project.local
    echo "Added $dirs to cabal.project.local"
}

patch_pkg() {
    local pkg=$(basename $1 .patch)
    local patch=$patches_dir/$pkg.patch
    if [ ! -e "$patch" ]; then
        echo "No patches for $pkg; skipping patching." return
    else
        local pkg_dir=packages/$pkg
        git -C $pkg_dir apply $patch
        git -C $pkg_dir commit -q -a -m "head.hackage.org patch"
    fi
}

unpack_patch_pkg() {
    local patch=$(basename $1)
    local pkg=$(basename $patch .patch)
    unpack_pkg $pkg
    patch_pkg $patch
}

unpack_patch_all() {
    for p in $patches_dir/*.patch; do
        unpack_patch_pkg $p
    done
}

# Unpack the given package
unpack_pkg() {
    local pkg=$1
    pushd packages
    cabal unpack $pkg
    local pkg_dir="$(ls -d $pkg* | head)"
    if [ -z "$pkg_dir" ]; then
        echo "failed to unpack $pkg"
        exit 1
    fi
    cd $pkg_dir
    git init
    git add .
    git commit -q -m "Initial commit of $pkg"
    git tag upstream
    popd
    add_pkg_dirs packages/$pkg_dir
}

update_patches() {
    for p in packages/*; do
        local patch_path=$patches_dir/$(basename $p).patch
        git -C $p diff upstream > $patch_path
        git -C $patches_dir add $patch_path
    done
    git -C $patches_dir status .
}

mkdir -p packages

usage() {
    cat <<EOF
usage: $0 MODE ...

Modes:
  unpack-all            unpack and patch all packages with patches
  unpack \$pkg           unpack the given package into packages/
  unpack-patch \$pkg     unpack and apply patches to the given package
  update-patches        update patches for all unpacked packages
  drop-old              drop patches for obsolete package versions
EOF
}

case "X$1" in
    Xunpack)
        unpack_pkg $2
        ;;

    Xunpack-all)
        unpack_patch_all
        ;;

    Xunpack-patch)
        patch=$2
        unpack_pkg $(basename $patch .patch)
        patch_pkg $patch
        ;;

    Xupdate-patches)
        update_patches
        ;;

    Xdrop-old)
        drop_old
        ;;

    *)
        echo "unknown mode $1"
        usage
esac
