# -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import os

from tools.translate import _
from base_module_quality import base_module_quality

class quality_test(base_module_quality.abstract_quality_check):

    def __init__(self):
        super(quality_test, self).__init__()
        self.name = _("PEP-8 Test")
        self.note = _("""
PEP-8 Test , copyright of py files check, method can not call from loops
""")
        self.bool_installed_only = False
        self.bad_standard = 0
        self.good_standard = 0
        self.result_py = {}
        self.min_score = 40
        return None

    def run_test(self, cr, uid, module_path):
        list_files = os.listdir(module_path)
        for i in list_files:
                path = os.path.join(module_path, i)
                if os.path.isdir(path):
                    for j in os.listdir(path):
                        list_files.append(os.path.join(i, j))
        py_list = []
        for file_py in list_files:
            if file_py.split('.')[-1] == 'py' and not file_py.endswith('__init__.py') and not file_py.endswith('__openerp__.py'):
                file_path = os.path.join(module_path, file_py)
                py_list.append(file_path)

        open_files = map(lambda x: open(x, 'r'), py_list)
        if not py_list:
            self.error = True
            self.result = _("No python file found")
            return None

        #below functions check:

        #1. Imports should usually be on separate lines
        #2. Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants
        self.check_import(open_files)

        #1. there should be a one space after , : ;
        self.check_space(open_files)

        #1. Have all the .py files a copyright?
        self.check_licence(open_files)

        #1. Does the module avoid unecessary queries like when we put a browse into a loop?
        self.check_loop(open_files)

        #1.More than one space around an assignment (or other) operator to align it with another.
#        self.check_space_operator(open_files)

        #1. For sequences, (strings, lists, tuples), use the fact that empty sequences are false
        #for e.g : if seq: => good  & if len(seq): => not good
        self.check_len(open_files)

        #  below function checks
        #  1. Don't compare boolean values to True or False using == and !=
        self.check_boolean(open_files)

        self.score = self.good_standard and float(self.good_standard) / float(self.good_standard + self.bad_standard)
        if self.score*100 < self.min_score:
            self.message = 'Score is below than minimal score(%s%%)' % self.min_score
        self.result = self.get_result({ module_path: [int(self.score * 100)]})
        self.result_details += self.get_result_general(self.result_py)
        return None

    def check_import(self, open_files):
        for py in open_files:
            py.seek(0)
            class_or_def = False
            line_counter = 0
            file_name = py.name.split('/')[-1]
            while True:
                line_counter += 1
                line = py.readline()
                if not line: break
                if ((line.find('class') > -1) or (line.find('def') > -1)):
                    class_or_def = True
                import_found = line.find('import')
                comment_found = line.find('#')
                if comment_found == -1 and import_found != -1:
                    self.good_standard += 1
                    if (class_or_def):
                        self.bad_standard += 1
                        self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants']
                    if (line.find('from') < 0 and line.find(',') != -1):
                        self.bad_standard += 1
                        self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'Imports should usually be on separate lines']

    def check_licence(self, open_files):
        for py in open_files:
            py.seek(0)
            bad_position = False
            copyright_found = False
            gnu_found = False
            license_found = False
            gnu_website_found = False
            line_counter = 0
            file_name = py.name.split('/')[-1]
            while True:
                declaration = False
                flag = False
                line_counter += 1
                line = py.readline()
                if not line: break
                if ((line.find('class') > -1) or (line.find('def') > -1) or (line.find('import') > -1)):
                    bad_position = True
                comment_found = line.find('#')
                copyright_found = line.find('Copyright')
                gnu_found = line.find('GNU')
                license_found = line.find('License')
                gnu_website_found = line.find('www.gnu.org/licenses')
                if ((copyright_found > -1) or (gnu_found > -1) or (license_found > -1) or (gnu_website_found > -1)):
                    self.good_standard += 1
                    declaration = True
                    flag = True
                    break
                if (comment_found > -1) and bad_position and declaration:
                    self.bad_standard += 1
                    self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'Declaration of copyright must be at the top of file']
                    break
            if bad_position and (not flag):
                self.bad_standard += 1
                self.result_py[file_name] = [file_name, '--', 'File is not copyright']

    def check_loop(self, open_files):
        for py in open_files:
            py.seek(0)
            methods = ['browse', 'search', 'read', 'copy', 'unlink']
            place_for = 1000
            file_name = py.name.split('/')[-1]
            line_counter = 0
            counter = 0
            while True:
                line_counter += 1
                line = py.readline()
                if not line: break
                place_method = 0
                for i in line :
                    if (i == ' '):
                        place_method += 1
                    elif (i != ' '):
                        break
                    elif (place_method > 100):
                        break
                if (line.find('for') > -1):
                    place_for = place_method
                if (place_for < place_method):
                    counter += 1
                    for method in methods:
                        got = line.find(method)
                        if(got > -1):
                            self.bad_standard += 1
                            self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'puting method inside loop is not good']
            self.good_standard += counter

    def check_space(self, open_files):
        for py in open_files:
            py.seek(0)
            counter_line = 0
            file_name = py.name.split('/')[-1]
            counter = 0
            while True:
                counter_line += 1
                line = py.readline()
                if not line: break
                pos_comma = line.find(',')
                pos_semicolon = line.find(';')
                pos_colon = line.find(':')
                space_find = -1
                if (pos_comma != -1 or pos_semicolon != -1 or pos_colon != -1):
                    counter += 1
                    for i in line:
                        space_find += 1
                        if (i == ' '):
                            if ((space_find + 1) == pos_comma) or ((space_find + 1) == pos_semicolon) or ((space_find + 1) == pos_colon):
                                self.bad_standard += 1
                                self.result_py[file_name + str(counter_line)] = [file_name, counter_line, 'You should not have space before (: ; ,)']
            self.good_standard += counter #  to be check


    def check_space_operator(self, open_files):
        for py in open_files:
            py.seek(0)
            space_counter = 0
            eq_found = False
            operator_found = False
            line_counter = 0
            file_name = py.name.split('/')[-1]
            while True:
                line_counter += 1
                line = py.readline()
                if not line: break
                for counter in line:
                    if (counter == ' '):
                        space_counter += 1
                    else:
                        if (space_counter > 1):
                            if counter in ['=', '<', '>', '!', '+', '-', '*', '/', '^', '%'] or operator_found:
                                self.bad_standard += 1
                                self.result_py[file_name + str(line_counter)] = [file_name, line_counter, 'More than one space around an assignment (or other) operator to align it with another']
                        operator_found = False
                        space_counter = 0
                    if counter in ['=', '<', '>', '!', '+', '-', '*', '/', '^', '%']:
                        self.good_standard += 1
                        operator_found = True

    def check_len(self, open_files):
        for py in open_files:
            py.seek(0)
            line_counter = 0
            file_name = py.name.split('/')[-1]
            while True:
                line_counter += 1
                line = py.readline()
                if not line: break
                if (line.find('if') > -1) and (line.find('len(') > -1) and (line.find(')') > -1):
                    self.good_standard += 1
                    if (line.find(':') > -1) and not line.find('<') > -1 and not line.find('>') > -1 and not line.find('=') > -1 and not line.find('!') > -1 :
                        self.bad_standard += 1
                        self.result_py[file_name + str(line_counter)] = [file_name, line_counter, ' For sequences, (strings, lists, tuples), use the fact that empty sequences are false']


    def check_boolean(self, open_files):
        for py in open_files:
            py.seek(0)
            line_counter = 0
            file_name = py.name.split('/')[-1]
            while True:
                line_counter += 1
                line = py.readline()
                if not line: break
                if (line.find('if') > -1):
                    self.good_standard += 1
                    if ((line.find('==') > -1) or (line.find('!=') > -1)) and ((line.find('True') > -1) or (line.find('False') > -1)):
                        self.bad_standard += 1
                        self.result_py[file_name + str(line_counter)] = [file_name, line_counter, "Don't compare boolean values to True or False using == or !="]

    def get_result(self, dict_obj):
        header = ('{| border="1" cellspacing="0" cellpadding="5" align="left" \n! %-40s \n', [_('Result of pep8_test in %')])
        if not self.error:
            return self.format_table(header, data_list=dict_obj)
        return ""

    def get_result_general(self, dict_obj):
        str_html = '''<html><strong>Result</strong><head>%s</head><body><table class="tablestyle">'''%(self.get_style())
        header = ('<tr><th class="tdatastyle">%s</th><th class="tdatastyle">%s</th><th class="tdatastyle">%s</th></tr>', [_('Object Name'), _('Line number'), _('Suggestion')])
        if not self.error:
            res = str_html + self.format_html_table(header, data_list=dict_obj) + '</table></body></html>'
            res = res.replace('''<td''', '''<td class="tdatastyle" ''')
            return res
        return ""

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: