from modulefinder import ModuleFinder
import os
import sys
import shutil
from shutil import copy
from pathlib import Path
import time
from textwrap import dedent
import platform

def build():
    project_folder = Path.cwd()
    project_name = project_folder.stem
    build_folder = Path(project_folder / f'build_{platform.system()}')
    build_folder.mkdir(exist_ok=True)

    ignore_folders = []
    ignore_filetypes = []

    compressed_textures = []
    compressed_textures_folder = Path(project_folder/'textures_compressed')
    if compressed_textures_folder.exists():
        compressed_textures = compressed_textures_folder.iterdir()

    def copytree(src, dst, symlinks=False, ignore_patterns=[], ignore_filetypes=[]):
        src = str(src)
        dst = str(dst)

        for item in os.listdir(src):
            if item in ignore_patterns:
                continue

            s = os.path.join(src, item)
            d = os.path.join(dst, item)
            if os.path.isdir(s):
                try:
                    ignore_pattern = shutil.ignore_patterns(*(ignore_patterns + [f'*{e}' for e in ignore_filetypes]))
                    shutil.copytree(s, d, symlinks, ignore_pattern)
                except Exception as e:
                    print(e)
            else:
                if Path(s).suffix in ignore_filetypes:
                    print('ignore filetype:', Path(s).suffix)
                    continue
                if Path(s).stem in compressed_textures:
                    continue
                shutil.copy2(s, d)



    include_modules = []

    python_dest = Path(build_folder / 'python')
    python_dlls_dest = Path(build_folder / 'python/DLLs')
    python_lib_dest = Path(build_folder / 'python/Lib')
    src_dest = Path(build_folder / 'src')
    build_engine = True
    build_game = True
    compile_to_pyc = True
    entry_point = 'main.py'

    for i, arg in enumerate(sys.argv):
        if arg == '--help':
            print(dedent('''
                package ursina application for windows10.
                provided with project folder path, creates a build folder where
                it copies python and project's dependent packages. requires a main.py file.
                copies game scripts and assets into 'build/src' folder.
                creates a .bat file to start the game.

                --ignore_folders=*      # add assets to ignore, for example: --ignore_folders=temp,unused
                --ignore_filetypes=*    # filetype to ignore, for example: --ignore_filetypes=.blend,.psd
                --name=''               # change project name
                --include_modules=*     # include extra modules like this: --include_modules=module_one,module_two,module_tree
                --overwrite             # don't ask to overwrite existing build, just overwrite
                --skip_engine
                --skip_game
                --compile_to_pyc=True/False

                Make sure to include any extra modules with --include_modules PIL,numpy for example.
                Any errors while the application is running will be logged in log.txt instead of the console.
                '''
                )
            )
            sys.exit()

        elif arg.startswith('--ignore_folders='):
            ignore_folders = arg.split('=')[1].split(',')
            print('ignoring folders:', ignore_folders)

        elif arg.startswith('--ignore_filetypes='):
            ignore_filetypes = arg.split('=')[1].split(',')
            print('ignoring filetypes:', ignore_filetypes)

        elif arg == '--name':
            project_name = sys.argv[i+1]

        elif arg.startswith('--include_modules='):
            include_modules = arg.split('=')[1].split(',')

        elif arg == '--skip_engine':
            build_engine = False
        elif arg == '--skip_game':
            build_game = False

        elif arg == '--compile_to_pyc=True':
            compile_to_pyc = True
        elif arg == '--compile_to_pyc=False':
            compile_to_pyc = False
        elif arg.startswith('--entry_point='):
            entry_point = arg.split('=')[1]


    if (build_engine and python_dest.exists() or (build_game and src_dest.exists())):
        if '--overwrite' not in sys.argv:
            for e in (python_dest, src_dest):
                msg = f'Folder {e} already exists. \nProceed to delete and overwrite?'
                overwrite = input("%s (y/N) " % msg).lower() == 'y'
                # if not overwrite:
                #     print('stopped building')
                #     exit()
                if e == python_dest:
                    build_engine = overwrite
                elif e == src_dest:
                    build_game = overwrite


    print('building project:', project_folder)
    start_time = time.time()

    if build_engine:
        if python_dest.exists():
            shutil.rmtree(str(python_dest))
        python_dest.mkdir()
        python_dlls_dest.mkdir()
        python_lib_dest.mkdir()


        # def copy_python():
        # copy files in python installation folder, but not the folders
        print('copying python')
        python_folder = Path(sys.executable).parent
        [copy(str(f), str(python_dest / f.name)) for f in python_folder.iterdir() if f.is_file()]


        # def copy_python_lib():
        print('copying python Lib files')
        [copy(str(f), str(python_lib_dest / f.name)) for f in Path(python_folder / 'Lib').iterdir() if f.is_file()]


        # def copy_always_included():
        print('copying always included modules and extra modules')
        include_modules = [f'Lib/site-packages/{e}' for e in include_modules]

        always_include = [
            'Lib/collections',
            'Lib/ctypes',
            'Lib/encodings',
            'Lib/importlib',
            'Lib/urllib',
            'Lib/logging',
            'Lib/xml',
            'Lib/re',
            'Lib/json',
            # 'Lib/site-packages/panda3d/',
            'Lib/site-packages/direct/',
            'Lib/site-packages/pyperclip/',
            'Lib/site-packages/PIL/',
            'Lib/site-packages/screeninfo/',
            'DLLs/libffi-7.dll',
            'DLLs/_ctypes.pyd',
            'DLLs',
            'Lib/site-packages/gltf',
            ]

        for path in always_include + include_modules:
            source = python_folder / path
            dest = python_dest / path
            print('copying always include:', source, '-->', dest)

            if source.is_file():
                dest.parent.mkdir(parents=True, exist_ok=True)
                copy(str(source), str(dest))
            elif source.is_dir():
                dest.mkdir(parents=True, exist_ok=True)
                copytree(source, dest)


        print('copying panda3d')
        (python_dest / 'Lib/site-packages/panda3d/').mkdir(parents=True, exist_ok=True)

        for f in (python_folder / 'Lib/site-packages/panda3d/').iterdir():
            if f.name in (
                'avcodec-55.dll',
                'fmodex64.dll',
                'egg.cp39-win_amd64.pyd',
                'libpandaode.dll',
                'ode.cp39-win_amd64.pyd',
                'models',
                'libp3tinydisplay.dll',
                'libp3assimp.dll',
                ):
                continue
            print('copying:', f, '-->', str(python_dest / 'Lib/site-packages/panda3d/' / f.name))
            if f.is_file():
                copy(str(f), str(python_dest / 'Lib/site-packages/panda3d/' / f.name))
            else:
                (python_dest / 'Lib/site-packages/panda3d/' / f.name).mkdir(parents=True, exist_ok=True)
                copytree(f, (python_dest / 'Lib/site-packages/panda3d/' / f.name))

        # def copy_ursina():
        print('copying ursina')
        import importlib
        spec = importlib.util.find_spec('ursina')
        ursina_path = Path(spec.origin).parent
        print('found ursina at:', ursina_path)
        dest = build_folder / 'python/Lib/site-packages/ursina'
        dest.mkdir(parents=True, exist_ok=True)
        copytree(ursina_path, dest, ignore_patterns=['samples', 'unused'], ignore_filetypes=['.blend', '.obj'])


        # print('copying found modules')
        # finder = ModuleFinder()
        # finder.run_script(str(project_folder) + '/main.py')

        # for name, mod in finder.modules.items():
        #     filename = mod.__file__
        #
        #     if filename is None:
        #         continue
        #     if '__' in name:
        #         print('ignore:', filename)
        #         continue
        #
        #     if 'Python' in filename and 'DLLs' in filename:
        #         print('copying:', filename)
        #         copy(filename, str(build_folder / 'python/DLLs'))
        #
        #     elif 'lib\\site-packages\\' in filename:
        #         print('copying module:', filename)
        #         forward_slashed = filename.split('lib\\site-packages\\')[1].replace('\\', '/')
        #         dir = build_folder / 'python/lib/site-packages' / forward_slashed
        #         dir.parent.mkdir(parents=True, exist_ok=True)
        #         copy(filename, dir)



    if build_game:
        if src_dest.exists():
            shutil.rmtree(str(src_dest))
        src_dest.mkdir()

        ignore_folders.extend(['build_Windows', 'build_Linux', 'build.bat', '__pycache__', '.git'])
        ignore_filetypes.extend(['.gitignore', '.psd', '.zip'])

        if compile_to_pyc:
            import py_compile
            for f in project_folder.glob('**/*.py'):
                in_ignore = False
                for e in ignore_folders:
                    if e in [str(e.name) for e  in f.parents]:
                        in_ignore = True
                        print('skip compiling:', f)
                        break
                if in_ignore:
                    continue

                parents = f.relative_to(project_folder).parents
                if 'scenes' in parents:
                    continue
                py_compile.compile(f, src_dest / (str(f)[len(str(project_folder))+1:]+'c'))

            ignore_filetypes.append('.py')


        print('copying assets')
        for f in project_folder.iterdir():
            name = f.name
            dest = Path(src_dest / f.name)
            if name in ignore_folders:
                print('ignore:', f)
                continue
            elif f.is_dir():
                print('copying assetfolder:', f, 'to', dest)
                dest.mkdir(parents=True, exist_ok=True)
                copytree(project_folder / f, dest, ignore_patterns=ignore_folders, ignore_filetypes=ignore_filetypes)
            elif f.is_file():
                if f.suffix in ignore_filetypes:
                    continue
                print('copying asset:', f, 'to', src_dest / f.name)
                copy(str(f), str(dest))


        print('creating .bat file')
        c = ''
        if compile:
            c = 'c'
        with Path(build_folder / f'{project_name}.bat').open('w') as f:
            f.write(dedent(fr'''
                chcp 65001
                set PYTHONIOENCODING=utf-8

                call "python\python.exe" "src\{entry_point}{c}" > "log.txt" 2>&1'''
            ))

    # make exe
    # import subprocess
    # import importlib
    # spec = importlib.util.find_spec('ursina')
    # ursina_path = Path(spec.origin).parent
    # subprocess.call([
    #     f'{ursina_path}\\scripts\\_bat_to_exe.bat',
    #     f'{build_folder}\\{project_name}.bat',
    #     f'\\build\\{project_folder.stem}.exe'
    #     ])


    print('build complete! time elapsed:', time.time() - start_time)


if __name__ == '__main__':
    build()