#!/usr/bin/env bash

# The clang-format binary to use.
CLANG_FORMAT="${CLANG_FORMAT:-clang-format}"
# The set of files to check, either 'only-staged-modified', or 'all'.
# 'only-staged-modified' is useful in a git presubmit checks because it only
# checks files that are about to be commited.
TO_CHECK="${TO_CHECK:-only-staged-modified}"

declare -a BAD_FORMAT
declare DIFFS

function staged_modified_files() {
    git status --porcelain=2 \
        | egrep '^1 M' | awk '{ print $9 }'
}

function check_diff() {
    actual="$1"
    expected="$2"

    diff_out="$(diff --unified "${actual}" "${expected}")"
    if [[ $? -eq 0 ]]; then
        return
    fi

    BAD_FORMAT+=( "$actual" )

    # otherwise, a diff occured.
    if [[ -n "${DIFFS}" ]]; then
        DIFFS+=$'\n'
    fi
    DIFFS+="-- Diff in ${actual}:"
    DIFFS+=$'\n'
    # Use tail to hide the file name header.
    DIFFS+="$(echo "${diff_out}" | tail -n +3)"
}

function check_go() {
    F="$1"
    check_diff "$F" <(gofmt "$F") 
}

function check_c() {
    F="$1"
    check_diff "$F" <("${CLANG_FORMAT}" "$F")
}

function files_to_check() {
    case "${TO_CHECK}" in
        only-staged-modified)
            staged_modified_files
            ;;
        all)
            git ls-files
            ;;
        *)
            echo "Unrecognized TO_CHECK option \"${TO_CHECK}\"" >&2
            exit 1
            ;;
    esac
}

while read F; do
    if test -z "$F"; then continue; fi
    case "$F" in
        *.cc|*.c|*.h)
            check_c "$F"
            ;;
        *.go)
            check_go "$F"
            ;;
    esac
done <<<$( files_to_check )

if test "${#BAD_FORMAT[@]}" -gt 0; then
    echo "Unformatted files found:"
    printf "  %s\n" "${BAD_FORMAT[@]}"
    echo
    echo "Fix by running:"
    echo "  scripts/format \\"
    echo "    " "${BAD_FORMAT[@]}"
    echo
    echo "Diffs:"
    echo
    echo "${DIFFS}"
    echo
    echo "clang-format version: $("${CLANG_FORMAT}" --version)"
    echo "go version: $(go version)"
    exit 1
fi

exit 0
