# Copyright 2017 Virgil Dupras
#
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html

import sys
import os
import os.path as op
import compileall
import shutil
import json
from argparse import ArgumentParser
import platform
import distro
import re

from hscommon.build import (
    print_and_do,
    copy_packages,
    build_debian_changelog,
    get_module_version,
    filereplace,
    copy,
    setup_package_argparser,
    copy_all,
)

ENTRY_SCRIPT = "run.py"
LOCALE_DIR = "build/locale"
HELP_DIR = "build/help"


def parse_args():
    parser = ArgumentParser()
    setup_package_argparser(parser)
    return parser.parse_args()


def check_loc_doc():
    if not op.exists(LOCALE_DIR):
        print('Locale files are missing. Have you run "build.py --loc"?')
    # include help files if they are built otherwise exit as they should be included?
    if not op.exists(HELP_DIR):
        print('Help files are missing. Have you run "build.py --doc"?')
    return op.exists(LOCALE_DIR) and op.exists(HELP_DIR)


def copy_files_to_package(destpath, packages, with_so):
    # when with_so is true, we keep .so files in the package, and otherwise, we don't. We need this
    # flag because when building debian src pkg, we *don't* want .so files (they're compiled later)
    # and when we're packaging under Arch, we're packaging a binary package, so we want them.
    if op.exists(destpath):
        shutil.rmtree(destpath)
    os.makedirs(destpath)
    shutil.copy(ENTRY_SCRIPT, op.join(destpath, ENTRY_SCRIPT))
    extra_ignores = ["*.so"] if not with_so else None
    copy_packages(packages, destpath, extra_ignores=extra_ignores)
    # include locale files if they are built otherwise exit as it will break
    # the localization
    if not check_loc_doc():
        print("Exiting...")
        return
    shutil.copytree(op.join("build", "help"), op.join(destpath, "help"))
    shutil.copytree(op.join("build", "locale"), op.join(destpath, "locale"))
    compileall.compile_dir(destpath)


def package_debian_distribution(distribution):
    app_version = get_module_version("core")
    version = "{}~{}".format(app_version, distribution)
    destpath = op.join("build", "dupeguru-{}".format(version))
    srcpath = op.join(destpath, "src")
    packages = ["hscommon", "core", "qt", "send2trash"]
    copy_files_to_package(srcpath, packages, with_so=False)
    os.mkdir(op.join(destpath, "modules"))
    copy_all(op.join("core", "pe", "modules", "*.*"), op.join(destpath, "modules"))
    copy(
        op.join("qt", "pe", "modules", "block.c"),
        op.join(destpath, "modules", "block_qt.c"),
    )
    copy(
        op.join("pkg", "debian", "build_pe_modules.py"),
        op.join(destpath, "build_pe_modules.py"),
    )
    debdest = op.join(destpath, "debian")
    debskel = op.join("pkg", "debian")
    os.makedirs(debdest)
    debopts = json.load(open(op.join(debskel, "dupeguru.json")))
    for fn in ["compat", "copyright", "dirs", "rules", "source"]:
        copy(op.join(debskel, fn), op.join(debdest, fn))
    filereplace(op.join(debskel, "control"), op.join(debdest, "control"), **debopts)
    filereplace(op.join(debskel, "Makefile"), op.join(destpath, "Makefile"), **debopts)
    filereplace(op.join(debskel, "dupeguru.desktop"), op.join(debdest, "dupeguru.desktop"), **debopts)
    changelogpath = op.join("help", "changelog")
    changelog_dest = op.join(debdest, "changelog")
    project_name = debopts["pkgname"]
    from_version = "2.9.2"
    build_debian_changelog(
        changelogpath,
        changelog_dest,
        project_name,
        from_version=from_version,
        distribution=distribution,
    )
    shutil.copy(op.join("images", "dgse_logo_128.png"), srcpath)
    os.chdir(destpath)
    cmd = "dpkg-buildpackage -F -us -uc"
    os.system(cmd)
    os.chdir("../..")


def package_debian():
    print("Packaging for Debian/Ubuntu")
    for distribution in ["unstable"]:
        package_debian_distribution(distribution)


def package_arch():
    # For now, package_arch() will only copy the source files into build/. It copies less packages
    # than package_debian because there are more python packages available in Arch (so we don't
    # need to include them).
    print("Packaging for Arch")
    srcpath = op.join("build", "dupeguru-arch")
    packages = ["hscommon", "core", "qt", "send2trash"]
    copy_files_to_package(srcpath, packages, with_so=True)
    shutil.copy(op.join("images", "dgse_logo_128.png"), srcpath)
    debopts = json.load(open(op.join("pkg", "arch", "dupeguru.json")))
    filereplace(op.join("pkg", "arch", "dupeguru.desktop"), op.join(srcpath, "dupeguru.desktop"), **debopts)


def package_source_txz():
    print("Creating git archive")
    app_version = get_module_version("core")
    name = "dupeguru-src-{}.tar".format(app_version)
    base_path = os.getcwd()
    build_path = op.join(base_path, "build")
    dest = op.join(build_path, name)
    print_and_do("git archive -o {} HEAD".format(dest))
    print_and_do("xz {}".format(dest))


def package_windows():
    app_version = get_module_version("core")
    arch = platform.architecture()[0]
    # Information to pass to pyinstaller and NSIS
    match = re.search("[0-9]+.[0-9]+.[0-9]+", app_version)
    version_array = match.group(0).split(".")
    match = re.search("[0-9]+", arch)
    bits = match.group(0)
    if bits == "64":
        arch = "x64"
    else:
        arch = "x86"
    # include locale files if they are built otherwise exit as it will break
    # the localization
    if not check_loc_doc():
        print("Exiting...")
        return
    # create version information file from template
    try:
        version_template = open("win_version_info.temp", "r")
        version_info = version_template.read()
        version_template.close()
        version_info_file = open("win_version_info.txt", "w")
        version_info_file.write(version_info.format(version_array[0], version_array[1], version_array[2], bits))
        version_info_file.close()
    except Exception:
        print("Error creating version info file, exiting...")
        return
    # run pyinstaller from here:
    import PyInstaller.__main__

    # UCRT dlls are included if the system has the windows kit installed
    PyInstaller.__main__.run(
        [
            "--name=dupeguru-win{0}".format(bits),
            "--windowed",
            "--noconfirm",
            "--icon=images/dgse_logo.ico",
            "--add-data={0};locale".format(LOCALE_DIR),
            "--add-data={0};help".format(HELP_DIR),
            "--version-file=win_version_info.txt",
            "--paths=C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\ucrt\\DLLs\\{0}".format(arch),
            ENTRY_SCRIPT,
        ]
    )
    # remove version info file
    os.remove("win_version_info.txt")
    # Call NSIS (TODO update to not use hardcoded path)
    cmd = (
        '"C:\\Program Files (x86)\\NSIS\\Bin\\makensis.exe" '
        "/DVERSIONMAJOR={0} /DVERSIONMINOR={1} /DVERSIONPATCH={2} /DBITS={3} setup.nsi"
    )
    print_and_do(cmd.format(version_array[0], version_array[1], version_array[2], bits))


def package_macos():
    # include locale files if they are built otherwise exit as it will break
    # the localization
    if not check_loc_doc():
        print("Exiting")
        return
    # run pyinstaller from here:
    import PyInstaller.__main__

    PyInstaller.__main__.run(
        [
            "--name=dupeguru",
            "--windowed",
            "--noconfirm",
            "--icon=images/dupeguru.icns",
            "--osx-bundle-identifier=com.hardcoded-software.dupeguru",
            "--add-data={0}:locale".format(LOCALE_DIR),
            "--add-data={0}:help".format(HELP_DIR),
            "{0}".format(ENTRY_SCRIPT),
        ]
    )


def main():
    args = parse_args()
    if args.src_pkg:
        print("Creating source package for dupeGuru")
        package_source_txz()
        return
    print("Packaging dupeGuru with UI qt")
    if sys.platform == "win32":
        package_windows()
    elif sys.platform == "darwin":
        package_macos()
    else:
        if not args.arch_pkg:
            distname = distro.id()
        else:
            distname = "arch"
        if distname == "arch":
            package_arch()
        else:
            package_debian()


if __name__ == "__main__":
    main()
