#!/bin/bash
# shellcheck disable=SC1090,SC1091

# The MIT License (MIT)
#
# Copyright (c) 2013-2019 Pablo Acosta-Serafini
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# Find directory where script is
# from http://stackoverflow.com/questions/59895/
# can-a-bash-script-tell-what-directory-its-stored-in)
# BASH_SOURCE[0] is the pathname of the currently executing function or script
# -h True if file exists and is a symbolic link
# cd -P does not follow symbolic links
current_dir() {
    local sdir="$1"
    local udir=""
    # Resolve ${sdir} until the file is no longer a symlink
    while [ -h "${sdir}" ]; do
        udir="$(cd -P "$(dirname "${sdir}")" && pwd)"
        sdir="$(readlink "${sdir}")"
        # If ${sdir} was a relative symlink, we need to resolve it
        # relative to the path where the symlink file was located
        [[ "${sdir}" != /* ]] && sdir="${udir}/${sdir}"
    done
    udir="$(cd -P "$(dirname "${sdir}")" && pwd)"
    echo "${udir}"
}

strcat() {
  local IFS=""
  echo -n "$*"
}
# Options {{{
print=0
non_ascii_file_names=0
trailing_white_space=0
pep8=0
email=0
personal_repo=0
code_standard=0
pep257=0
cfg_fname="$(current_dir "${BASH_SOURCE[0]}")/repo-cfg.sh"
if [ -f "${cfg_fname}" ]; then
    source "$(current_dir "${BASH_SOURCE[0]}")/repo-cfg.sh"
else
    echo -e "\tConfiguration file not found, using defaults"
fi
pkg_dir=$(git rev-parse --show-toplevel)
if [ "${print}" == 1 ]; then
    echo -e "\tGit pre-commit setup"
    echo -e "\t\tprint=${print}"
    echo -e "\t\tnon_ascii_file_names=${non_ascii_file_names}"
    echo -e "\t\ttrailing_white_space=${trailing_white_space}"
    echo -e "\t\tpep8=${pep8}"
    echo -e "\t\tpep257=${pep257}"
    echo -e "\t\temail=${email}"
    echo -e "\t\tpersonal_repo=${personal_repo}"
    echo -e "\t\tcode_standard=${code_standard}"
fi
# }}}
# Redirect output to stderr {{{ STDOUT redirection
exec 1>&2
# }}}
# Determine initial commit {{{
if git rev-parse --verify HEAD >/dev/null 2>&1; then
    #echo "pre-commit check against HEAD"
    against=HEAD
else
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# }}}
# Check for non-ASCII file names {{{
if [ "${non_ascii_file_names}" == 1 ]; then
    if [ "${print}" == 1 ]; then
        echo -e "\tChecking for non-ASCII file names"
    fi
    # Cross platform projects tend to avoid non-ASCII filenames; prevent
    # them from being added to the repository. We exploit the fact that the
    # printable range starts at the space character and ends with tilde.
    # If you want to allow non-ASCII filenames set this variable to true.
    allownonascii=$(git config --bool hooks.allownonascii)
    if [ "$allownonascii" != "true" ] &&
        # Note that the use of brackets around a tr range is ok here,
        # (it's even required, for portability to Solaris 10's
        # /usr/bin/tr), since the square bracket bytes happen to fall
        # in the designated range.
        test "$(\
            git diff --cached --name-only --diff-filter=A -z "${against}" | \
            LC_ALL=C tr -d '[ -~]\0' | \
            wc -c \
        )" != 0
    then
        msg=$(strcat \
            "Error: attempt to add a non-ASCII file name.\n" \
            "This can cause problems if you want to work with people on\n" \
            "other platforms. To be portable it is advisable to rename\n" \
            "the file. If you know what you are doing you can disable\n" \
            " this check using:\n" \
            "    git config hooks.allownonascii true\n" \
        )
        >&2 echo "${msg}"
        exit 1
    fi
fi
# }}}
# Check for trailing whitespace {{{
if [ "${trailing_white_space}" == 1 ]; then
    if [ "${print}" == 1 ]; then
        echo -e "\tChecking for trailing whitespace"
    fi
    # If there are whitespace errors, print the offending file names and fail.
    diff_output=$(git diff-index --check --cached $against --)
    if [ "${diff_output}" != "" ]; then
        exec git diff-index --check --cached $against --
        exit 1
    fi
fi
# }}}
# Check for Python PEP8 (code style) compliance {{{
if [ "${pep8}" == 1 ]; then
    config_fname="${pkg_dir}/.pylintrc"
    if [ ! -f "${config_fname}" ]; then
        >&2 echo -e "Pylint config file ${config_fname} not found"
        exit 1
    fi
    if [ "${print}" == 1 ]; then
        echo -e "\tValidating Python PEP8 compliance with Pylint"
    fi
    python_files=$(git diff --name-only --cached -- *.py)
    pep8_output=""
    # shellcheck disable=SC2068
    for file in ${python_files[@]}; do
        if [ "${file}" != "" ]; then
            tfile="$(mktemp)"
            file_output=""
            pylint --rcfile="${config_fname}" -r n "${file}" &> "${tfile}"
            if [ "$?" != 0 ]; then
                file_output=$(cat "${tfile}")
                rm -rf "${tfile}"
            fi
            if [ "${file_output}" != "" ]; then
                pep8_output=${pep8_output}"\n"${file_output}
            fi
        fi
    done
    if [ "${pep8_output}" != "" ]; then
        >&2 echo -e "${pep8_output}"
        exit 1
    fi
    #if [ "${python_files}" != "" ]; then
    #   echo -e "All Python files are PEP8 compliant"
    #fi
fi
# }}}
# Check for Python PEP257 (docstring style) compliance {{{
if [ "${pep257}" == 1 ]; then
    config_fname="${pkg_dir}/.pydocstyle"
    if [ ! -f "${config_fname}" ]; then
        >&2 echo -e "Pydocstyle config file ${config_fname} not found"
        exit 1
    fi
    if [ "${print}" == 1 ]; then
        echo -e "\tValidating Python PEP257 compliance with pydocstyle"
    fi
    python_files=$(git diff --name-only --cached -- *.py)
    pep257_output=""
    # shellcheck disable=SC2068
    for file in ${python_files[@]}; do
        if [ "${file}" != "" ]; then
            tfile="$(mktemp)"
            file_output=""
            pydocsytle --config="${config_fname}" "${file}" &> "${tfile}"
            if [ "$?" != 0 ]; then
                file_output=$(cat "${tfile}")
                rm -rf "${tfile}"
            fi
            if [ "${file_output}" != "" ]; then
                pep257_output=${pep257_output}"\n"${file_output}
            fi
        fi
    done
    if [ "${pep257_output}" != "" ]; then
        >&2 echo -e "${pep257_output}"
        exit 1
    fi
    #if [ "${python_files}" != "" ]; then
    #   echo -e "All Python files are PEP257 compliant"
    #fi
fi
# }}}
# Check for congruence between author file and Git contact information {{{
if [ "${email}" == 1 ]; then
    fname=${pkg_dir}/AUTHORS.rst
    if [ ! -f "${fname}" ]; then
        >&2 echo -e "Email check could not be done, ${fname} not found"
        exit 1
    fi
    git_author="$(git config user.name) <$(git config user.email)>"
    names=$(\
        grep -E '^\s*.*\s+<.*>\s*$' "${fname}" | \
        sed -r -e 's/(\s*\*\s+)(.*)/\2/g' | \
        sed -r -e 's/\s+/ /g' \
    )
    names=${names//[$'\t\r\n']}
    sep=">"
    found=0
    while [ "${names}" != "${names#*${sep}}" ]; do
        name="${names%%${sep}*}${sep}"
        name="${name## }"
        echo "|${name}|"
        if [ "${name}" == "${git_author}" ]; then
            found=1
            break
        fi
        names="${names#*${sep}}"
    done
    if [ "${found}" == 0 ]; then
        >&2 echo -e "Error: git author ${git_author} not found in ${fname}"
        exit 1
    fi
fi
# }}}
# Check for code "standard" compliance {{{
if [ "${code_standard}" == 1 ]; then
    script="${pkg_dir}/bin/check_files_compliance.py"
    if [ ! -f "${script}" ]; then
        >&2 echo -e "Error: code compliance script ${script} not found"
        exit 1
    fi
    if [ "${print}" == 1 ]; then
        echo -e "\tChecking code standard compliance"
    fi
    files=$(git diff --name-only --cached)
    if ! "${script}" -qstp "${files}"; then
        >&2 echo -e "Error: files do not comply with package code standard"
        exit 1
    fi
fi
# }}}
