import os
import shutil
import inspect
import pkg_resources
 
from grpc_tools.protoc import main as protoc
 
POSIX_SEP = '/'
 
 
def paths_print(items):
    print("Paths:")
    print("-"*80)
    for k, v in items:
        print("\t", k, "\t\t", v)
    print("-"*80)
 
 
def clear_folder(output_dir):
    if os.path.exists(output_dir):
        shutil.rmtree(output_dir)
 
 
def generate_bindings(protos_dir, axxonsoft_protos_dir):
    proto_files_relative = get_proto_files_relpath(axxonsoft_protos_dir, protos_dir)
 
    protoc_keys = [
        f'-I{protos_dir}',
        '--python_out=.',
        '--grpc_python_out=.',
    ]
    protoc_args = protoc_keys + proto_files_relative
 
    is_protoc_patched = 'include_module_proto' in inspect.getfullargspec(protoc).args
    if not is_protoc_patched:
        protoc_args_patch_start = [inspect.getfile(protoc)]
        protoc_args_patch_end = [f'-I{pkg_resources.resource_filename("grpc_tools", "_proto")}']
    else:
        protoc_args_patch_start = protoc_args_patch_end = []
 
    print('Call of "protoc":')
    protoc_retcode = protoc(protoc_args_patch_start + protoc_args + protoc_args_patch_end)
    # print(f'\targs = {protoc_args}\n\tretcode = {protoc_retcode}\n')
    return protoc_retcode
 
 
def generate_init_py_files(bindings_output_dir):
 
    def make_init_py_subtree(base_path):
        # print('\tprocess {!r}'.format(base_path))
        make_init_py(base_path)
        for subdir in get_subdirs(base_path):
            make_init_py_subtree(subdir)
 
    def make_init_py(base_path):
        modules = get_py_modules(base_path)
        init_py_path = os.path.join(base_path, '__init__.py')
        with open(init_py_path, 'w') as init_py:
            init_py.write('# Generated AUTOMATICALLY by \"{}\".\n'.format(os.path.basename(__file__)))
            init_py.write('# DO NOT EDIT manually!\n\n')
            for m in modules:
                if '.Internal' not in m:
                    init_py.write('from . import {!s}\n'.format(m))
 
    def get_subdirs(base_path):
        _, subdirs, _ = next(os.walk(base_path))
        return [os.path.abspath(os.path.join(base_path, s)) for s in subdirs]
 
    def get_py_modules(base_path):
        _, subdirs, files = next(os.walk(base_path))
        file_modules = [f.rstrip('.py') for f in files if f.endswith('.py') and f != '__init__.py']
        return subdirs + file_modules
 
    # print('Generate "__init__.py" files:')
    make_init_py_subtree(bindings_output_dir)
 
 
def get_proto_files_relpath(axxonsoft_protos_dir, protos_dir):
    out = []
    for root, dirs, files in os.walk(axxonsoft_protos_dir):
        for file in files:
            if file.endswith(".proto") and ".Internal" not in file:
                full_path = os.path.abspath(os.path.join(root, file))
                rel_path = os.path.relpath(full_path, protos_dir)
                posix_rel_path = rel_path.replace(os.sep, POSIX_SEP)
                out.append(posix_rel_path)
    return out
 
 
def run_generate():
 
    protos_dir_name = 'grpc-proto-files'
    axxonsoft_dir_name = 'axxonsoft'
 
    current_dir = os.path.dirname(os.path.abspath(__file__))
    protos_dir = os.path.join(current_dir, protos_dir_name)
    axxonsoft_protos_dir = os.path.join(protos_dir, axxonsoft_dir_name)
    bindings_package_dir = os.path.join(current_dir, axxonsoft_dir_name)
    paths_print([
        ('protos_dir', protos_dir),
        ('axxonsoft_protos_dir', axxonsoft_protos_dir),
        ('bindings_package_dir', bindings_package_dir),
    ])
 
    clear_folder(bindings_package_dir)
    code = generate_bindings(protos_dir, axxonsoft_protos_dir)
    if code == 0:
        generate_init_py_files(bindings_package_dir)
 
    return code
 
 
if __name__ == '__main__':
    print('AxxonNext NativeBL bindings generator.')
    print('To generate that bindings you need to copy to `grpc-proto-files` folder: ')
    print('1) `axxonsoft` folder with AxxonSoft proto-files, ')
    print('2) `google` folder with Google common proto-files.')
    result = run_generate()
    if result == 0:
        print('Bindings generation was completed successfully')
    else:
        print(f'An error occurred while generating bindings: {result}')