'''
This module should be run to recreate the files that we generate automatically
(i.e.: modules that shouldn't be traced and cython .pyx)
'''

from __future__ import print_function

import os
import struct
import re


def is_python_64bit():
    return (struct.calcsize('P') == 8)


root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))


def get_cython_contents(filename):
    if filename.endswith('.pyc'):
        filename = filename[:-1]

    state = 'regular'

    replacements = []

    new_contents = []
    with open(filename, 'r') as stream:
        for line in stream:
            strip = line.strip()
            if state == 'regular':
                if strip == '# IFDEF CYTHON':
                    state = 'cython'

                    new_contents.append('%s -- DONT EDIT THIS FILE (it is automatically generated)\n' % line.replace('\n', '').replace('\r', ''))
                    continue

                new_contents.append(line)

            elif state == 'cython':
                if strip == '# ELSE':
                    state = 'nocython'
                    new_contents.append(line)
                    continue

                elif strip == '# ENDIF':
                    state = 'regular'
                    new_contents.append(line)
                    continue

                if strip == '#':
                    continue

                assert strip.startswith('# '), 'Line inside # IFDEF CYTHON must start with "# ". Found: %s' % (strip,)
                strip = strip.replace('# ', '', 1).strip()

                if strip.startswith('cython_inline_constant:'):
                    strip = strip.replace('cython_inline_constant:', '')
                    word_to_replace, replacement = strip.split('=')
                    replacements.append((word_to_replace.strip(), replacement.strip()))
                    continue

                line = line.replace('# ', '', 1)
                new_contents.append(line)

            elif state == 'nocython':
                if strip == '# ENDIF':
                    state = 'regular'
                    new_contents.append(line)
                    continue
                new_contents.append('# %s' % line)

    assert state == 'regular', 'Error: # IFDEF CYTHON found without # ENDIF'

    ret = ''.join(new_contents)

    for (word_to_replace, replacement) in replacements:
        ret = re.sub(r"\b%s\b" % (word_to_replace,), replacement, ret)

    return ret


def _generate_cython_from_files(target, modules):
    contents = ['''from __future__ import print_function

# Important: Autogenerated file.

# DO NOT edit manually!
# DO NOT edit manually!
''']

    found = []
    for mod in modules:
        found.append(mod.__file__)
        contents.append(get_cython_contents(mod.__file__))

    print('Generating cython from: %s' % (found,))

    with open(target, 'w') as stream:
        stream.write(''.join(contents))


def generate_dont_trace_files():
    template = '''# Important: Autogenerated file.

# DO NOT edit manually!
# DO NOT edit manually!

LIB_FILE = 1
PYDEV_FILE = 2

DONT_TRACE_DIRS = {
%(pydev_dirs)s
}

DONT_TRACE = {
    # commonly used things from the stdlib that we don't want to trace
    'Queue.py':LIB_FILE,
    'queue.py':LIB_FILE,
    'socket.py':LIB_FILE,
    'weakref.py':LIB_FILE,
    '_weakrefset.py':LIB_FILE,
    'linecache.py':LIB_FILE,
    'threading.py':LIB_FILE,
    'dis.py':LIB_FILE,

    # things from pydev that we don't want to trace
%(pydev_files)s
}

# if we try to trace io.py it seems it can get halted (see http://bugs.python.org/issue4716)
DONT_TRACE['io.py'] = LIB_FILE

# Don't trace common encodings too
DONT_TRACE['cp1252.py'] = LIB_FILE
DONT_TRACE['utf_8.py'] = LIB_FILE
DONT_TRACE['codecs.py'] = LIB_FILE
'''

    pydev_files = []
    pydev_dirs = []

    exclude_dirs = [
        '.git',
        '.settings',
        'build',
        'build_tools',
        'dist',
        'pydevd.egg-info',
        'pydevd_attach_to_process',
        'pydev_sitecustomize',
        'stubs',
        'tests',
        'tests_mainloop',
        'tests_python',
        'tests_runfiles',
        'test_pydevd_reload',
        'third_party',
        '__pycache__',
        'pydev_ipython',
        'vendored',
        '.mypy_cache',
        'pydevd.egg-info',
    ]

    for root, dirs, files in os.walk(root_dir):

        for d in dirs:
            if 'pydev' in d and d != 'pydevd.egg-info':
                # print(os.path.join(root, d))
                pydev_dirs.append("    '%s': PYDEV_FILE," % (d,))

        for d in exclude_dirs:
            try:
                dirs.remove(d)
            except:
                pass

        for f in files:
            if f.endswith('.py'):
                if f not in (
                    '__init__.py',
                    'runfiles.py',
                    'pydev_coverage.py',
                    'pydev_pysrc.py',
                    'setup.py',
                    'setup_pydevd_cython.py',
                    'interpreterInfo.py',
                    'conftest.py',
                    ):
                    pydev_files.append("    '%s': PYDEV_FILE," % (f,))

    contents = template % (dict(
        pydev_files='\n'.join(sorted(set(pydev_files))),
        pydev_dirs='\n'.join(sorted(set(pydev_dirs))),
    ))
    assert 'pydevd.py' in contents
    assert 'pydevd_dont_trace.py' in contents
    with open(os.path.join(root_dir, '_pydevd_bundle', 'pydevd_dont_trace_files.py'), 'w') as stream:
        stream.write(contents)


def remove_if_exists(f):
    try:
        if os.path.exists(f):
            os.remove(f)
    except:
        import traceback;traceback.print_exc()


def generate_cython_module():
    print('Removing pydevd_cython.pyx')
    remove_if_exists(os.path.join(root_dir, '_pydevd_bundle', 'pydevd_cython.pyx'))

    target = os.path.join(root_dir, '_pydevd_bundle', 'pydevd_cython.pyx')
    curr = os.environ.get('PYDEVD_USE_CYTHON')
    try:
        os.environ['PYDEVD_USE_CYTHON'] = 'NO'

        from _pydevd_bundle import pydevd_additional_thread_info_regular
        from _pydevd_bundle import pydevd_frame, pydevd_trace_dispatch_regular
        _generate_cython_from_files(target, [pydevd_additional_thread_info_regular, pydevd_frame, pydevd_trace_dispatch_regular])
    finally:
        if curr is None:
            del os.environ['PYDEVD_USE_CYTHON']
        else:
            os.environ['PYDEVD_USE_CYTHON'] = curr


if __name__ == '__main__':
    generate_dont_trace_files()
    generate_cython_module()
