#!/usr/bin/env python

# Copyright JS Foundation and other contributors, http://js.foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import print_function

import argparse
import multiprocessing
import subprocess
from os import path
from glob import glob
import sys
import re

from settings import PROJECT_DIR

RE_CLANG_FORMAT_VERSION = re.compile(
    r".*clang-format version (?P<version>\d{2})\.")
RE_DIRECTIVE_COMMENT = re.compile(r"^#\s*(else|endif)$", re.MULTILINE)
RE_FUNCTION_NAME_COMMENT = re.compile(
    r"^\}(?!(?:\s\/\*\s\w+\s\*\/$|\s?\w*;))", re.MULTILINE)

CLANG_FORMAT_MIN_VERSION = 10

FOLDERS = ["jerry-core",
           "jerry-ext",
           "jerry-port",
           "jerry-math",
           "jerry-main",
           "tests/unit-core",
           "tests/unit-ext",
           "tests/unit-math"]


def get_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument('--fix', action='store_true', dest='fix',
                        help='fix source code stlye')
    parser.add_argument('--clang-format', dest='clang_format', default='clang-format-%d' % CLANG_FORMAT_MIN_VERSION,
                        help='path to clang-format executable')

    script_args = parser.parse_args()
    return script_args


def check_clang_format(args, source_file_name):
    cmd = [args.clang_format]

    if args.fix:
        cmd.append('-i')
    else:
        cmd.extend(['--dry-run', '--Werror'])

    cmd.append(source_file_name)

    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    _, error = proc.communicate()

    if proc.returncode == 0:
        return 0

    print(error.decode('utf8'))

    return 1


def check_comments(regexps, source_file_name):
    failed = 0

    with open(source_file_name, encoding='utf-8') as source_file:
        data = source_file.read()

        for regexp in regexps:
            for match in re.finditer(regexp, data):
                failed += 1
                print("Invalid/Missing comment in %s:%d" %
                      (source_file_name, data.count('\n', 0, match.start()) + 1))

    return 1 if failed else 0


def run_pass(pool, process, process_args):
    return sum(pool.starmap(process, process_args))


def check_clang_format_version(args):
    try:
        out = subprocess.check_output([args.clang_format, '--version'])
        match = RE_CLANG_FORMAT_VERSION.match(out.decode('utf8'))

        if match and int(match.group('version')) >= CLANG_FORMAT_MIN_VERSION:
            return 0
    except OSError:
        pass

    return 1


def main(args):
    if check_clang_format_version(args) != 0:
        print("clang-format >= %d is not installed." %
              CLANG_FORMAT_MIN_VERSION)
        return 1

    pool = multiprocessing.Pool()
    failed = 0

    for folder in FOLDERS:
        # pylint: disable=unexpected-keyword-arg
        files = sum(([glob(path.join(PROJECT_DIR, folder, "**/*.%s" % e), recursive=True)
                      for e in ['c', 'h']]), [])

        failed += run_pass(pool, check_clang_format,
                           [(args, sourece_file) for sourece_file in files])
        failed += run_pass(pool, check_comments,
                           [([RE_DIRECTIVE_COMMENT, RE_FUNCTION_NAME_COMMENT], sourece_file) for sourece_file in files])

    pool.close()

    return 1 if failed else 0


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