'''
Helper module to do refactoring to convert names to pep8.
'''
import re
import os
import names_to_rename

_CAMEL_RE = re.compile(r'(?<=[a-z])([A-Z])')
_CAMEL_DEF_RE = re.compile(r'(def )((([A-Z0-9]+|[a-z0-9])[a-z][a-z0-9]*[A-Z]|[a-z0-9]*[A-Z][A-Z0-9]*[a-z])[A-Za-z0-9]*)')

def _normalize(name):
    return _CAMEL_RE.sub(lambda x: '_' + x.group(1).lower(), name).lower()

def find_matches_in_contents(contents):
    return [x[1] for x in re.findall(_CAMEL_DEF_RE, contents)]

def iter_files_in_dir(dirname):
    for root, dirs, files in os.walk(dirname):
        for name in ('pydevd_attach_to_process', '.git', 'stubs', 'pydev_ipython', 'third_party', 'pydev_ipython'):
            try:
                dirs.remove(name)
            except:
                pass
        for filename in files:
            if filename.endswith('.py') and filename not in ('rename_pep8.py', 'names_to_rename.py'):
                path = os.path.join(root, filename)
                with open(path, 'rb') as stream:
                    initial_contents = stream.read()

                yield path, initial_contents

def find_matches():
    found = set()
    for path, initial_contents in iter_files_in_dir(os.path.dirname(os.path.dirname(__file__))):
        found.update(find_matches_in_contents(initial_contents))
    print '\n'.join(sorted(found))
    print 'Total', len(found)

def substitute_contents(re_name_to_new_val, initial_contents):
    contents = initial_contents
    for key, val in re_name_to_new_val.iteritems():
        contents = re.sub(key, val, contents)
    return contents

def make_replace():
    re_name_to_new_val = load_re_to_new_val(names_to_rename.NAMES)
    # traverse root directory, and list directories as dirs and files as files
    for path, initial_contents in iter_files_in_dir(os.path.dirname(os.path.dirname(__file__))):
        contents = substitute_contents(re_name_to_new_val, initial_contents)
        if contents != initial_contents:
            print 'Changed something at: %s' % (path,)

            for val in re_name_to_new_val.itervalues():
                # Check in initial contents to see if it already existed!
                if re.findall(r'\b%s\b' % (val,), initial_contents):
                    raise AssertionError('Error in:\n%s\n%s is already being used (and changes may conflict).' % (path, val,))

            with open(path, 'wb') as stream:
                stream.write(contents)


def load_re_to_new_val(names):
    name_to_new_val = {}
    for n in names.splitlines():
        n = n.strip()
        if not n.startswith('#') and n:
            name_to_new_val[r'\b'+n+r'\b'] = _normalize(n)
    return name_to_new_val

def test():
    assert _normalize('RestoreSysSetTraceFunc') == 'restore_sys_set_trace_func'
    assert _normalize('restoreSysSetTraceFunc') == 'restore_sys_set_trace_func'
    assert _normalize('Restore') == 'restore'
    matches = find_matches_in_contents('''
    def CamelCase()
    def camelCase()
    def ignore()
    def ignore_this()
    def Camel()
    def CamelCaseAnother()
    ''')
    assert matches == ['CamelCase', 'camelCase', 'Camel', 'CamelCaseAnother']
    re_name_to_new_val = load_re_to_new_val('''
# Call -- skip
# Call1 -- skip
# Call2 -- skip
# Call3 -- skip
# Call4 -- skip
CustomFramesContainerInit
DictContains
DictItems
DictIterItems
DictIterValues
DictKeys
DictPop
DictValues
''')
    assert re_name_to_new_val == {'\\bDictPop\\b': 'dict_pop', '\\bDictItems\\b': 'dict_items', '\\bDictIterValues\\b': 'dict_iter_values', '\\bDictKeys\\b': 'dict_keys', '\\bDictContains\\b': 'dict_contains', '\\bDictIterItems\\b': 'dict_iter_items', '\\bCustomFramesContainerInit\\b': 'custom_frames_container_init', '\\bDictValues\\b': 'dict_values'}
    assert substitute_contents(re_name_to_new_val, '''
CustomFramesContainerInit
DictContains
DictItems
DictIterItems
DictIterValues
DictKeys
DictPop
DictValues
''') == '''
custom_frames_container_init
dict_contains
dict_items
dict_iter_items
dict_iter_values
dict_keys
dict_pop
dict_values
'''

if __name__ == '__main__':
#     find_matches()
    make_replace()
#     test()

