#!/usr/bin/env bash
#
# @license Apache-2.0
#
# Copyright (c) 2017 The Stdlib Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# A git hook called by `git push` after it has checked the remote status, but before anything has been pushed. If this script exits with a non-zero status, nothing will be pushed.
#
# This hook is called with the following arguments:
#
# * `$1` - name of the remote to which the push is being done
# * `$2` - URL to which the push is being done
#
# If pushing without using a named remote, these arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to `stdin` in the form:
#
# ``` text
# <local ref> <local sha1> <remote ref> <remote sha1>
# ```

# shellcheck disable=SC2181


# VARIABLES #

remote="$1"

# Determine the current branch:
GIT_CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)

# Determine root directory:
root=$(git rev-parse --show-toplevel)


# FUNCTIONS #

# Defines an error handler.
#
# $1 - error status
on_error() {
	cleanup
	exit "$1"
}

# Runs clean-up tasks.
cleanup() {
	echo '' >&2
}

# Checks if commits exist to push, as `git push` will execute regardless of whether commits exist to push or not.
has_commits() {
	local commits

	echo 'Checking if remote branch exists...' >&2
	if git branch -r | grep "${GIT_CURRENT_BRANCH}" > /dev/null; then
		echo 'Remote branch exists.' >&2
		echo 'Checking for commits to push...' >&2
		commits=$(git log "${remote}/${GIT_CURRENT_BRANCH}..${GIT_CURRENT_BRANCH}" --oneline --)
	else
		echo 'Remote branch does not exist.' >&2
		echo 'Checking for commits to push...' >&2
		commits=$(git log "${GIT_CURRENT_BRANCH}" --oneline --)
	fi
	if [[ -z "${commits}" ]]; then
		echo 'No commits to push.'
		return 1
	fi
	echo 'Found commits to push.'
	return 0
}

# Checks licenses.
check_licenses() {
	echo 'Checking licenses...' >&2
	make check-licenses-production > /dev/null 2>&1
	if [[ "$?" -ne 0 ]]; then
		# shellcheck disable=SC2016
		echo 'Detected dependency licensing issues. Run `make check-licenses-production` to see failing dependency licenses.' >&2
		return 1
	fi
	echo 'No dependency licensing issues detected.' >&2
	return 0
}

# Main execution sequence.
main() {
	local changed_files
	local files

	has_commits
	if [[ "$?" -ne 0 ]]; then
		on_error 1
	fi
	check_licenses
	if [[ "$?" -ne 0 ]]; then
		on_error 1
	fi

	# Get the set of changed files (added, copied, modified, and renamed):
	changed_files=$(git diff --name-only --cached --diff-filter ACMR "${remote}/${GIT_CURRENT_BRANCH}" | sed -e "s#^#${root}\\/#")

	# Run JavaScript example files:
	files=$(echo "${changed_files}" | grep '/examples/.*\.js$' | grep -v '/examples/fixtures/.*\.js$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running JavaScript example files...' >&2
		make FILES="${files}" examples-javascript-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running JavaScript examples.' >&2
			on_error 1
		fi
	fi

	# Run C example files...
	files=$(echo "${changed_files}" | grep '/examples/.*\.c$' | grep -v '/examples/fixtures/.*\.c$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running C example files...' >&2
		make FILES="${files}" examples-c-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running C examples.' >&2
			on_error 1
		fi
	fi

	# Run C++ example files...
	files=$(echo "${changed_files}" | grep '/examples/.*\.cpp$' | grep -v '/examples/fixtures/.*\.cpp$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running C++ example files...' >&2
		make FILES="${files}" examples-cpp-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running C++ examples.' >&2
			on_error 1
		fi
	fi

	# Run JavaScript examples in README files:
	files=$(echo "${changed_files}" | grep 'README.md$' | grep -v '^tools/' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running README JavaScript examples...' >&2
		make FILES="${files}" markdown-examples-javascript-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running README JavaScript examples.' >&2
			on_error 1
		fi
	fi

	# TODO: run CLI examples found in Markdown files

	# Run C benchmark files...
	files=$(echo "${changed_files}" | grep '/benchmark/.*\.c$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running C benchmark files...' >&2
		make FILES="${files}" benchmark-c-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running C benchmarks.' >&2
			on_error 1
		fi
	fi

	# Run C++ benchmark files...
	files=$(echo "${changed_files}" | grep '/benchmark/.*\.cpp$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running C++ benchmark files...' >&2
		make FILES="${files}" benchmark-cpp-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running C++ benchmarks.' >&2
			on_error 1
		fi
	fi

	# Run Fortran benchmark files...
	files=$(echo "${changed_files}" | grep '/benchmark/.*\.f$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running Fortran benchmark files...' >&2
		make FILES="${files}" benchmark-fortran-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running Fortran benchmarks.' >&2
			on_error 1
		fi
	fi

	# Run JavaScript benchmark files...
	files=$(echo "${changed_files}" | grep '/benchmark/.*\.js$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running JavaScript benchmark files...' >&2
		make FILES="${files}" benchmark-javascript-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running JavaScript benchmarks.' >&2
			on_error 1
		fi
	fi

	# Run Julia benchmark files...
	files=$(echo "${changed_files}" | grep '/benchmark/.*\.jl$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running Julia benchmark files...' >&2
		make FILES="${files}" benchmark-julia-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running Julia benchmarks.' >&2
			on_error 1
		fi
	fi

	# Run Python benchmark files...
	files=$(echo "${changed_files}" | grep '/benchmark/.*\.py$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running Python benchmark files...' >&2
		make FILES="${files}" benchmark-python-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running Python benchmarks.' >&2
			on_error 1
		fi
	fi

	# Run R benchmark files...
	files=$(echo "${changed_files}" | grep '/benchmark/.*\.R$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running R benchmark files...' >&2
		make FILES="${files}" benchmark-r-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running R benchmarks.' >&2
			on_error 1
		fi
	fi

	# Run JavaScript test files...
	files=$(echo "${changed_files}" | grep '/test/.*\.js$' | grep -v '/test/fixtures/.*\.js$' | tr '\n' ' ')
	if [[ -n "${files}" ]]; then
		echo 'Running JavaScript test files...' >&2
		make FILES="${files}" test-javascript-files > /dev/null >&2
		if [[ "$?" -ne 0 ]]; then
			echo '' >&2
			echo 'Encountered an error when running JavaScript tests.' >&2
			on_error 1
		fi
	fi

	cleanup
	exit 0
}

# Run main:
main
