# Source code for bbarchivist.archivist

    #!/usr/bin/env python3

    import os  # filesystem read
    import shutil  # directory read/write
    import hashlib  # SHA-x, MD5
    import time  # time for downloader
    import math  # rounding of floats
    try:
        from . import bbconstants  # @UnusedImport
    except SystemError:
        import bbconstants  # @UnresolvedImport @Reimport
    try:
        from . import utilities  # @UnusedImport
    except SystemError:
        import utilities  # @UnresolvedImport @Reimport
    try:
        from . import barutils  # @UnusedImport
    except SystemError:
        import barutils  # @UnresolvedImport @Reimport
    try:
        from . import networkutils  # @UnusedImport
    except SystemError:
        import networkutils  # @UnresolvedImport @Reimport
    try:
        from . import loadergen  # @UnusedImport
    except SystemError:
        import loadergen  # @UnresolvedImport @Reimport
    try:
        from . import hashwrapper  # @UnusedImport
    except SystemError:
        import hashwrapper  # @UnresolvedImport @Reimport

    def do_magic(osversion, radioversion, softwareversion,
                 localdir, radios=True, compressed=True, deleted=True,
                 hashed=True, crc32=False, adler32=False,
                 sha1=True, sha224=False, sha256=False,
                 sha384=False, sha512=False, md5=True,
                 md4=False, ripemd160=False, whirlpool=False,
                 cappath="cap.exe", download=True, extract=True,
                 loaders=True, signed=True, compmethod="7z"):

        """
        Wrap around multi-autoloader creation code.
        Some combination of creating, downloading, hashing,
        compressing and moving autoloaders.

        :param osversion: OS version, 10.x.y.zzzz.
        :type osversion: str

        :param radioversion: Radio version, 10.x.y.zzzz.
        :type radioversion: str

        :param softwareversion: Software release, 10.x.y.zzzz.
        :type softwareversion: str

        :param localdir: Working directory. Required.
        :type localdir: str

        :param radios: Whether to create radio autoloaders. True by default.
        :type radios: bool

        :param compressed: Whether to compress files. True by default.
        :type compressed: bool

        :param deleted: Whether to delete uncompressed files. True by default.
        :type deleted: bool

        :param hashed: Whether to hash files. True by default.
        :type hashed: bool

        :param crc32: Whether to use CRC32. False by default.
        :type crc32: bool

        :param adler32: Whether to use Adler-32. False by default.
        :type adler32: bool

        :param sha1: Whether to use SHA-1. True by default.
        :type sha1: bool

        :param sha224: Whether to use SHA-224. False by default.
        :type sha224: bool

        :param sha256: Whether to use SHA-256. False by default.
        :type sha256: bool

        :param sha384: Whether to use SHA-384. False by default.
        :type sha384: bool

        :param sha512: Whether to use SHA-512. False by default.
        :type sha512: bool

        :param md5: Whether to use MD5. True by default.
        :type md5: bool

        :param md4: Whether to use MD4. False by default.
        :type md4: bool

        :param ripemd160: Whether to use RIPEMD160. False by default.
        :type ripemd160: bool

        :param whirlpool: Whether to use Whirlpool. False by default.
        :type whirlpool: bool

        :param cappath: Path to cap.exe. Default is local dir 'cap.exe'.
        :type cappath: str

        :param download: Whether to download bar files. True by default.
        :type download: bool

        :param extract: Whether to extract bar files. True by default.
        :type extract: bool

        :param loaders: Whether to create autoloaders. True by default.
        :type loaders: bool

        :param signed: Whether to delete signed files. True by default.
        :type signed: bool

        :param compmethod: Compression method. Default is "7z" with fallback "zip".
        :type compmethod: str
        """
        starttime = time.clock()
        version = bbconstants._version  # update as needed

        print("~~~ARCHIVIST VERSION", version + "~~~")
        print("OS VERSION:", osversion)
        print("RADIO VERSION:", radioversion)
        print("SOFTWARE VERSION:", softwareversion)

        if compmethod == "7z":
            print("\nCHECKING PRESENCE OF 7ZIP...")
            psz = utilities.prep_seven_zip()
            if psz:
                print("7ZIP OK")
                szexe = utilities.get_seven_zip()
            else:
                szexe = ""
                print("7ZIP NOT FOUND")
                cont = utilities.str2bool(input("CONTINUE? Y/N "))
                if cont:
                    print("FALLING BACK TO ZIP...")
                    compmethod = "zip"
                else:
                    print("\nEXITING...")
                    raise SystemExit  # bye bye
        else:
            szexe = ""

        # Hash software version
        swhash = hashlib.sha1(softwareversion.encode('utf-8'))
        hashedsoftwareversion = swhash.hexdigest()

        # Root of all urls
        baseurl = "http://cdn.fs.sl.blackberry.com/fs/qnx/production/"
        baseurl += hashedsoftwareversion

        # List of OS urls
        osurls = [baseurl + "/winchester.factory_sfi.desktop-" +
                  osversion + "-nto+armle-v7+signed.bar",
                  baseurl + "/qc8960.factory_sfi.desktop-" +
                  osversion + "-nto+armle-v7+signed.bar",
                  baseurl + "/qc8960.factory_sfi_hybrid_qc8x30.desktop-" +
                  osversion + "-nto+armle-v7+signed.bar",
                  baseurl + "/qc8960.factory_sfi_hybrid_qc8974.desktop-" +
                  osversion + "-nto+armle-v7+signed.bar"]

        # List of radio urls
        radiourls = [baseurl + "/m5730-" + radioversion +
                     "-nto+armle-v7+signed.bar",
                     baseurl + "/qc8960-" + radioversion +
                     "-nto+armle-v7+signed.bar",
                     baseurl + "/qc8960.omadm-" + radioversion +
                     "-nto+armle-v7+signed.bar",
                     baseurl + "/qc8960.wtr-" + radioversion +
                     "-nto+armle-v7+signed.bar",
                     baseurl + "/qc8960.wtr5-" + radioversion +
                     "-nto+armle-v7+signed.bar",
                     baseurl + "/qc8930.wtr5-" + radioversion +
                     "-nto+armle-v7+signed.bar",
                     baseurl + "/qc8974.wtr2-" + radioversion +
                     "-nto+armle-v7+signed.bar"]

        # Add URLs to dict, programmatically
        osdict = {}
        radiodict = {}
        for i in osurls:
            osdict[str(i)] = i
        for i in radiourls:
            radiodict[str(i)] = i

        # Check availability of software release
        print("\nCHECKING SOFTWARE RELEASE AVAILABILITY...")
        av = networkutils.availability(baseurl)
        if av:
            print("SOFTWARE RELEASE", softwareversion, "EXISTS")
        else:
            print("SOFTWARE RELEASE", softwareversion, "NOT FOUND")
            cont = utilities.str2bool(input("CONTINUE? Y/N "))
            if cont:
                pass
            else:
                print("\nEXITING...")
                raise SystemExit  # bye bye

        # Make dirs
        if not os.path.exists(localdir):
            os.makedirs(localdir)

        if not os.path.exists(os.path.join(localdir, 'bars')):
            os.mkdir(os.path.join(localdir, 'bars'))
        bardir = os.path.join(localdir, 'bars')
        if not os.path.exists(os.path.join(bardir, osversion)):
            os.mkdir(os.path.join(bardir, osversion))
        bardir_os = os.path.join(bardir, osversion)
        if not os.path.exists(os.path.join(bardir, radioversion)):
            os.mkdir(os.path.join(bardir, radioversion))
        bardir_radio = os.path.join(bardir, radioversion)

        if not os.path.exists(os.path.join(localdir, 'loaders')):
            os.mkdir(os.path.join(localdir, 'loaders'))
        loaderdir = os.path.join(localdir, 'loaders')
        if not os.path.exists(os.path.join(loaderdir, osversion)):
            os.mkdir(os.path.join(loaderdir, osversion))
        loaderdir_os = os.path.join(loaderdir, osversion)
        if not os.path.exists(os.path.join(loaderdir, radioversion)):
            os.mkdir(os.path.join(loaderdir, radioversion))
        loaderdir_radio = os.path.join(loaderdir, radioversion)

        if not os.path.exists(os.path.join(localdir, 'zipped')):
            os.mkdir(os.path.join(localdir, 'zipped'))
        zipdir = os.path.join(localdir, 'zipped')
        if not os.path.exists(os.path.join(zipdir, osversion)):
            os.mkdir(os.path.join(zipdir, osversion))
        zipdir_os = os.path.join(zipdir, osversion)
        if not os.path.exists(os.path.join(zipdir, radioversion)):
            os.mkdir(os.path.join(zipdir, radioversion))
        zipdir_radio = os.path.join(zipdir, radioversion)

        # Download files
        if download:
            print("\nBEGIN DOWNLOADING...")
            download_manager = networkutils.DownloadManager(radiodict, localdir, 5)
            download_manager.begin_downloads()
            download_manager.download_dict = osdict
            download_manager.begin_downloads()

        # Extract bar files
        if extract:
            print("\nEXTRACTING...")
            barutils.extract_bars(localdir)

        # Move bar files
        print("\nMOVING .bar FILES...")
        for files in os.listdir(localdir):
            if files.endswith(".bar"):
                print("MOVING: " + files)
                bardest_os = os.path.join(bardir_os, files)
                bardest_radio = os.path.join(bardir_radio, files)
                # even the fattest radio is less than 90MB
                if os.path.getsize(os.path.join(localdir, files)) > 90000000:
                    try:
                        shutil.move(os.path.join(localdir, files), bardir_os)
                    except shutil.Error:
                        os.remove(bardest_os)
                else:
                    try:
                        shutil.move(os.path.join(localdir, files), bardir_radio)
                    except shutil.Error:
                        os.remove(bardest_radio)

        # Create loaders
        if loaders:
            print("\nGENERATING LOADERS...")
            loadergen.generate_loaders(osversion,
                                       radioversion,
                                       radios,
                                       cappath,
                                       localdir)

        # Remove .signed files
        if signed:
            print("\nREMOVING .signed FILES...")
            for file in os.listdir(localdir):
                if os.path.join(localdir, file).endswith(".signed"):
                    print("REMOVING: " + file)
                    os.remove(os.path.join(localdir, file))

        # If compression = true, compress
        if compressed:
            print("\nCOMPRESSING...")
            barutils.compress(localdir, compmethod, szexe)
        else:
            pass

        # Move zipped/unzipped loaders
        print("\nMOVING...")
        barutils.move_loaders(localdir,
                              loaderdir_os, loaderdir_radio,
                              zipdir_os, zipdir_radio)

        # Get hashes (if specified)
        if hashed:
            print("\nHASHING LOADERS...")
            print(
                "ADLER32:",
                adler32,
                "CRC32:",
                crc32,
                "MD4:",
                md4,
                "\nMD5:",
                md5,
                "SHA1:",
                sha1,
                "SHA224:",
                sha224,
                "\nSHA256:",
                sha256,
                "SHA384:",
                sha384,
                "SHA512:",
                sha512,
                "\nRIPEMD160:",
                ripemd160,
                "WHIRLPOOL:",
                whirlpool,
                "\n")
            blocksize = 32 * 1024 * 1024
            if compressed:
                hashwrapper.verifier(
                    zipdir_os,
                    blocksize,
                    crc32,
                    adler32,
                    sha1,
                    sha224,
                    sha256,
                    sha384,
                    sha512,
                    md5,
                    md4,
                    ripemd160,
                    whirlpool)
                if radios:
                    hashwrapper.verifier(
                        zipdir_radio,
                        blocksize,
                        crc32,
                        adler32,
                        sha1,
                        sha224,
                        sha256,
                        sha384,
                        sha512,
                        md5,
                        md4,
                        ripemd160,
                        whirlpool)
            if not deleted:
                hashwrapper.verifier(
                    loaderdir_os,
                    blocksize,
                    crc32,
                    adler32,
                    sha1,
                    sha224,
                    sha256,
                    sha384,
                    sha512,
                    md5,
                    md4,
                    ripemd160,
                    whirlpool)
                if radios:
                    hashwrapper.verifier(
                        loaderdir_radio,
                        blocksize,
                        crc32,
                        adler32,
                        sha1,
                        sha224,
                        sha256,
                        sha384,
                        sha512,
                        md5,
                        md4,
                        ripemd160,
                        whirlpool)

        # Remove uncompressed loaders (if specified)
        if deleted:
            print("\nDELETING UNCOMPRESSED LOADERS...")
            shutil.rmtree(loaderdir)

        # Delete empty folders
        print("\nREMOVING EMPTY FOLDERS...")
        barutils.remove_empty_folders(localdir)

        print("\nFINISHED!")
        endtime = time.clock() - starttime
        endtime_proper = math.ceil(endtime * 100) / 100
        print("\nCompleted in " + str(endtime_proper) + " seconds\n")