#!/usr/bin/env python3

import os
import argparse
import re
import shutil
import subprocess
from time import gmtime, strftime


# At this point we have all the required data: bundle directory and its location
# in the filesystem, along with the binary program file to be used as a starting
# point for all the framework stuff.

def createBundleSubDirs(dirDic):

    macosDir = dirDic["bundleDirAbsPathName"] + "/Contents/MacOS";
    if not os.path.exists(macosDir):
        os.mkdir(macosDir);
        print("Created " + macosDir + "\n");
    dirDic["macosDir"] = macosDir;
    
    frameworksDir = dirDic["bundleDirAbsPathName"] + "/Contents/Frameworks";
    if not os.path.exists(frameworksDir):
        os.mkdir(frameworksDir);
        print("Created " + frameworksDir + "\n");
    dirDic["frameworksDir"] = frameworksDir; 

    pluginsDir = dirDic["bundleDirAbsPathName"] + "/Contents/Plugins";
    if not os.path.exists(pluginsDir):
        os.mkdir(pluginsDir);
        print("Created " + pluginsDir + "\n");
    dirDic["pluginsDir"] = pluginsDir; 
    
    platformsDir = pluginsDir + "/platforms";
    # In this platforms directory we'll later copy the libqcocoa.dylib (see below)
    if not os.path.exists(platformsDir):
        os.mkdir(platformsDir);
        print("Created " + platformsDir + "\n");
    dirDic["platformsDir"] = platformsDir;
    
    sqldriversDir = pluginsDir + "/sqldrivers";
    # In this sqldrivers directory we'll later copy the libqsqlite.dylib (see below)
    if not os.path.exists(sqldriversDir):
        os.mkdir(sqldriversDir);
        print("Created " + sqldriversDir + "\n");
    dirDic["sqldriversDir"] = sqldriversDir;
    
    resourcesDir = dirDic["bundleDirAbsPathName"] + "/Contents/Resources";
    if not os.path.exists(resourcesDir):
        os.mkdir(resourcesDir);
        print("Created " + resourcesDir + "\n");
    dirDic["resourcesDir"] = resourcesDir;
    

def usrLocalDepLibs(binaryImage):
    if not os.path.exists(binaryImage):
        print("Could not find " + binaryImage + " .Exiting");
        exit(1);

    # Use the otool utility to check for the dependencies of binaryImage. This
    # tool is roughly like using objdump -p <binaryImage> | grep NEEDED on
    # GNU/Linux.

    completedProcess = subprocess.run(["otool", "-L", binaryImage], stdout=subprocess.PIPE)
    completedProcess.check_returncode();
    
    output = completedProcess.stdout.decode("utf-8");
    # print(output);
    
    # The various dependency libraries are all contained in the /opt/local/lib
    # directory. The output thus contains the full absolute path of the dep
    # libs.

    # Thus retain only the "/local/lib"-containing lines from the single string
    # that is output by the command call.
    
    lines = output.split("\n");
    # print(lines);
    libDependencies = [ ];
    
    for line in lines:
        if "/local/lib" in line:
            print(line + "\n");

            # Now extract only the library absolute path.
            match = re.match(r"^\s*(/\S+) .*$", line)
            if match:
                # print(match.group(1));
                # Store the dep lib absolute path
                libDependencies.append(match.group(1));
            
    #print("All the local/lib dependencies of the binary " + binaryImage + ": \n");
    #print('\n'.join(libDependencies));
       
    return libDependencies;


def copyLibsToBundle(libList, frameworksDir, overwrite = False):

    if not os.path.exists(frameworksDir):
        print("Could not find " + frameworksDir + " .Exiting");
        exit(1);

    copiedFilesList = [];

    for lib in libList:
        if not os.path.exists(frameworksDir + "/" + os.path.basename(lib)) or overwrite:
            shutil.copy2(lib, frameworksDir);
            copiedFilesList.append(lib);

    print("List of copied libraries:\n" + "\n".join(copiedFilesList) + "\n");

    completedProcess = subprocess.run(["ls", frameworksDir], stdout=subprocess.PIPE)
    completedProcess.check_returncode();
    output = completedProcess.stdout.decode("utf-8");

    print("\nThe list of frameworks copied to the bundle:\n" + output);


def doInstallNameDashI(lib, frameworksDir):
    
    if not os.path.exists(frameworksDir):
        print("Could not find " + frameworksDir + " .Exiting");
        exit(1);
    if not os.path.exists(frameworksDir + "/" + lib):
        print("Could not find " + frameworksDir + "/" + lib + " .Exiting");
        exit(1);

    string1 = "@executable_path/../Frameworks/{lib}".format(lib=lib);
    string2 = "{frameworksDir}/{lib}".format(lib=lib, frameworksDir=frameworksDir);

    completedProcess = subprocess.run(["install_name_tool",
        "-id", string1, string2], stdout=subprocess.PIPE)
    completedProcess.check_returncode();
    output = completedProcess.stdout.decode("utf-8");
    # print(output);


def doInstallNameDashChange(binaryImage, directory):

    if not os.path.exists(directory):
        print("Could not find " + directory + " .Exiting");
        exit(1);
    if not os.path.exists(directory + "/" + binaryImage):
        print("Could not find " + directory + "/" + binaryImage + " .Exiting");
        exit(1);

    print("Processing binaryImage: " + binaryImage + "\n");
    
    # returns the dependency libraries as system paths.
    depLibs = usrLocalDepLibs(directory + "/" + binaryImage);

    print("Dep libs:\n" + " ".join(depLibs) + "\n");

    for depLib in depLibs:

        print("Sub-processing lib: " + depLib + "\n");

        baseName = os.path.basename(depLib);

        string1 = "{depLib}".format(depLib = depLib);
        string2 = "@executable_path/../Frameworks/{baseName}".format(baseName =
                baseName);
        string3 = "{directory}/{binaryImage}".format(directory =
                directory, binaryImage = binaryImage);
        
        commandLine = ["install_name_tool", "-change", string1, string2, string3];
        print("Command line:\n" + " ".join(commandLine) + "\n");

        completedProcess = subprocess.run(["install_name_tool",
            "-change", string1, string2, string3], stdout=subprocess.PIPE)
        completedProcess.check_returncode();
        output = completedProcess.stdout.decode("utf-8");
        # print(output);


def doProgramSpecific(dirDic):
   
    if(dirDic["programName"] == "massXpert"):
        doMassXpert(dirDic);

    if(dirDic["programName"] == "mineXpert"):
        doMineXpert(dirDic);


def doMassXpert(dirDic):

    # The data directory is filled-in by CMake make install directives
    # in Contents/Resources/massxpert/data
    # and the documentation goes in 
    # Contents/Resources/massxpert/doc 
   
    print("Copy the massXpert user manual to the bundle\n");
    shutil.copy2(dirDic["devDirName"] + "/doc/user-manuals/massxpert-doc.pdf",
        dirDic["bundleDirAbsPathName"] + "/Contents/Resources/massxpert/doc");
    

def doMineXpert(dirDic):

    print("Copy the mineXpert user manual to the bundle\n");
    shutil.copy2(dirDic["devDirName"] + "/doc/user-manuals/minexpert-doc.pdf",
        dirDic["bundleDirAbsPathName"] + "/Contents/Resources/minexpert/doc");
    

def backupLibraries(libList):

    # print("All backupLibs:\n");
    # print(str(libList));
    
    # allLibs = libList[0].split(" ");
    
    # print("allLibs\n");
    # print(allLibs);
    # print("done allLibs\n");
    
    for lib in libList:
    
        # print("Making a backup of library: " + lib + "\n");
        baseName = os.path.basename(lib);
        dirName = os.path.dirname(lib);
        backupDirName = dirName + "/backup";
    
        if not os.path.exists(backupDirName):
            os.mkdir(backupDirName);
            print("Creating " + backupDirName);
            if not os.path.exists(backupDirName):
                print("Could not create " + backupDirName + " Exiting.\n");
                exit(1);
        
        bkpLibName = backupDirName + "/" + baseName;
        
        command = "shutil.copy2({lib}, {bkpLibName})".format(lib = lib,
                bkpLibName = bkpLibName);
        print(command);
    
        newPath = shutil.copy2(lib, bkpLibName);
        if newPath != bkpLibName:
            print("Failed to backup " + lib + "\n");
            exit(1);
    
    print("\nDone making backup of all the libraries\n");
    

def renameLibraries(libList, dirDic):

    # print("\nRename the libraries in list:\n");
    # print(libList);
    # print("\n");

    # allLibs = libList[0].split(" ");

    # Open a text file with write bit set

    libRenamingScriptFile = dirDic["devDirName"] + "/libRenamingScript.sh";
    dirDic["libRenamingScriptFile"] = libRenamingScriptFile;

    f = open(libRenamingScriptFile , 'w')

    for lib in libList:

        baseName = os.path.basename(lib);
        dirName = os.path.dirname(lib);

        wasLibName = dirName + "/was-" + baseName;
        
        command = "mv -v {lib} {wasLibName}\n".format(lib = lib,
                wasLibName = wasLibName);

        f.write(command);

    f.close();

    print("\nDone scripting all the renaming calls in " 
            + libRenamingScriptFile
            + "\n");


def reinstateLibraries(libList, dirDic):
  
    # print("\nReinstate the libraries in list:\n");
    # print(libList);
    # print("\n");

    # allLibs = backupLibs[0].split(" ");

    libReinstatingScriptFile = dirDic["devDirName"] + "/libReinstatingScript.sh";
    dirDic["libReinstatingScriptFile"] = libReinstatingScriptFile;

    f = open(libReinstatingScriptFile , 'w')

    for lib in libList:

        baseName = os.path.basename(lib);
        dirName = os.path.dirname(lib);

        wasLibName = dirName + "/was-" + baseName;
        
        command = "mv -v {wasLibName} {lib}\n".format(lib = lib,
                wasLibName = wasLibName);

        f.write(command);

    f.close();

    print("\nDone scripting all the reinstating calls in " 
            + libReinstatingScriptFile
            + "\n");


def moveBundleToDesktop(dirDic):

    bundleDirAbsPathName = dirDic["bundleDirAbsPathName"];
    print("bundleDirAbsPathName: " + bundleDirAbsPathName);

    if not os.path.exists(bundleDirAbsPathName):
        print("The bundle directory does not exist.\n");

    homeDir = os.getenv('USERPROFILE') or os.getenv('HOME')
    if not os.path.exists(homeDir):
        print("Home directory not found.\n");

    print("homeDir: " + homeDir);

    destDir = homeDir + "/Desktop/" + dirDic["bundleDir"];
    print("destDir: " + destDir);

    backupDir = destDir + strftime("-at-%Y%H%M%S", gmtime());
    print("backupDir: " + backupDir);

    print("Copying bundle to " + destDir + "\n");

    if os.path.exists(destDir):
        print("Creating backup of destDir to backupDir\n");
        shutil.move(destDir, backupDir);

    newDir = shutil.copytree(bundleDirAbsPathName, destDir);

    if newDir != destDir:
        print("Failed to copy the bundle to the Desktop.\n");

    print("Copied " + bundleDirAbsPathName + " to " + destDir);









    


