#!/usr/bin/env bash
# Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md

set -e
set -o pipefail
# trap 'echo ERROR on line: $LINENO running command: $BASH_COMMAND ; trap - EXIT; exit $?' EXIT

# Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. 
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

# taken from: https://raw.githubusercontent.com/andrewseidl/githook-clang-format/master/clang-format.hook
# - with just added check for cpp file & check for proper version of clang-format
# - with use of clang-format diff.py - for changed chunk changes only
# might be worth to consider: https://github.com/Sarcasm/run-clang-format

# shellcheck source=../config/format-config.sh
# shellcheck source=../config/clang/colors.sh
# shellcheck source=../config/clang/common.sh

L_GIT_DIR=$(git rev-parse --show-toplevel)
source $L_GIT_DIR/config/format-config.sh
source $L_GIT_DIR/config/clang/colors.sh
source $L_GIT_DIR/config/clang/clang-common.sh
CHANGE_TARGET=${CHANGE_TARGET:-master}


# if autoformatting was disabled by user - then ignore this commit hook
if [ "$DISABLE_AUTO_FORMATTING" -eq 1 ]; then
    [[ $VERBOSE ]] && echo "auto formatting is disabled"
    exit 0
fi

verify_clang_format_version

L_CLANG_TOOL=$(get_clang_format)
if ! [[ $L_CLANG_TOOL ]] || [[ $L_CLANG_TOOL == "" ]]; then
    [[ $VERBOSE ]] && echo "clang-format not found"
    exit 1
fi

L_CLANG_DIFF_TOOL=$(get_clang_format_diff)
if ! [[ $L_CLANG_DIFF_TOOL ]] || [[ $L_CLANG_DIFF_TOOL == "" ]]; then
    [[ $VERBOSE ]] && echo "clang-format-diff not found"
    exit 1
fi


check_file() {
    file="${1}"
    last_commit="${2}"
    if [ -f "${file}" ]; then
        #[[ $VERBOSE ]] && echo -en "Checking: \e[33m${file}\e[0m :\t"
        results["${file}"]="${OK}"
        case ${last_commit} in
        "True")
            if [[ ${FIX,,} == "true" ]]; then
                git diff -U0 --no-color remotes/origin/"${CHANGE_TARGET}"...HEAD "${file}" | ${L_CLANG_DIFF_TOOL} -style file -p1 -i
                STATUS=$(git status --short -- "${file}")
                if [ -n "${STATUS}" ]; then
                    git add "${file}"
                    results["${file}"]="${FIXED}";
                fi
            else
                OUT=$(git diff -U0 --no-color remotes/origin/"${CHANGE_TARGET}"...HEAD "${file}" | ${L_CLANG_DIFF_TOOL} -style file -p1 )
                if [ -n "${OUT}" ]; then
                    results["${file}"]="${ERROR}"
                    return 1
                fi
            fi
            ;;
        "Stage")
            if [[ ${FIX,,} == "true" ]]; then
                git diff -U0 --no-color --cached "${file}" | ${L_CLANG_DIFF_TOOL} -style file -p1 -i
                STATUS=$(git status --short -- "${file}")
                if [ -n "${STATUS}" ]; then
                    git add "${file}"
                    results["${file}"]="${FIXED}";
                fi
            else
                OUT=$(git diff -U0 --no-color --cached "${file}" | ${L_CLANG_DIFF_TOOL} -style file -p1)
                if [ -n "${OUT}" ]; then
                    results["${file}"]="${ERROR}"
                    return 1
                fi
            fi
            ;;
        "All")
            if [[ ${FIX,,} == "true" ]]; then
                ${L_CLANG_TOOL} -style file -i ${file}
                STATUS=$(git status --short -- "${file}")
                if [ -n "${STATUS}" ]; then
                    git add "${file}"
                    results["${file}"]="${FIXED}";
                fi
            else
                OUT=$(${L_CLANG_TOOL} -style file ${file})
                if [ -n "${OUT}" ]; then
                    results["${file}"]="${ERROR}"
                    return 1
                fi
            fi
            ;;
        *)
            OUT=$(git diff -U0 --no-color --cached "${file}" | ${L_CLANG_DIFF_TOOL} -style file -p1 )
            if [[ -n ${OUT} ]]; then
                results["${file}"]="${ERROR}"
                return 1
            fi
            ;;
        esac
    fi
    return 0
}

function help() {
        echo "Runs clang-format on source files"
        cat <<- EOM
		usage:
		   $0 [option]
		        --about             does nothing
		        --last              checks current branch against origin/master - doesn't fix
		        --branch-fix        checks current branch against origin/master and fixes errors - you have to 'git add' and 'git commit' them
		        --fix               fix style in currently staged files (ignores user.fixinstage variale), this is usefull for manual style applying
		        --all               check style in all files
		        --fix-all           fix style in all files
		
		If you would like to automatically fix style before commit
		add to your git config "user.fixinstage" variable with value "True"
		by calling:
		    git config user.fixinstage True
		EOM
}


case "${1}" in
    --about|--help )
        help
        FILES=''
        LAST=""
        exit 0
        ;;
    --last)
        FILES=$(git diff --name-only remotes/origin/"${CHANGE_TARGET}"...HEAD)
        LAST="True"
        FIX="false"
        ;;
    --branch-fix)
        FILES=$(git diff --name-only remotes/origin/"${CHANGE_TARGET}"...HEAD)
        LAST="True"
        FIX="true"
        ;;
    --fix)
        FILES=$(git diff-index --cached --name-only HEAD)
        LAST="Stage" 
        FIX="true"
        ;;
    --all)
        FILES=$(find $L_GIT_DIR -type f -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' -o -name '*.cxx' -o -name '*.gcc' -o -name '*.cc')
        LAST="All" 
        FIX="false"
        ;;
    --fix-all)
        FILES=$(find $L_GIT_DIR -type f -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' -o -name '*.cxx' -o -name '*.gcc' -o -name '*.cc')
        LAST="All" 
        FIX="true"
        ;;
    *)
        if [[ $# -ne 0 ]]; then
            echo "unknown parameters: '$*'"
            help
            exit 1
        fi
        FILES=$(git diff-index --cached --name-only HEAD)
        LAST="Stage" 
        FIX=$([[ $(git config user.fixinstage) ]] && git config user.fixinstage || echo "false")
        FIX=${FIX:-false}
        ;;
esac

declare -A results

EXIT_CODE=0
for file in ${FILES}; do
    if [[ ${file} =~ ^.*\.(cpp|hpp|c|h|cxx|gcc|cc)$ ]] && shouldnt_ignore ${file}; then
        check_file "${file}" ${LAST} || RESULT=$?
        if [[ ${RESULT} -eq 1 ]]; then
            EXIT_CODE=1
        fi
    fi
done

for FILE in "${!results[@]}"
do
    echo -e "  ${FILE} : ${results[${FILE}]}"
done

echo "exit: ${EXIT_CODE}"
exit ${EXIT_CODE}

