#!/usr/bin/env python3
#
# Script to check if any GNOME games Vala coding guidelines are violated.
#
# Copyright (C) 2015 Sahil Sareen (ssareen [ AT ] gnome [ DOT ] org)
#
# 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.

# =======================
#     Sample usage
# =======================
#
# To use it as a pre-commit hook, Update autogen.sh to symlink to this file
#
# if [ -d $srcdir/.git ]; then
#   for HOOK in pre-commit pre-applypatch
#   do
#     if [ ! -L $srcdir/.git/hooks/$HOOK ]; then
#       ln -s ../../../libgnome-games-support/style-checker \
#         $srcdir/.git/hooks/$HOOK && echo "Enabled $HOOK style checker."
#     fi
#   done
# fi
#
# Note: This can be used as the following hooks:
# Client side
#  - pre-commit
#  - pre-applypatch
#  - pre-push
# Server side
#  - pre-receive

import sys
import re
import os


def find(s, ch):
    return [i for i, ltr in enumerate(s) if ltr == ch]


def main():
    print("Validating the diff ...")

    p = os.popen('git diff --cached --unified=0', "r")

    # Read the diff
    lines = []
    lineNum = 0
    fileName = ''
    nIssues = 0

    while True:
        line = p.readline()
        if not line:
            break
        if line.startswith('@@'):
            lineNumSearch = re.search(r'.* .* \+(\d+).*', line)
            lineNum = int(lineNumSearch.groups()[0])
        elif line.startswith('+'):
            if line.startswith('+++'):
                pos = line.rfind('/')
                fileName = line[pos + 1: -1]
            elif fileName.endswith('vala'):
                lines += [(fileName, lineNum, line[1:])]
                lineNum += 1

    def getLineFileName(line):
        return line[0]

    def getLineNum(line):
        return line[1]

    def getLineData(line, pos=-1):
        if pos == -1:
            return line[2]
        else:
            return line[2][pos]

    def getLineWidth(line):
        return len(getLineData(line))

    def printIssue(line, msg):
        print("%s => Line %d %s"
              % (getLineFileName(line), getLineNum(line), msg))

    for lineNum in range(0, len(lines)):
        currLine = lines[lineNum]

        # Line Width
        if getLineWidth(currLine) > 120:
            printIssue(currLine, "is greater than 120 characters")
            nIssues += 1

        # Lines with trailing white-space or tabspace
        if getLineData(currLine).endswith(' \n'):
            printIssue(currLine, "has trailing whitespace")
            nIssues += 1

        if getLineData(currLine).endswith('\t\n'):
            printIssue(currLine, "has trailing tabspace")
            nIssues += 1

        # Lines with tabspace
        if '\t' in getLineData(currLine):
            printIssue(currLine, "has tabspace")
            nIssues += 1

        # Single whitespace before "{"
        indexes = find(getLineData(currLine), '{')
        for index in indexes:
            if index > 1:
                if getLineData(currLine, index - 1) not in [' ', '_']:
                    printIssue(currLine, "missing whitespace before {")
                    nIssues += 1
                elif getLineData(currLine, index - 2) == ' ' \
                    and getLineData(currLine, index - 1) != '_' \
                    and not (re.findall("\s*{",
                             getLineData(currLine)) != [] and
                             getLineData(currLine, 0) in ['\t', ' ']):
                    printIssue(currLine, "multiple whitespace before {")
                    nIssues += 1

        # Single whitespace before "("
        indexes = find(getLineData(currLine), '(')
        for index in indexes:
            if index > 1:
                if getLineData(currLine, index - 1) != ' ' \
                    and getLineData(currLine, index - 1) \
                        not in ['_', '(', '&', '*', '-', '$', '!', '\t', '"']:
                    printIssue(currLine, "missing whitespace before (")
                    nIssues += 1
                elif getLineData(currLine, index - 2) == ' ' \
                    and getLineData(currLine, index - 1) not in ['_', '('] \
                    and not (getLineData(currLine).startswith(" *") or
                             getLineData(currLine).startswith("#") or
                             (re.findall("\s*\(",
                              getLineData(currLine)) != [] and
                                 getLineData(currLine, 0) in ['\t', ' '])):
                    printIssue(currLine, "multiple whitespace before (")
                    nIssues += 1

        # No whitespace between round brackets "(xyz)"
        indexes = find(getLineData(currLine), '(')
        for index in indexes:
            if index > 1:
                if getLineData(currLine, index + 1) == ' ':
                    printIssue(currLine, "has whitespace after (")
                    nIssues += 1

        indexes = find(getLineData(currLine), ')')
        for index in indexes:
            if index > 1:
                if getLineData(currLine, index - 1) == ' ':
                    printIssue(currLine, "has whitespace before )")
                    nIssues += 1

    if nIssues != 0:
        if nIssues == 1:
            print("Guideline checker found one issue.")
        else:
            print("Guideline checker found " + str(nIssues) + " issues.")

        if os.path.basename(__file__) == 'pre-commit':
            print("To ignore use `git commit --no-verify`")
            return -1
        elif os.path.basename(__file__) == 'pre-push':
            print("To ignore use `git push --no-verify`")
            return -1
        elif os.path.basename(__file__) == 'pre-receive':
            print("Your push has been rejected.")
            return -1
        elif os.path.basename(__file__) == 'pre-applypatch':
            print("We strongly recommend you to fix these.")
            print("Continuing anyway....")
    else:
        print("Guideline checker found no issues.")

    return 0

if __name__ == "__main__":
    sys.exit(main())
