#!/usr/bin/env python3

# ###################################################
# Copyright (C) 2008-2017 The Unknown Horizons Team
# team@unknown-horizons.org
# This file is part of Unknown Horizons.
#
# Unknown Horizons is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# ###################################################

"""
INFORMATION:
This is the pre-commit hook used on the unknown horizons team. It checks the
staged files using pyflakes and checks for added print statements. It gives you
the choice to cancel a commit if errors are found.

= Installation =

== Windows ==
Copy this file to .git/hooks/
Your done! If the hook is updated you have to copy it again manually.

== Linux/MacOS ==
cd .git/hooks
ln -s ../../development/pre-commit pre-commit
This will create a soft-link to the file in development and therefore keep it
updated automatically.

"""

import re
import sys
import tkinter
import tkinter.messagebox
from subprocess import PIPE, Popen

import crayons


def run(command):
	"""Runs a command and returns a triple with the output: (returnvalue, stdout, stderr)"""
	p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
	p.wait()
	return (
		p.returncode,
		[line.strip().decode('utf-8') for line in p.stdout.readlines()],
		[line.strip().decode('utf-8') for line in p.stderr.readlines()]
	)


def output(*args, **kwargs):
	kwargs['file'] = sys.stderr
	print(*args, **kwargs)
	sys.stderr.flush()


def check_syntax(filename):
	syntax_checker = "pycodestyle"
	output('Checking syntax on {}: '.format(filename), end='')

	exit_code, stdout, stderr = run('{} {}'.format(syntax_checker, filename))
	errors = stdout + stderr
	if errors:
		output(crayons.red('FAILED!'))
		for line in errors[:-1]:
			output("\t", line)

		return False
	else:
		output(crayons.green('OK!'))
		return True


def check_debug_statements(filename):
	"""
	Check for added prints or other stuff
	"""
	invalid_regexes = ['print\(', 'pdb', 'set_trace']

	output("Checking for 'print', 'pdb' and 'set_trace' on {}: ".format(filename), end='')

	_, result, _ = run('git diff-index -u HEAD {}'.format(filename))

	found_statement = False
	for i, line in enumerate(result):
		if not line.startswith('+'):
			continue

		for r in invalid_regexes:
			if re.search(r, line):
				if not found_statement:
					output(crayons.red('FAILED!'))
					found_statement = True

				if i > 0:
					output("\t", result[i - 1])
				output("\t", crayons.red(line))
				if len(result) > i + 1:
					output("\t", result[i + 1])

	if found_statement:
		return False
	else:
		output(crayons.green('OK!'))
		return True


def check_isort(filename):
	output('Checking isort on {}: '.format(filename), end='')

	exit_code, stdout, stderr = run('isort -c {}'.format(filename))
	errors = stdout + stderr
	if errors:
		output(crayons.red('FAILED!'))
		for line in errors:
			output("\t", line)

		return False
	else:
		output(crayons.green('OK!'))
		return True


def main():
	found_problems = False
	_, files_modified, _ = run("git diff-index --cached --name-only HEAD")

	for fname in files_modified:
		if fname.strip().endswith(".py"):
			if not check_syntax(fname):
				found_problems = True

			if not check_debug_statements(fname):
				found_problems = True

			if not check_isort(fname):
				found_problems = True

	if found_problems:
		# Hide root Tk window that is always created
		window = tkinter.Tk()
		window.wm_withdraw()

		if not tkinter.messagebox.askyesno("Errors have been detected", "Cancel Commit?"):
			sys.exit(0)
		else:
			sys.exit(1)
	else:
		sys.exit(0)


main()
