#!/usr/bin/env python3
#
# Copyright 2017 Dell, Inc.
#
# SPDX-License-Identifier: LGPL-2.1-or-later
#
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(f"{local} doesn't exist")
            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)
