#!/usr/bin/python
# -*- coding: UTF-8 -*-
#
# Copyright (c) 2007 Tarek Ziadé
#
# Authors:
#   Tarek Ziadé <tarek@ziade.org>
#
# This program 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
from subprocess import Popen
from subprocess import PIPE
import re
import os
import smtplib

re_options = re.IGNORECASE | re.MULTILINE | re.DOTALL

class EOF(object):
    def findall(self, content):
        if content.endswith('\n'):
            return []
        return ['\n']

tab_catcher = re.compile(r'^\t', re_options)
windows_catcher = re.compile(r'\r\n$', re_options)

testers = (('found <TAB>', tab_catcher),
           ('found <CR/LF>', windows_catcher),
           ('no new line at the end', EOF()))


def command_output(cmd):
    """ Capture a command's standard output."""
    return Popen(cmd.split(), stdout=PIPE).communicate()[0]

def pylint_output(cmd):
    """ Capture a command's standard output."""
    def _getLine(line):
        if line == '':
            return False
        if 'Wrong encoding specified' in line:
            return False
        return line[0] in ('E', 'C', 'W', 'F')

    return [line for line in
            Popen(cmd.split(), stdout=PIPE).communicate()[0].split('\n')
            if _getLine(line)]

def checkFileQuality(repos, path):
    """Checks a file"""
    file = os.path.join(repos, path)
    try:
        return command_output('pylint %s' % file).split('\n')
    except OSError:
        return []

def sendResults(file, results):
    """Sends QA result"""
    fromaddr = 'tarek@emencia.com'
    toaddrs  = ['tarek@emencia.com']
    subject = '[ECS QA Check] %s' % file
    msg = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n"
        % (fromaddr, ", ".join(toaddrs), subject))
    msg = '%s%s\n' % (msg, results)
    server = smtplib.SMTP('localhost')
    server.sendmail(fromaddr, toaddrs, msg)
    server.quit()

def files_changed(look_cmd):
    """ List the files added or updated by this transaction."""
    def filename(line):
        return line[4:]

    def added_or_updated(line):
        return line and line[0] in ("A", "U")

    return [filename(line) for line in
            command_output(look_cmd % "changed").split("\n")
            if added_or_updated(line)]

def file_contents(filename, look_cmd):
    """Return a file's contents for this transaction"""
    return command_output("%s %s" % (look_cmd % "cat", filename))

def test_expression(expr, filename, look_cmd):
    """test regexpr over file"""
    return len(expr.findall(file_contents(filename, look_cmd))) > 0

def check_file(look_cmd, repos):
    """checks Python files in this transaction"""
    def is_python_file(fname):
        return os.path.splitext(fname)[1] in ".py".split()

    erroneous_files = []

    checked = []

    for file in files_changed(look_cmd):
        if not is_python_file(file):
            continue

        errors = 0

        for error_type, tester in testers:
            if test_expression(tester, file, look_cmd):
                erroneous_files.append((error_type, file))
                errors += 1

    num_failures = len(erroneous_files)

    if num_failures > 0:
        sys.stderr.write("[ERROR] please check your files:\n")
        for error_type, file in erroneous_files:
            sys.stderr.write("[ERROR] %s in %s\n" % (error_type, file))

    return num_failures

def main():
    from optparse import OptionParser
    parser = OptionParser()
    parser.add_option("-r", "--revision",
                        help="Test mode. TXN actually refers to a revision.",
                        action="store_true", default=False)
    errors = 0
    (opts, (repos, txn_or_rvn)) = parser.parse_args()
    look_opt = ("--transaction", "--revision")[opts.revision]
    look_cmd = "svnlook %s %s %s %s" % (
        "%s", repos, look_opt, txn_or_rvn)
    errors += check_file(look_cmd, repos)

    return errors

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

