#!%{__python3}
#
# Copyright (C) 2017 Dell, Inc.
#
# SPDX-License-Identifier: LGPL-2.1+
#
from base64 import b64encode
import io
import os
import subprocess
import shutil
import sys
import tempfile
import zipfile
from assets.header import TAG


def error(msg):
    print(msg)
    sys.exit(1)


def parse_args():
    import argparse

    parser = argparse.ArgumentParser(
        description="Generate a standalone firmware updater"
    )
    parser.add_argument(
        "--disable-snap-download",
        action='store_true',
        help="Don't download support for snap",
    )
    parser.add_argument(
        "--disable-flatpak-download",
        action='store_true',
        help="Don't download support for flatpak",
    )
    parser.add_argument(
        "--snap-channel", help="Channel to download snap from (optional)"
    )
    parser.add_argument(
        "--minimum", help="Use already installed fwupd version if at least this version"
    )
    parser.add_argument(
        "cab",
        help="CAB file or directory containing CAB files to automatically install",
    )
    parser.add_argument('target', help='target file to create')
    args = parser.parse_args()
    return args


def bytes_slicer(length, source):
    start = 0
    stop = length
    while start < len(source):
        yield source[start:stop]
        start = stop
        stop += length


def generate_installer(directory, target):
    asset_base = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets")

    # header
    shutil.copy(os.path.join(asset_base, "header.py"), target)

    # zip file
    buffer = io.BytesIO()
    archive = zipfile.ZipFile(buffer, "a")
    for root, dirs, files in os.walk(directory):
        for f in files:
            source = os.path.join(root, f)
            archive_fname = source.split(directory)[1]
            archive.write(source, archive_fname)
    if 'DEBUG' in os.environ:
        print(archive.namelist())
    archive.close()

    with open(target, 'ab') as bytes_out:
        encoded = b64encode(buffer.getvalue())
        for section in bytes_slicer(64, encoded):
            bytes_out.write(TAG)
            bytes_out.write(section)
            bytes_out.write(b'\n')


def download_snap(directory, channel):
    cmd = ['snap', 'download', 'fwupd']
    if channel is not None:
        cmd += ['--channel', channel]
    if 'DEBUG' in os.environ:
        print(cmd)
    subprocess.run(cmd, cwd=directory, check=True)
    for f in os.listdir(directory):
        # the signatures associated with the snap
        if f.endswith(".assert"):
            shutil.move(
                os.path.join(directory, f), os.path.join(directory, 'fwupd.assert')
            )
        # the snap binary itself
        elif f.endswith(".snap"):
            shutil.move(
                os.path.join(directory, f), os.path.join(directory, 'fwupd.snap')
            )


def download_cab_file(directory, uri):
    cmd = ['wget', uri]
    if 'DEBUG' in os.environ:
        print(cmd)
    subprocess.run(cmd, cwd=directory, check=True)


def download_flatpak(directory):
    dep = 'org.freedesktop.fwupd'
    flatpak_dir = os.path.join(os.getenv('HOME'), '.local', 'share', 'flatpak')
    verbose = 'DEBUG' in os.environ

    # check if we have installed locally already or not
    if not os.path.exists(os.path.join(flatpak_dir, 'app', dep)):
        # install into local user's repo
        cmd = [
            'flatpak',
            'install',
            '--user',
            'https://www.flathub.org/repo/appstream/org.freedesktop.fwupd.flatpakref',
            '--no-deps',
            '-y',
        ]
        if verbose:
            print(cmd)
        subprocess.run(cmd, cwd=directory, check=True)

    # generate a bundle
    repo = os.path.join(flatpak_dir, 'repo')
    cmd = ['flatpak', 'build-bundle', repo, 'fwupd.flatpak', dep, 'stable']
    if verbose:
        print(cmd)
    subprocess.run(cmd, cwd=directory, check=True)


if __name__ == '__main__':
    args = parse_args()

    if not args.cab.startswith("http"):
        local = args.cab

    with tempfile.TemporaryDirectory(prefix='fwupd') as directory:
        if local:
            if not os.path.exists(local):
                error("%s doesn't exist" % local)
            if not os.path.isdir(local):
                shutil.copy(local, directory)
            else:
                for root, dirs, files in os.walk(local):
                    for f in files:
                        shutil.copy(os.path.join(root, f), directory)
        else:
            download_cab_file(directory, args.cab)

        if not args.disable_snap_download:
            download_snap(directory, args.snap_channel)

        if not args.disable_flatpak_download:
            download_flatpak(directory)

        if args.minimum:
            with open(os.path.join(directory, "minimum"), "w") as wfd:
                wfd.write(args.minimum)

        generate_installer(directory, args.target)
