#!/usr/bin/env python
#
############################################################################
#
# MODULE:       grass_py_static_check
# AUTHOR(S):    Vaclav Petras <wenzeslaus gmail com>
# PURPOSE:      Static source code analysis of Python code for GRASS GIS
# COPYRIGHT:    (C) 2014 by Vaclav Petras and the GRASS Development Team
#
#               This program is free software under the GNU General Public
#               License (>=v2). Read the file COPYING that comes with GRASS
#               for details.
#
#############################################################################

"""
Can run only from the source code top level directory.
"""

import sys
import os
from subprocess import Popen, PIPE
import re


# also in gunittest.main
def ensure_dir(directory):
    if not os.path.exists(directory):
        os.makedirs(directory)


def pylint(target, output_dir):
    cmd = ['pylint', '--rcfile=tools/pylintrc.txt', '--output-format=html',
           '--ignore=.svn']
    cmd = cmd + [target]
    # graph options:
    # --import-graph=<file.dot>
    # --ext-import-graph=<file.dot>
    # --int-import-graph=<file.dot>
    # also we can call pylint directly as a package and create our own HTML
    # report with message ids and perhaps even symbolic message names
    # result
    ensure_dir(output_dir)
    output_file = open(output_dir + '/pylint_report.html', 'w')
    # Path to the directory where the persistent for the run will be stored.
    env = os.environ.copy()
    env['PYLINTHOME'] = output_dir
    proc = Popen(cmd, stdout=output_file, env=env)
    ret = proc.wait()
    if ret == 32:
        raise RuntimeError("pylint usage error")
    elif ret & 1:
        cmd = ['pylint', '--rcfile=tools/pylintrc.txt', '--ignore=.svn']
        cmd = cmd + [target]
        proc = Popen(cmd, stdout=PIPE, env=env)
        stdout, stderr = proc.communicate()
        pylint_status = "pylint fatal message was issued and prevented pytlint from working"
        msg = "{target}: {stat}\n{o}".format(stat=pylint_status, target=target, o=stdout)
        raise RuntimeError(msg)
    return get_num_of_messages_from_pylint_file(output_dir + '/pylint_report.html')


def pylint_errors(target, output_dir):
    # this ignores rcfile?
    cmd = ['pylint', '--errors-only', '--rcfile=tools/pylintrc.txt', '--output-format=html',
           '--ignore=.svn']
    cmd = cmd + [target]
    # result
    ensure_dir(output_dir)
    output_file = open(output_dir + '/pylint_errors_report.html', 'w')
    # Path to the directory where the persistent for the run will be stored.
    env = os.environ.copy()
    env['PYLINTHOME'] = output_dir
    proc = Popen(cmd, stdout=output_file, env=env)
    ret = proc.wait()
    if ret == 32:
        raise RuntimeError("pylint usage error")
    elif ret & 1:
        cmd = ['pylint', '--rcfile=tools/pylintrc.txt', '--ignore=.svn']
        cmd = cmd + [target]
        proc = Popen(cmd, stdout=PIPE, env=env)
        stdout, stderr = proc.communicate()
        pylint_status = "pylint fatal message was issued and prevented pytlint from working"
        msg = "{target}: {stat}\n{o}".format(stat=pylint_status, target=target, o=stdout)
        raise RuntimeError(msg)
    return get_num_of_messages_from_pylint_file(output_dir + '/pylint_errors_report.html')


def remove_absolute_path_from_pep8(txt_file):
    txt = open(txt_file, 'r')
    content = txt.read()
    txt.close()

    # this would need to be more sophisticated for being crossplatform
    # and to be a general function
    gisbase = os.environ['GISBASE']
    assert gisbase
    exp = re.compile('%s/?' % (gisbase))
    content = exp.sub('', content)

    txt = open(txt_file, 'w')
    txt.write(content)
    txt.close()


def get_num_of_messages_from_pep8_file(txt_file):
    count = 0
    txt = open(txt_file, 'r')
    exp = re.compile(r'^.+.py:[0-9]+: \[[EW][0-9]{1,3}\] .+$')
    for line in txt.readlines():
        if exp.match(line):
            count += 1
    txt.close()
    return count


def get_num_of_messages_from_pylint_file(html_file):
    count = 0
    txt = open(html_file, 'r')
    exp = re.compile(r'<td>(error|warning|refactor|convention)</td>')
    for line in txt.readlines():
        if exp.match(line):
            count += 1
        #print count, line, re.template
    txt.close()
    return count


def pep8txt_to_html(txt_file, html_file):
    txt = open(txt_file, 'r')
    html = open(html_file, 'w')
    html.write('<html><body>')
    html.write('<pre>')
    html.write(txt.read())
    html.write('</pre>')
    html.write('</body></html>')
    txt.close()
    html.close()


def pep8(target, output_dir):
    cmd = ['pep8', '--config=tools/pep8config.txt', '--format=pylint']
    cmd = cmd + [target]
    # result
    ensure_dir(output_dir)
    output_file = open(output_dir + '/pep8_report.txt', 'w')
    # Path to the directory where the persistent for the run will be stored.
    env = os.environ.copy()
    proc = Popen(cmd, stdout=output_file, env=env)
    proc.wait()
    # pep8 always returns 1 when some error occurred
    output_file.close()
    remove_absolute_path_from_pep8(output_dir + '/pep8_report.txt')
    pep8txt_to_html(output_dir + '/pep8_report.txt', output_dir + '/pep8_report.html')
    return get_num_of_messages_from_pep8_file(output_dir + '/pep8_report.txt')


def main():
    gisbase = os.environ['GISBASE']
    ensure_dir('pylint_report')

    main_index = open('pylint_report/index.html', 'w')

    # a lot of special treatment is needed to get packages nicely shown

    grass_packages = ['script', 'pygrass', 'temporal']
    # not included: 'imaging', 'pydispatch', 'lib'
    ensure_dir('pylint_report/grass')
    index = open('pylint_report/grass/index.html', 'w')
    for package in grass_packages:
        num_pylint = pylint('grass.' + package, 'pylint_report/grass/' + package)
        num_errors = pylint_errors('grass.' + package, 'pylint_report/grass/' + package)
        num_pep8 = pep8(gisbase + '/etc/python/grass/' + package, 'pylint_report/grass/' + package)
        index.write('{pkg}'.format(pkg=package))
        index.write(' <a href="{pkg}/pylint_report.html">pylint</a> ({num})'.format(pkg=package, num=num_pylint))
        index.write(' <a href="{pkg}/pylint_errors_report.html">pylint errors only</a> ({num})'.format(pkg=package, num=num_errors))
        index.write(' <a href="{pkg}/pep8_report.html">pep8</a> ({num})<br>'.format(pkg=package, num=num_pep8))
    index.close()
    main_index.write('<a href="grass/index.html">grass</a><br>'.format(pkg=package))

    dirlist = os.listdir(gisbase + '/gui/wxpython')
    packages = []
    directories = []
    for f in dirlist:
        if os.path.isdir('gui/wxpython/' + f):
            if os.path.isfile('gui/wxpython/' + f + '/__init__.py'):
                packages.append(f)
            else:
                directories.append(f)

    # this is generated but not used
    modules = [f for f in dirlist if f.endswith('.py')]
    subdirs_with_modules = {}
    for d in directories:
        subdirlist = os.listdir(gisbase + '/gui/wxpython/' + d)
        subdir_modules = [f for f in dirlist if os.path.isfile(gisbase + '/gui/wxpython/' + d + '/' + f) and f.endswith('.py')]
        if subdir_modules:
            subdirs_with_modules[d] = subdir_modules

    grass_wxgui_package = ['gui']
    ensure_dir('pylint_report/gui/wxpython')
    gui_dir = gisbase + '/gui/wxpython/'
    index = open('pylint_report/gui/wxpython/index.html', 'w')
    for package in packages:
        num_pylint = pylint(gui_dir + package, 'pylint_report/gui/wxpython/' + package)
        num_errors = pylint_errors(gui_dir + package, 'pylint_report/gui/wxpython/' + package)
        num_pep8 = pep8(gisbase + '/gui/wxpython/' + package, 'pylint_report/gui/wxpython/' + package)
        index.write('{pkg}'.format(pkg=package))
        index.write(' <a href="{pkg}/pylint_report.html">pylint</a> ({num})'.format(pkg=package, num=num_pylint))
        index.write(' <a href="{pkg}/pylint_errors_report.html">pylint errors only</a> ({num})'.format(pkg=package, num=num_errors))
        index.write(' <a href="{pkg}/pep8_report.html">pep8</a> ({num})<br>'.format(pkg=package, num=num_pep8))
    index.close()
    main_index.write('<a href="gui/wxpython/index.html">GRASS wxGUI</a><br>'.format(pkg=package))

    #pylint(grass_wxgui_packages_path, 'pylint_report/gui/scripts')

    return 0


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