#!/bin/bash
# PHP CodeSniffer pre-commit hook for git
#
# @author Lars Eidam <larseidam@googlemail.com>
#
# Git pre-commit hook to check chanced Code with the PHP CodeSniffer.
# Features:
#  - differnt severity, coding standard, whitelist filetypes and report types
#    are possible
#  - not be set parameter are taken from the Makefile
#  - only added or chanced files in the staging area be tested
#  - if partially files in the staging area, only the parts of the file in the
#    staging area be checked, not the whole file (ATTENTION:it makes it a little
#    bit difficult to find the right line were a error or warning is caused)
#  - check if a commit goes to the AKSW/Erfurt Master branch, if so the commit will
#    be canceled, because it's forbidden to commit in this branch
#

PCSOURCE="${BASH_SOURCE[0]}"
while [ -h "$PCSOURCE" ] ; do PCSOURCE="$(dirname $0)/$(readlink "$PCSOURCE")"; done
PCDIR="$( cd -P "$( dirname "$PCSOURCE" )" && pwd )"

source $PCDIR/config;

# check if the commit goes to the master branch of AKSW/OntoWiki.git, if so the commit will be canceled
# get the remote url
REMOTEURL=$(git config remote.`git config branch.$(git rev-parse --abbrev-ref HEAD).remote`.url)
if [[ $REMOTEURL =~ 'AKSW/Erfurt.git' ]]; then
    # get the current branch
    BRANCH=$(git config branch.$(git rev-parse --abbrev-ref HEAD).merge | cut -d "/" -f 3-)
    if [ "$BRANCH" == "master" ]; then
        echo "A commit in the protected Master branch of AKSW/Erfurt is forbidden. Commit canceled!"
        exit 1
    fi
fi

# folder for the tempary stage files
TMP_STAGING=".tmp_staging"

# set window-width
WIDTH=80;

# parse parameter for severity, cspath (path to the coding standard), filetypes
# and reporttyp
while getopts ":sp:f:r:" optname
    do
        case "$optname" in
        "s")
            SEVERITY=$SEVERITY_INTENSIVE;
        ;;
        "p")
            CSPATH=$OPTARG
        ;;
        "f")
            FILETYPES=$OPTARG
        ;;
        "r")
            REPORT=$OPTARG
        ;;
        "?")
            echo "Unknown option $OPTARG"
        ;;
        ":")
            echo "No argument value for option $OPTARG"
        ;;
        *)
            # Should not occur
            echo "Unknown error while processing options"
        ;;
    esac
done

# if no parameter are set then read the parameter from the Makefile
if [ -e $REPORT ]; then
    REPORT="full"
fi

CSPATH=$PCDIR/$CSPATH

# parameter for the CodeSniffer
ARGS="-n -p -s --report=$REPORT --report-width=$WIDTH --severity=$SEVERITY --standard=$CSPATH"

# check if CodeSniffer are found
phpcs --version
RETVAL=$?
if [ "$RETVAL" -ne "0" ]; then
echo "PHP CodeSniffer bin not found or executable"
    exit 1
fi

# get the reference commit
if [ `git rev-parse --verify HEAD` ]; then
    against='HEAD'
else
    # Initial commit: diff against an empty tree object
    against='4b825dc642cb6eb9a060e54bf8d69288fbee4904'
fi

# retrieve all files in staging area that are added, modified or renamed
# but no deletions etc
COMMITFILES=$(git diff-index --name-only --cached --diff-filter=ACMR $against -- )

if [ "$COMMITFILES" == "" ]; then
    exit 1
fi

# clean and create temporary copy of staging area
if [ -e $TMP_STAGING ]; then
    rm -rf $TMP_STAGING
fi
mkdir $TMP_STAGING


# match files against whitelist
PHPCS_FILE_PATTERN=`echo $FILETYPES | tr , '|'`
PHPCS_FILE_PATTERN="\.("$PHPCS_FILE_PATTERN")$"
FILES_TO_CHECK=""
FILES_COUNT=0;
for FILE in $COMMITFILES
do
    echo "$FILE" | egrep -q "$PHPCS_FILE_PATTERN"
    RETVAL=$?
    if [ "$RETVAL" -eq "0" ]; then
        FILES_TO_CHECK="$FILES_TO_CHECK $FILE"
        FILES_COUNT=$((FILES_COUNT+1))
    fi
done
if [ "$FILES_TO_CHECK" == "" ]; then
    rm -rf $TMP_STAGING
    exit 0
fi
# if more than 5 files to check then show only a summary
if [[ $FILES_COUNT > 5 ]]; then
    ARGS="$ARGS --report=summary"
else
    ARGS="$ARGS --report=$REPORT"
fi

# Copy contents of staged version of files to temporary staging area
# because we only want the staged version that will be commited and not
# the version in the working directory
STAGED_FILES=""
for FILE in $FILES_TO_CHECK
do
  ID=$(git diff-index --cached HEAD $FILE | cut -d " " -f4)
  # create staged version of file in temporary staging area with the same
  # path as the original file so that the phpcs ignore filters can be applied
  mkdir -p "$TMP_STAGING/$(dirname $FILE)"
  git cat-file blob $ID > "$TMP_STAGING/$FILE"
  STAGED_FILES="$STAGED_FILES $TMP_STAGING/$FILE"
done

OUTPUT=$(phpcs $ARGS $STAGED_FILES)
RETVAL=$?

# delete temporary copy of staging area
rm -rf $TMP_STAGING

OUTPUT=${OUTPUT//.tmp_staging\//}

# check for output
if [ $RETVAL -ne 0 ]; then
    echo "$OUTPUT"
fi

exit $RETVAL
