#!/usr/bin/env bash
#
# @license Apache-2.0
#
# Copyright (c) 2019 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.

# Script for compiling a Fortran benchmark.
#
# Usage: compile_fortran_benchmark <filepath>
#
# Arguments:
#
#   filepath      Benchmark file path.
#
#
# Environment Variables:
#
#   OS                  Host operating system. Default: `linux`.
#   NODE                Command for running Node.js. Default: `node`.
#   NODE_PATH           Path for resolving node modules.
#   FORTRAN_COMPILER    Fortran compiler. Default: `gfortran`.
#   INCLUDE             Includes (e.g., `-I /foo/bar -I /a/b`).
#   SOURCE_FILES        Source file list.
#


# VARIABLES #

# Set the benchmark file path:
file_path="$1"

# Define the host operating system:
os="${OS}"
if [[ "${os}" = "Darwin" ]]; then
	os='mac'
elif [[ "${os}" = "WINNT" ]]; then
	os='win'
else
	os='linux'
fi

# Define the command for running Node.js:
node_cmd="${NODE}"
if [[ -z "${node_cmd}" ]]; then
	node_cmd='node'
fi

# Define the node path:
node_path="${NODE_PATH}"

# Define the path to a Fortran compiler:
fortran_compiler="${FORTRAN_COMPILER}"
if [[ -z "${fortran_compiler}" ]]; then
	fortran_compiler='gfortran'
fi

# Define a list of `include` directories (e.g., `-I /foo/bar -I /beep/boop/include`):
include="${INCLUDE}"

# Define a list of source files:
source_files="${SOURCE_FILES}"


# FUNCTIONS #

# Defines an error handler.
#
# $1 - error status
on_error() {
	echo 'ERROR: An error was encountered during execution.' >&2
	cleanup
	exit "$1"
}

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

# Prints a success message.
print_success() {
	echo 'Success!' >&2
}

# Prints usage information.
usage() {
	echo '' >&2
	echo 'Usage: compile_fortran_benchmark <filepath>' >&2
	echo '' >&2
}

# Resolves a package path.
#
# $1 - source directory
resolve_pkg_path() {
	local pkg_path
	local script

	# Generate the script for resolving a package path:
	script='"'"var resolve = require('@stdlib/fs/resolve-parent-path').sync; var dirname = require('@stdlib/utils/dirname'); var opts = {'dir':'$1'}; var path = resolve('package.json', opts); console.log(dirname(path));"'"'

	# Resolve package path:
	pkg_path=$(eval NODE_PATH="${node_path}" "${node_cmd}" -e "${script}")

	echo "${pkg_path}"
}

# Resolves a package `manifest.json`.
#
# $1 - package path
resolve_pkg_manifest() {
	if [[ -e "$1/manifest.json" ]]; then
		return 0
	fi
	return 1
}

# Resolves `include` directories.
#
# $1 - package directory
resolve_includes() {
	local includes
	local script
	local opts

	opts="{'task':'benchmark'}"

	# Generate the script for resolving `include` directories:
	script='"'"var path = require('path'); var arr = require('@stdlib/utils/library-manifest')(path.join('$1','manifest.json'),${opts},{'basedir':'$1','paths':'posix'}).include; var str = ''; for (var i = 0; i < arr.length; i++){var p = path.resolve('$1', arr[i]); str += '-I '+p+' ';}; console.log(str.substring(0, str.length-1));"'"'

	# Resolve `include` directories:
	includes=$(eval NODE_PATH="${node_path}" "${node_cmd}" -e "${script}")

	echo "${includes}"
}

# Compiles benchmark.
#
# $1 - source directory
compile() {
	if [[ -z "${source_files}" ]]; then
		cd "$1" && FORTRAN_COMPILER="${fortran_compiler}" INCLUDE="${include}" make 2>&1
	else
		cd "$1" && FORTRAN_COMPILER="${fortran_compiler}" INCLUDE="${include}" SOURCE_FILES="${source_files}" make 2>&1
	fi
	if [[ "$?" -ne 0 ]]; then
		echo 'Error when attempting to compile benchmark.' >&2
		return 1
	fi
	echo 'Successfully compiled benchmark.' >&2
	return 0
}


# MAIN #

# Main execution sequence.
main() {
	local pkg_path
	local manifest
	local src_dir

	echo 'Resolving package path...' >&2
	pkg_path=$(resolve_pkg_path "${src_dir}")
	if [[ "$?" -ne 0 ]]; then
		on_error 1
	fi
	echo "Package path: ${pkg_path}" >&2

	echo 'Resolving package manifest...' >&2
	manifest=$(resolve_pkg_manifest "${pkg_path}")
	if [[ "$?" -eq 0 ]]; then
		echo 'Successfully resolved package manifest.' >&2
		if [[ -z "${include}" ]]; then
			echo 'Resolving include directories...' >&2
			include=$(resolve_includes "${pkg_path}")
			if [[ "$?" -ne 0 ]]; then
				on_error 1
			fi
		fi
	else
		echo 'Unable to resolve package manifest.' >&2
	fi
	echo 'Compiling benchmark...' >&2
	src_dir=$(dirname "${file_path}")
	compile "${src_dir}"
	if [[ "$?" -ne 0 ]]; then
		on_error 1
	fi
	print_success
	cleanup
	exit 0
}

# Handle arguments...
if [[ "$#" -eq 0 ]]; then
	usage
	exit 0
elif [[ "$#" -gt 1 ]]; then
	echo 'ERROR: unrecognized arguments. Must provide a file path.' >&2
	exit 1
fi

# Run main:
main
