"""
Check that Cython extensions in setup.py files match those in bento.info.
"""
import os
import re
import sys


RE_CYTHON = re.compile("config.add_extension\(\s*['\"]([\S]+)['\"]")

BENTO_TEMPLATE = """
    Extension: {module_path}
        Sources:
            {dir_path}.pyx"""


def each_setup_in_pkg(top_dir):
    """Yield path and file object for each setup.py file"""
    for dir_path, dir_names, filenames in os.walk(top_dir):
        for fname in filenames:
            if fname == 'setup.py':
                with open(os.path.join(dir_path, 'setup.py')) as f:
                    yield dir_path, f


def each_cy_in_setup(top_dir):
    """Yield path for each cython extension package's setup file."""
    for dir_path, f in each_setup_in_pkg(top_dir):
        text = f.read()
        match = RE_CYTHON.findall(text)
        if match:
            for cy_file in match:
                # if cython files in different directory than setup.py
                if '.' in cy_file:
                    parts = cy_file.split('.')
                    cy_file = parts[-1]
                    # Don't overwrite dir_path for subsequent iterations.
                    path = os.path.join(dir_path, *parts[:-1])
                else:
                    path = dir_path
                full_path = os.path.join(path, cy_file)
                yield full_path


def each_cy_in_bento(bento_file='bento.info'):
    """Yield path for each cython extension in bento info file."""
    with open(bento_file) as f:
        for line in f:
            line = line.strip()
            if line.startswith('Extension:'):
                path = line.lstrip('Extension:').strip()
                yield path


def remove_common_extensions(cy_bento, cy_setup):
    # normalize so that cy_setup and cy_bento have the same separator
    cy_setup = set(ext.replace('/', '.') for ext in cy_setup)
    cy_setup_diff = cy_setup.difference(cy_bento)
    cy_setup_diff = set(ext.replace('.', '/') for ext in cy_setup_diff)
    cy_bento_diff = cy_bento.difference(cy_setup)
    return cy_bento_diff, cy_setup_diff


def print_results(cy_bento, cy_setup):
    def info(text):
        print('')
        print(text)
        print('-' * len(text))

    if not (cy_bento or cy_setup):
        print("bento.info and setup.py files match.")

    if cy_bento:
        info("Extensions found in 'bento.info' but not in any 'setup.py:")
        print('\n'.join(cy_bento))


    if cy_setup:
        info("Extensions found in a 'setup.py' but not in any 'bento.info:")
        print('\n'.join(cy_setup))
        info("Consider adding the following to the 'bento.info' Library:")
        for dir_path in cy_setup:
            module_path = dir_path.replace('/', '.')
            print(BENTO_TEMPLATE.format(module_path=module_path,
                                        dir_path=dir_path))


if __name__ == '__main__':
    # All cython extensions defined in 'setup.py' files.
    cy_setup = set(each_cy_in_setup('skimage'))

    # All cython extensions defined 'bento.info' file.
    cy_bento = set(each_cy_in_bento())

    cy_bento, cy_setup = remove_common_extensions(cy_bento, cy_setup)
    print_results(cy_bento, cy_setup)

    if cy_setup or cy_bento:
        sys.exit(1)
