#!/usr/bin/env bash
# SPDX-License-Identifier: Apache-2.0
# Copyright (C) 2021 Arm Limited or its affiliates and Contributors. All rights reserved.

[[ "$TRACE" ]] && set -x
set -euo pipefail

print_help() {
    echo 'Usage: git-list-between BRANCH YYYY/MM/DD YYYY/MM/DD [[--] <path>...]'
}

# Workaround for https://github.com/elfshaker/elfshaker/issues/89, that
# there are occasional issues with the clock, and '--since'
# unfortunately stops once it sees the first date which is older than
# the specified time. Strategy: Go back a large number of commits, and
# find the earliest with the given date.
find-first-commit() {
    ARGS=(
        # Tradeoff of runtime vs max expected commits in the month.
        # It's >10x higher than I have observed in a month.
        --max-count 40000
        --first-parent
        --topo-order
        --until="$DATETIME_UPPER"
        --format='%cd %H'
        --date=format-local:"%Y/%m/%d"
        "$BRANCH"
    )

    TZ=UTC git rev-list "${ARGS[@]}" | \
        egrep "^$DATE_LOWER" | \
        tail -n1 | \
        awk '$1 != "commit" {print $2}'
}

list() {
    FIRST_COMMIT=$(find-first-commit)
    ARGS=(
        # Don't follow the other side of merges.
        --first-parent
        # Commit order.
        --topo-order
        --reverse
        '--abbrev=15'
        "--until=$DATETIME_UPPER"
        "${FIRST_COMMIT}".."$BRANCH"
    )
    TZ=UTC git log "${ARGS[@]}" "$@"
}

main() {
    BRANCH="$1"
    DATE_LOWER="$2"
    DATETIME_LOWER="$2"' 00:00:00 UTC'
    DATETIME_UPPER="$3"' 00:00:00 UTC'
    EXTRA_ARGS=("${@:4}")

    # Validate args
    # shellcheck disable=SC2015
    git rev-parse "$BRANCH" 1>/dev/null \
        && date -d "$DATETIME_LOWER" 1>/dev/null \
        && date -d "$DATETIME_UPPER" 1>/dev/null \
        || {
            print_help
            exit 1
        }

    # Read the list of commits without passing EXTRA_ARGS
    # This prevents the list from being filtered by a PATHSPEC...
    readarray -t COMMIT_LIST < <(list '--format=format:%H')
    # Create a hashmap [SHA=>index] that contains the indexes of the commits in
    # the selected time span. These indexes represent an increasing 'generation
    # number' starting at 1 in the given series and increasing from there.
    # Histories with merge commits will have collisions in this number,
    # llvm-project has a linear-only history.
    declare -A INDEX_LOOKUP=()
    for i in "${!COMMIT_LIST[@]}"; do
        INDEX_LOOKUP["${COMMIT_LIST[$i]}"]=$i
    done

    # Construct the final list by passing EXTRA_ARGS, allowing the user to filter
    # the commits by a PATHSPEC..., use the index of the commit in the unfiltered
    # log, and format the output.
    while read -r SHA ABBREV DATE TIME; do
        INDEX="${INDEX_LOOKUP["$SHA"]}"
        printf '%s %s-%05dT%s-%s\n' "$SHA" "$DATE" "$((INDEX+1))" "$TIME" "$ABBREV"
    done < <(list \
        '--date=format-local:%Y%m%d %H%M%S' \
        '--format=format:%H %h %cd' \
        "${EXTRA_ARGS[@]}")
}

case "${1:--h}" in
    -h | --help)
        print_help
        [ $# -ne 0 ]
        exit $?
        ;;
    *)
        main "$@"
        ;;
esac
