import hashlib
import shutil
import webbrowser
from pathlib import Path
from itertools import chain

from invoke import task


# =============================================================================
# = Paths                                                                     =
# =============================================================================

urho_repo_dir = Path('Urho3D').resolve()
urho_native_build_dir = urho_repo_dir / 'build_native'
urho_emscripten_build_dir = urho_repo_dir / 'build_emscripten'

build_native_dir = Path('build_native').resolve()
build_emscripten_dir = Path('build_emscripten').resolve()


# =============================================================================
# = Cleaning                                                                  =
# =============================================================================

@task
def clean_all(ctx):
    ctx.run(f'rm -fr {urho_repo_dir}')
    ctx.run('git clean -dfX')


@task
def urho_clean_native(ctx):
    ctx.run(f'rm -fr {urho_native_build_dir}')


@task
def urho_clean_emscripten(ctx):
    ctx.run(f'rm -fr {urho_emscripten_build_dir}')


@task
def clean(ctx):
    ctx.run('git clean -dfX')


@task
def clean_native(ctx):
    ctx.run(f'rm -fr {build_native_dir}')


@task
def clean_emscripten(ctx):
    ctx.run(f'rm -fr {build_emscripten_dir}')



# =============================================================================
# = Setup                                                                     =
# =============================================================================

@task
def setup(ctx, skip_pacman_packages=False):
    if not skip_pacman_packages:
        install_pacman_packages(ctx)
    install_aur_packages(ctx)

    urho_clone(ctx)
    urho_cmake_native(ctx)
    urho_cmake_emscripten(ctx)
    urho_make_native(ctx)
    urho_make_emscripten(ctx)

    copy_bin(ctx)
    copy_cmake(ctx)
    cmake(ctx)
    build(ctx)


@task
def install_pacman_packages(ctx):
    ctx.sudo('pacman -S --needed cmake emscripten python')


@task
def install_aur_packages(ctx):
    if not ctx.run('pacman -Q google-chrome', hide=True, warn=True):
        ctx.run('yay -S google-chrome')


@task
def urho_clone(ctx):
    if not urho_repo_dir.exists():
        ctx.run('git clone https://github.com/urho3d/Urho3D.git')


@task
def urho_cmake_native(ctx):
    ctx.run(f'mkdir -p {urho_native_build_dir}')
    with ctx.cd(str(urho_repo_dir)):
        ctx.run(f'''
            ./script/cmake_generic.sh \
                {urho_native_build_dir} \
                -D URHO3D_SAMPLES=0
        ''')


@task
def urho_cmake_emscripten(ctx):
    ctx.run(f'mkdir -p {urho_emscripten_build_dir}')
    with ctx.cd(str(urho_repo_dir)):
        ctx.run(f'''
            ./script/cmake_emscripten.sh \
                {urho_emscripten_build_dir} \
                -D EMSCRIPTEN_ROOT_PATH=/usr/lib/emscripten \
                -D EMSCRIPTEN_WASM=1 \
                -D EMSCRIPTEN_AUTO_SHELL=0 \
                -D URHO3D_SAMPLES=0
        ''')


@task
def urho_make_native(ctx):
    with ctx.cd(str(urho_native_build_dir)):
        ctx.run('make')


@task
def urho_make_emscripten(ctx):
    with ctx.cd(str(urho_emscripten_build_dir)):
        ctx.run('make')


@task
def copy_cmake(ctx):
    with ctx.cd('Urho3D'):
        ctx.run('cp -rv CMake script ..')


@task
def copy_bin(ctx):
    spec = {
        'CoreData': '*',
        'Data': {
            'Models': [
                'Box.mdl',
            ],
        },
    }

    def make_paths(parent, spec):
        paths = []
        for name, child in spec.items():
            path = parent / name
            if child == '*':
                paths.append(path)
            elif isinstance(child, list):
                for grandchild in child:
                    if isinstance(grandchild, dict):
                        paths.extend(make_paths(path, grandchild))
                    else:
                        paths.append(path / grandchild)
            else:
                paths.extend(make_paths(path, child))
        return paths


    dst_path = Path('bin').resolve()

    with ctx.cd(str(urho_repo_dir / 'bin')):
        ctx.run(' '.join(map(str, [
            'rsync', '-aR', '--delete', '--progress',
            *make_paths(Path('.'), spec),
            dst_path,
        ])))


@task
def minify(ctx):
    bin_dir = Path('bin')
    it = chain(*(bin_dir.glob(f'**/*.{ext}') for ext in
                 ctx.run('minify -l | cut -f 1', hide=True).stdout.split()))
    for path in it:
        minified = ctx.run(f'minify {path}', hide=True).stdout
        with path.open('w') as file_:
            file_.write(minified)


@task
def cmake(ctx):
    cmake_native(ctx)
    cmake_emscripten(ctx)


@task
def cmake_native(ctx):
    ctx.run(f'mkdir -p {build_native_dir}')
    ctx.run(f'''
        ./script/cmake_generic.sh {build_native_dir} \
            -D URHO3D_HOME={urho_native_build_dir}
    ''')


@task
def cmake_emscripten(ctx):
    ctx.run(f'mkdir -p {build_emscripten_dir}')
    ctx.run(f'''
        ./script/cmake_emscripten.sh \
            {build_emscripten_dir} \
            -D URHO3D_HOME={urho_emscripten_build_dir} \
            -D EMSCRIPTEN_ROOT_PATH=/usr/lib/emscripten \
            -D EMSCRIPTEN_AUTO_SHELL=0 \
            -D EMSCRIPTEN_WASM=1
    ''')


# =============================================================================
# = Build                                                                     =
# =============================================================================

@task
def build(ctx):
    copy_assets(ctx)
    make_native(ctx)
    make_emscripten(ctx)
    write_index_html(ctx)


@task
def build_native(ctx):
    copy_assets(ctx)
    make_native(ctx)


@task
def build_emscripten(ctx):
    copy_assets(ctx)
    make_emscripten(ctx)
    write_index_html(ctx)


@task
def copy_assets(ctx):
    fds_assets_dir = 'bin/Data/fds'
    ctx.run(f'mkdir -p {fds_assets_dir}')
    ctx.run(f'cp -arv assets/exports/fds/* {fds_assets_dir}')


@task
def make_native(ctx):
    with ctx.cd(str(build_native_dir)):
        ctx.run('make')


@task
def make_emscripten(ctx):
    with ctx.cd(str(build_emscripten_dir)):
        ctx.run('make')


@task
def write_index_html(ctx):
    js_path = Path('build_emscripten/bin/bunion.js')
    cache_bust = hashlib.new('sha256', js_path.read_bytes()).hexdigest()

    index_html = Path('src/index.html') \
        .read_text() \
        .replace('@CACHE_BUST@', cache_bust)

    with open(build_emscripten_dir / 'bin/index.html', 'w') as output:
        output.write(index_html)


# =============================================================================
# = Run                                                                       =
# =============================================================================

@task
def run_native(ctx):
    ctx.run(str(build_native_dir / 'bin/bunion'))


@task
def serve(ctx):
    ctx.run('scripts/server')


@task
def browse(ctx):
    name = shutil.which('google-chrome-stable')
    if name is None:
        raise OSError('cannot find google-chrome-stable executable')
    browser = webbrowser.Chrome(name=name)
    browser.open('http://localhost:8000')


# =============================================================================
# = Archive                                                                   =
# =============================================================================

@task
def archive(ctx, output_dir):
    output_path = Path(output_dir, 'bunion.zip')
    ctx.run(f'git archive --format=zip -o {output_path} HEAD .')
    font_path = 'assets/exports/fds/0_CocogoosePro-Regular.ttf'
    ctx.run(f'zip -d {output_path} {font_path}')
