#!/usr/bin/env python3

import os
import argparse
import re
import shutil
import subprocess
import copy
import macBundleCreation as mbc

workingDir = os.getcwd()
print("working directory:" + workingDir + "\n");

if not os.path.exists("CMakeCache.txt"):
    print('''No CMakeCache.txt file found. Please change directory to the
    CMAKE_BINARY_DIRECTORY''')
    exit(1);

parser = argparse.ArgumentParser()

parser.add_argument("--bundleDir", nargs=1, help='''Relative directory of the
        <filename>.app bundle (like minexpert/mineXpert.app)''');

parser.add_argument("--allBundles", action='store_true',
help='''Handle both bundles massxpert/massXpert.app and minexpert/mineXpert.app''');

args = parser.parse_args()

# bundleDirs is gotten as a string list, even if it contains a single item.
bundleDirs = args.bundleDir;
allBundles = args.allBundles;

bundleList = [ ];

if allBundles:
    print("allBundles");
    bundleList.append("massxpert/massXpert.app");
    bundleList.append("minexpert/mineXpert.app");
else:
    if not bundleDirs:
        print('''Please, provide the mac os bundle directory name as a relative path name
                or specify --allBundles to build all the bundles\n''');
        exit(1);
    else:
        if not os.path.exists(bundleDirs[0]):
            print("MacOSX bundle " + bundleDirs[0] + ''' not found. Please provide a
            correct path\n''');
            exit(1);

        bundleList.append(bundleDirs[0]);

print("Bundles to be processed:\n" +
        "\n".join(bundleList) + "\n");
answer = input("Continue ? (Return | Ctrl-C)\n");

# At this point we know what is/are the bundles. Now we need to process each
# bundle in sequence:

# Set apart a dictionary that we will need throughout all work, to store the
# various directories needed.
dirDic = {};

for bundleDirRelPath in bundleList:
    
    # Immediately set the development directory, that will be needed later.

    devDirName = "/Users/rusconi/devel/msxpertsuite/development";
    dirDic["devDirName"] = devDirName;

    # We need to craft the name of the program that we'll have to copy in the
    # bundleDir/Contents/MacOS directory.
    
    if not os.path.isabs(bundleDirRelPath):
        bundleDirAbsPathName = workingDir + "/" + bundleDirRelPath;
    
        if not os.path.exists(bundleDirAbsPathName):
            print('''Failed to craft an absolute path name for bundle directory.
            Exiting\n''');
    
            exit(1);

        bundleDir = os.path.basename(bundleDirAbsPathName);

    else:
        bundleDirAbsPathName = bundleDirRelPath;
        bundleDir = os.path.basename(bundleDirAbsPathName);
    
    dirDic["bundleDirAbsPathName"] = bundleDirAbsPathName;
    dirDic["bundleDirRelPath"] = bundleDirRelPath;
    dirDic["bundleDir"] = bundleDir;

    print("bundleDirAbsPathName: " + bundleDirAbsPathName + "\n");
    print("bundleDir: " + bundleDir + "\n");
 
    # Craft all the subdirectories in the bundle directory.
    # The dictionary will be filled-in with the various directory
    # names and paths.
    mbc.createBundleSubDirs(dirDic);

    # Craft the filename of the program that needs to be located in the
    # Contents/MacOS directory. The program binary image is copied there by the
    # CMake build system.
    
    programName = os.path.basename(bundleDir);
    programName = re.sub('\.app$', '', programName);
    dirDic["programName"] = programName;
    print("The program name is: " + dirDic["programName"] + "\n");
    
    programPath = dirDic["macosDir"] + "/" + dirDic["programName"];
    dirDic["programPath"] = programPath;
    
    if not os.path.exists(dirDic["programPath"]):
        print("Failed to find the binary " + dirDic["programName"] + "in the " +
                dirDic["macosDir"] + " directory.\n"
                "Please, rerun the build system to craft the bundle file system.");

    print("The program path is: " + dirDic["programPath"] + "\n");

    # Create the qt.conf file that will tell Qt where to find the 
    # various resources, in the bundle file system.
    print("Creating the qt.conf file in " + dirDic["resourcesDir"] + "\n");
    qtConfFileContents = "[Paths]\nPlugins = Plugins\n";
    with open(dirDic["resourcesDir"] + "/qt.conf", "w") as f:
        f.write(qtConfFileContents)
    
    # There is the platforms library dependency that does not show up 
    # when making the "otool -L" invocation. So we need to copy that library in the
    # frameworksDir and also add it to the deps list.
    
    shutil.copy2("/opt/local/libexec/qt5/plugins/platforms/libqcocoa.dylib",
            dirDic["platformsDir"]);
    
    # There is the sqldrivers directory in Plugins that must receive the
    # libqsqlite.dylib library in case the programName is mineXpert.
    
    if dirDic["programName"] == "mineXpert":
        shutil.copy2("/opt/local/libexec/qt5/plugins/sqldrivers/libqsqlite.dylib",
            dirDic["sqldriversDir"]);
    
    
    ####################### Library dependencies ######################
    ###################################################################
    
    # Now recursively check for the dependency libraries starting from the program
    # (massXpert or mineXpert, depending on the package) and recursively go down all
    # the libraries to check their own dependencies (QtScript will depend on QtCore,
    # for example).
    
    fullLibDepList = [ ]
    
    # Initialize the process with the program binary itself and then all platforms library.
    libDeps = [ dirDic["programPath"],
            "/opt/local/libexec/qt5/plugins/platforms/libqcocoa.dylib" ];
    
    # Note that with mineXpert, specifically, we need to handle the libqsqlite.dylib
    # library because that program needs it to open the db mass spec files.
    if dirDic["programName"] == "mineXpert":
        libDeps.append("/opt/local/libexec/qt5/plugins/sqldrivers/libqsqlite.dylib");
    
    while len(libDeps):
        for binImage in libDeps:
    
            print("Processing binary image: " + binImage);
    
            # Get a list of the dependencies of the current binImage
            # (be that binImage the program itself or a lib dependency).
            newLibDepList = mbc.usrLocalDepLibs(binImage);
    
            # print("This round dependencies (for " + binImage + ":\n" +
            #         "\n".join(newLibDepList) + "\n")
    
            for iterLib in newLibDepList:
                # If that library was not already found, then append it to the list
                # of the final list of dependencies. Also, make sure it will be
                # processed in turn by appending it to the libDeps list being
                # processed.
                if not iterLib in fullLibDepList:
                    fullLibDepList.append(iterLib);
                    libDeps.append(iterLib);
    
            # Now that we have iterated into all the lib deps of binImage, we can
            # remove the binImage from the libDeps.
            libDeps.remove(binImage);
    
    print("Now copy all these dependencies to the bundle:\n" +
            "\n".join(fullLibDepList) + "\n");
    
    mbc.copyLibsToBundle(fullLibDepList, dirDic["frameworksDir"], True);
    
    libList = os.listdir(dirDic["frameworksDir"]);
    
    print("Final list of dependency libraries actually in the Frameworks directory:\n" +
            "\n".join(libList));
    
    # Now that we have all the dependency libraries, we can iterate in each one and
    # make sure we change the "install_name_tool -i" such that the binary program
    # can find them all.
    for lib in libList:
        mbc.doInstallNameDashI(lib, dirDic["frameworksDir"]);
    
    # At this point we need to do the "install_name_tool -change" stuff.
    for lib in libList:
        mbc.doInstallNameDashChange(lib, dirDic["frameworksDir"]);
    
    # Do not forget the libqcocoa.dylib that is located in platformsDir.
    mbc.doInstallNameDashChange("libqcocoa.dylib", dirDic["platformsDir"]);
    
    # Do not forget the libqsqlite.dylib that is located in sqldriversDir.
    if dirDic["programName"] == "mineXpert":
        mbc.doInstallNameDashChange("libqsqlite.dylib", dirDic["sqldriversDir"]);
    
    # Finally we have to run the change function for the program itself.
    mbc.doInstallNameDashChange(dirDic["programName"], dirDic["macosDir"]);
    
    print("Finished generic processing " + dirDic["programName"] + "\n");
    
    print("Now starting the specific processing for " + dirDic["programName"] + "\n");
    
    mbc.doProgramSpecific(dirDic);

    # Finally, copy the app bundle to the Desktop
    mbc.moveBundleToDesktop(dirDic);

    input("Done working on " + dirDic["bundleDir"] + " Continue? (Return | Ctrl-C)\n");

    
answer = input('''Backup all the libraries ? (yY | nN)\n''');

if answer == 'y' or answer == 'Y':
    scriptPath = os.path.dirname(os.path.abspath( __file__ ));
    scriptPath += "/backupLibs.py";

    completedProcess = subprocess.run(["sudo", scriptPath, 
        "--backup", " ".join(fullLibDepList)], stdout=subprocess.PIPE)

    completedProcess.check_returncode();

    output = completedProcess.stdout.decode("utf-8");
    print(output);

# At this point we can prepare the library-renaming script.
mbc.renameLibraries(fullLibDepList, dirDic);

# At this point we can prepare the library-reinstating script.
mbc.reinstateLibraries(fullLibDepList, dirDic);


