#!/usr/bin/python
#
#******************************************************************************
# Copyright (c) 2004-2006 Autodesk Canada Co. / Autodesk, Inc.
# All rights reserved.
#******************************************************************************

import os
import sys
import string
import re
#import select
#import popen2
import distutils.dir_util


packagesToUpgrade = [ \
    	"lustre_help_2013"
     ]

oneOfAKindPackages = [
]

interestingPackages = [ \
    	"lustre_help_2013"
     ]

knownArchitectures = [ "i386", "i486", "i586", "i686", "x86_64" ]

machineArch = os.popen4("uname -m")[1].read().strip()
kernelVersion = os.popen4("uname -r")[1].read().strip()


def executeCommand(command):
    if os.getenv("DL_INSTALL_DEBUG"):
        print "\tExecuting: ", command
    return os.popen4(command)[1].readlines()


def stripExtensions(fileName):
    index = string.rfind(fileName, ".rpm")

    if index != -1:
        fileName = fileName[0:index]

    for arch in knownArchitectures:
        index = string.rfind(fileName, "." + arch)
        if index != -1:
            fileName = fileName[0:index]
            break

    return fileName

releaseTags = { "prealpha":"0", "alpha":"1", "beta":"2" }
ignoreReleaseTags = [ "SP" ]

def splitPackageName(package):
    packageVersion = ""

    parts = package.split("-")
    packageName = parts[0]

    if len(parts) > 1:
        finishedName = False

        for partnumber in range(1, len(parts)):
            part = parts[partnumber]

            if not finishedName:
                if len(part) > 0:
                    if not part[0].isdigit():
                        packageName += "-" + part
                    else:
                        packageVersion = part
                        finishedName = True
                else:
                    packageName += "-"
            else:
                packageVersion += "-" + part

    return (packageName, packageVersion)

def splitVersion(versionString):
    parts = versionString.split("-")
    version = parts[0]
    release = ""

    if len(parts) > 1:
        release = "-".join(parts[1:])

    # replace alpha/beta strings by a number
    # move alpha/beta strings to the "release" section
    for tag in releaseTags:
        pattern = re.compile(tag + "[0-9]*")
        match = pattern.search(version)
        if match:
            release += "-" + version[match.start():]
            version = version[0:match.start()]

    # strip off SP string, while keeping the actual SP version number.
    # 2007.0.SP2 will become 2007.0.2
    for tag in ignoreReleaseTags:
        pattern = re.compile(tag + "[0-9]*")
        match = pattern.search(version)
        if match:
            version = version[0:match.start()] + version[match.start()+len(tag):]

    # remove any unknown release tag
    match = re.compile("([0-9\.]*).*").search(version)
    if match:
        version = match.group(1)

    return (version, release)

def splitSubVersion(versionString):
    versions = versionString.split(".")
    major = versions[0]

    minor = 0
    if len(versions) > 1 and versions[1] != '':
        minor = versions[1]

    patch = 0
    if len(versions) > 2:
       if len(versions[2]) > 0:
          patch = versions[2]

    return (int(major), int(minor), int(patch))

def splitSubRelease(releaseString):
    (releaseNum, preReleaseInfo) = re.compile("([0-9]+)(.*)").search(releaseString).groups()

    preReleaseString = ""
    preReleaseNum = 0

    if (preReleaseInfo != ""):
        match = re.compile("(.*?)([0-9]+)").search(preReleaseInfo)
        if match:
            (preReleaseString, preReleaseNum) = match.groups()
        else:
            preReleaseString = preReleaseInfo

    return (int(releaseNum), preReleaseString, int(preReleaseNum))

def samePackage(firstPackage, secondPackage):
    return splitPackageName(firstPackage)[0] == splitPackageName(secondPackage)[0]

def similarPackage(firstPackage, secondPackage):
    (firstName, firstVersion) = splitPackageName(firstPackage)
    (secondName, secondVersion) = splitPackageName(secondPackage)

    pattern = re.compile("[0-9]*")

    firstName = pattern.sub("", firstName.lower())
    secondName = pattern.sub("", secondName.lower())

    return firstName == secondName

def sameMajorVersion(firstPackage, secondPackage):
    if samePackage(firstPackage, secondPackage):
        (firstVersion, firstRelease) = splitVersion(splitPackageName(firstPackage)[1])
        (secondVersion, secondRelease) = splitVersion(splitPackageName(secondPackage)[1])

        (fMajor, fMinor, fPatch) = splitSubVersion(firstVersion)
        (sMajor, sMinor, sPatch) = splitSubVersion(secondVersion)
 
        if fMajor == sMajor:
            return True

    return False

def sameMinorVersion(firstPackage, secondPackage):
    if samePackage(firstPackage, secondPackage):
        (firstVersion, firstRelease) = splitVersion(splitPackageName(firstPackage)[1])
        (secondVersion, secondRelease) = splitVersion(splitPackageName(secondPackage)[1])

        (fMajor, fMinor, fPatch) = splitSubVersion(firstVersion)
        (sMajor, sMinor, sPatch) = splitSubVersion(secondVersion)
 
        if fMinor == sMinor:
            return True

    return False

def isPreRelease(package):
    for tag in releaseTags:
        pattern = re.compile(tag + "[0-9]*")
        match = pattern.search(package)
        if match:
            return True

    return False;

def getMostRecentVersion(firstVersion, secondVersion):
    (fVer, fRel) = splitVersion(firstVersion)
    (sVer, sRel) = splitVersion(secondVersion)

    (fMajor, fMinor, fPatch) = splitSubVersion(fVer)
    (sMajor, sMinor, sPatch) = splitSubVersion(sVer)
    
    if fMajor < sMajor:
        return secondVersion
    elif sMajor < fMajor:
        return firstVersion
    else:
        if fMinor < sMinor:
            return secondVersion
        elif sMinor < fMinor:
            return firstVersion
        else:
            if fPatch < sPatch:
                return secondVersion
            elif sPatch < fPatch:
                return firstVersion
            else:
                (fReleaseNum, fPreRelString, fPreRelNum) = splitSubRelease(fRel)
                (sReleaseNum, sPreRelString, sPreRelNum) = splitSubRelease(sRel)

                if fReleaseNum < sReleaseNum:
                    return secondVersion
                elif sReleaseNum < fReleaseNum:
                    return firstVersion
                else:
                    if (fPreRelString in releaseTags) and (sPreRelString in releaseTags):
                        if releaseTags[fPreRelString] > releaseTags[sPreRelString]:
                            return firstVersion
                        else:
                            return secondVersion
                    elif fPreRelString < sPreRelString:
                        return secondVersion
                    elif sPreRelString < fPreRelString:
                        return firstVersion
                    else:
                        if fPreRelNum < sPreRelNum:
                            return secondVersion
                        elif sPreRelNum < fPreRelNum:
                            return firstVersion

    return secondVersion

def getMostRecentPackage(firstPackage, secondPackage):
    firstVersion = splitPackageName(firstPackage)[1]
    secondVersion = splitPackageName(secondPackage)[1]

    if getMostRecentVersion(firstVersion, secondVersion) == firstVersion:
        return firstPackage

    return secondPackage


def isPackageInstalled(packageName):
    result = executeCommand("/bin/rpm -q " + packageName)
    if result[0].find(" is not installed") == -1:
        fixedResult = []
        for line in result:
            fixedResult.append(line.strip())
        return fixedResult
    else:
        return None

def checkUninstall(packageName):
    print "\t\tTesting uninstall of", packageName
    ok = True
    result = executeCommand("/bin/rpm -e --allmatches --test " + packageName)
    if len(result):
        ok = False
        for line in result:
            print "\t\t>", line.strip()
    return ok


def uninstallPackage(packageName, otherOptions = ""):
    result = executeCommand("/bin/rpm -e --allmatches " + otherOptions + " " + packageName)
    for line in result:
        print ">", line.strip()

def checkUpgrade(fileName):
    print "\tTesting upgrade"
    ok = True
    result = executeCommand("/bin/rpm -U --nomd5 --test --nosignature " + fileName)
    if len(result):
        ok = False
        for line in result:
            print "\t>", line.strip()
    return ok


def upgradePackage(fileName, otherOptions = ""):
    print "\tUpgrading ..."
    os.system("/bin/rpm -U --nomd5 " + otherOptions + " " + fileName)

conflictPattern = re.compile(" conflicts with file from package ")

def checkForceRequired(fileName):
    forceRequired = False
    result = executeCommand("/bin/rpm -i --test --nosignature --nomd5 " + fileName)
    if len(result):
        newerPattern = re.compile("package (.+) \(which is newer than (.+)\) is already installed")
        for message in result:
            if not newerPattern.search(message):
                break
        else:
            forceRequired = True
    return forceRequired
    

def checkInstall(fileName, otherOptions = ""):
    print "\tTesting install of", fileName
    ok = True
    result = executeCommand("/bin/rpm -i " + otherOptions + " --test --nosignature --nomd5 " + fileName)
    if len(result):
        ok = False
        for line in result:
            print "\t>", line.strip()
    return ok


def getInstallConflicts(fileName):
    conflicts = []

    result = executeCommand("/bin/rpm -i --test --nosignature --nomd5 " + fileName)
    for line in result:
        line = line.strip()
        match = conflictPattern.search(line)
        if match:
            conflicts.append( line[match.end():] )

    dict = {}
    for conf in conflicts:
        dict[conf] = ""
    return dict.keys()


def installPackage(fileName, otherOptions = ""):
    print "\tInstalling ..."
    os.system("/bin/rpm -i --nomd5 " + otherOptions + " " + fileName)

def conflictsOkToUninstall(toInstall, conflicts):

    for conflict in conflicts:
        if not similarPackage(toInstall, conflict):
            return False

        if getMostRecentPackage(toInstall, conflict) != toInstall:
            return False
    return True


def proceedPackageList(packageListToInstall):

    for packageInfo in packageListToInstall:
        package = packageInfo[NAME]
        packageFile = packageInfo[FILE]

        if package == "backburner_server.sw.base" and isPackageInstalled("backburner_server.sw.base"):
            print "Stopping Backburner Server"
            executeCommand("/usr/discreet/backburner/backburner_server_stop")
        
        if package in packagesToUpgrade:
            print "Upgrading " + package + " with " + packageFile
            if checkUpgrade(packageFile):
                upgradePackage(packageFile)
                installed.append(package)
            elif force:
                print "Force install specified, continuing upgrade..."
                upgradePackage(packageFile, "--nodeps --force")
                installed.append(package)
            else:
                print "Force install not specified, stopping install..."
                break
        else:
            versionToInstall = stripExtensions(os.path.basename(packageFile))

            print "Installing " + packageFile

            conflicts = getInstallConflicts(packageFile)

            if package in oneOfAKindPackages:
                installedVersions = isPackageInstalled(package)
                if installedVersions:
                    for installedVersion in installedVersions:
                        if (sameMajorVersion(installedVersion, versionToInstall) \
                            and sameMinorVersion(installedVersion, versionToInstall) \
                            and (not isPreRelease(versionToInstall) \
                                 or (isPreRelease(installedVersion) \
                                     and getMostRecentPackage(installedVersion, versionToInstall) == versionToInstall))):
				         if not installedVersion in conflicts:
                                             conflicts.append(installedVersion)

            if conflicts:
                if conflictsOkToUninstall(versionToInstall, conflicts):
                    # special case, conflicts with older versions of the same package
                    print "\tWarning: package", packageFile, "conflicts with older version:"
                    for conflict in conflicts:
                        print "\t\t" + conflict

                    print "\tWill replace older versions"
                    
                    for conflict in conflicts:
                        uninstallPackage(conflict, "--nodeps")
                else:
                    if package != "backburner.sw.base":
                        print "\tWarning:", packageFile, "conflicts with:"
                        for conflict in conflicts:
                            print "\t\t", conflict

                        if force:
                            print "\tForce install specified, removing conflicts..."
                            for conflict in conflicts:
                                uninstallPackage(conflict, "--nodeps")
                        else:
                            if getMostRecentPackage(versionToInstall, conflict) == conflict:
                                print "\tNewer version already installed, skipping", versionToInstall
                                continue
                            else:
                                print "\nForce install not specified, stopping install...\n"
                                break

            otherInstallOption = ""
            if package == "backburner.sw.base":
                # brutally install backburner.sw.base on top of the previous version
                print "Warning forcibly installing backburner.sw.base"
                installPackage(packageFile, "--nodeps --force")
            else:
                if checkForceRequired(packageFile):
                    otherInstallOption = "--force"

                if checkInstall(packageFile, otherInstallOption):
                    installPackage(packageFile, otherInstallOption)
                    installed.append(package)
                elif force:
                    print "\tForce install specified, continuing..."
                    installPackage(packageFile, "--force --nodeps")
                    installed.append(package)
                else:
                    print "Force install not specified, stopping install..."
                    break




# unset LD_ASSUME_KERNEL since adding/removing rpm packages with
# this environment variable set to 2.4.19 is likely to corrupt the rpm
# database 
os.unsetenv("LD_ASSUME_KERNEL");

# Let sub-processes know they are called from the install script.
os.putenv("DL_INSTALL_SCRIPT_RUNNING", sys.argv[0]);

# Enforce english locale, this ensures the system outputs messages in english,
# because we look for hard coded strings. 
# 
os.putenv("LANG", "en_US.UTF-8");

args = sys.argv[1:]

doUninstall = False
printUsage = False
force = False

wiretapArgumentString = ""

for arg in args:
    if arg == "--install":
        wiretapArgumentString += "--install "
        pass
    elif arg == "--uninstall":
        wiretapArgumentString += "--uninstall "
        doUninstall = True
    elif arg == "--force":
        wiretapArgumentString += "--force "
        force = True
    else:
        printUsage = True


if printUsage:
    print(sys.argv[0] + " [--install/--uninstall] [--force]")
    sys.exit(1)    
elif doUninstall:
    print("Uninstalling packages...")

    packages = interestingPackages
    packages.reverse()

    for package in packages:
        print "Looking for", package
        if isPackageInstalled(package):
            print "\tUninstalling " + package
            if checkUninstall(package):
                uninstallPackage(package)
            elif force:
                print "\t\tForce uninstall specified, continuing uninstall..."
                uninstallPackage(package, "--nodeps")
            else:
                print "\t\tForce uninstall not specified, skipping this package..."

else:
    print("Finding packages...")

    # Start with current dir then check if a kernel specific 
    # dir exists and add it to the list.
    dirList = []
    dirList.append(os.getcwd())
    dirKernel = os.getcwd() + "/" + kernelVersion
    if os.path.exists(dirKernel):
        dirList.append(dirKernel)

    packagesToInstall = []
    NAME = 0
    FILE = 1

    for package in interestingPackages:
        found = False
        for dir in dirList:
            dirContent = os.listdir(dir);
            #print("\tLooking in directory " + dir );
    
            for file in dirContent:
                if file.find(package) == 0:
                    filePath = dir + "/" + file
                    print("\tUsing " + filePath);
                    packagesToInstall.append((package,filePath))
                    found = True
        if not found:
            print("\tWarning: Can't find " + package)

    print("Checking installed versions...")

    toRemove = []

    for packageInfo in packagesToInstall:
        package = packageInfo[NAME]
        packageFile = packageInfo[FILE]
        versionToInstall = stripExtensions(os.path.basename(packageFile))

        if isPackageInstalled(versionToInstall):
            print "\t",versionToInstall,"is already installed"
            toRemove.append((package,packageFile))
        elif package in packagesToUpgrade:
            installedVersions = isPackageInstalled(package)
            if installedVersions:
                for version in installedVersions:
                    if getMostRecentPackage(versionToInstall, version) == version:
                        print "\tInstalled package ",version," is more recent"
                        toRemove.append((package,packageFile))
                        break

    for packageInfo in toRemove:
        packagesToInstall.remove((packageInfo[NAME],packageInfo[FILE]))

    print("Performing installs...")

    # Remove obsolete or renamed packages
    for packageInfo in packagesToInstall:
        package = packageInfo[NAME]
        packageFile = packageInfo[FILE]
        if package == "Stonewire.base.sw" and isPackageInstalled("Stonewire.eoe.sw"):
            print "Uninstalling Stonewire.eoe.sw"
            uninstallPackage("Stonewire.eoe.sw")

        if package == "backburner.sw.base" and isPackageInstalled("backburner_utils.sw.base"):
            print "Uninstalling backburner_utils.sw.base"
            uninstallPackage("backburner_utils.sw.base");

        if package == "backburner.sw.base" and isPackageInstalled("backburner_server.sw.base"):
            print "Uninstalling backburner_server.sw.base"
            uninstallPackage("backburner_server.sw.base");

        if package == "backburner.sw.base" and isPackageInstalled("backburner_manager.sw.base"):
            print "Uninstalling backburner_manager.sw.base"
            uninstallPackage("backburner_manager.sw.base");

        if package == "dlfonts.sw.fonts" and isPackageInstalled("DLfonts.sw.fonts"):
            print "Uninstalling DLfonts.sw.fonts"
            uninstallPackage("DLfonts.sw.fonts")

        if package == "dlfonts.sw.fonts" and isPackageInstalled("dlfonts.sw.fonts"):
            print "Uninstalling dlfonts.sw.fonts to upgrade"
            uninstallPackage("dlfonts.sw.fonts", "--nodeps")

        if package == "linuxtools.sw.base" and isPackageInstalled("linuxtools.sw.base"):
            print "Uninstalling linuxtools.sw.base to upgrade"
            uninstallPackage("linuxtools.sw.base", "--nodeps")

        if package == "autodesk.wiretapcentral.server" and isPackageInstalled("wiretapcentral.sw.server"):
            print "Uninstalling wiretapcentral.sw.server to upgrade"
            uninstallPackage("wiretapcentral.sw.server")



    toRemove = []
    installed = []

    # first pass, try upgrading all Stonewire packages
    stoneList = []
    for packageInfo in packagesToInstall:
        package = packageInfo[NAME]
        packageFile = packageInfo[FILE]
        if package.startswith("Stonewire."):
            stoneList.append((package,packageFile))
            toRemove.append((package,packageFile))

    if stoneList:
        # Stonewire depends on backburner_libs so install it first
        bbPackages = []
        for packageInfo in packagesToInstall:
            if packageInfo[NAME] == "backburner_libs.sw.base":
                bbPackages.append(packageInfo)
                toRemove.append(packageInfo)

        if bbPackages:
            proceedPackageList(bbPackages)
    
        print "Upgrading Stonewire packages"
    
        stoneFiles = []
        for packageInfo in stoneList:
            stoneFiles.append(packageInfo[FILE])
         
        stonePackages = " ".join(stoneFiles)
        if checkUpgrade(stonePackages):
            upgradePackage(stonePackages)
            for packageInfo in stoneList:
                installed.append(packageInfo[NAME])
        elif force:
            print "Force install specified, continuing upgrade..."
            upgradePackage(stonePackages, "--nodeps --force")
            for packageInfo in stoneList:
               installed.append(packageInfo[NAME])
        else:
            print "Force install not specified, stopping install..."
            sys.exit(1)

    for packageInfo in toRemove:
        packagesToInstall.remove((packageInfo[NAME],packageInfo[FILE]))

    # Do it!
    proceedPackageList(packagesToInstall)

    # Post-install operations
    if "Stonewire.base.sw" in installed:
        os.system("/etc/init.d/stone+wire start >/dev/null 2>&1 </dev/null")

    # Install documentation in the application's folder.
    # Used to be inside the package, now outside so the
    # PDFs can be browsed directly from the CD and/or from a laptop.
    # Caveat: needs to figure out manually the location of application
    #
    for packageInfo in packagesToInstall:
        packageName = packageInfo[NAME]
        packageFile = packageInfo[FILE]
        # kludge, the ifffs packages have ".sw.ogl" in their name.
        index = string.find(packageName,".sw.ogl")
        if index != -1:
            fileList = executeCommand("rpm -qlp " +  packageFile + " | head -2 | tail -1") 
            if len(fileList):
                appDir = os.path.dirname(fileList[0])
                if os.path.exists(appDir):
                    if os.path.exists("documentation/"):
                        print "Installing documentation in " + appDir + "/documentation/"
                        distutils.dir_util.copy_tree("documentation/", appDir + "/documentation/")
           

