from PackageUtils import PackageUtils
from Logger import Logger
from ChrootUtils import ChrootUtils
from ToolChainUtils import ToolChainUtils
from CommandUtils import CommandUtils
import os.path
from constants import constants
import shutil
from SpecData import SPECS

class PackageBuilder(object):

    def __init__(self,mapPackageToCycles,listAvailableCyclicPackages,listBuildOptionPackages,pkgBuildOptionFile):
        # will be initialized in buildPackageThreadAPI()
        self.logName=None
        self.logPath=None
        self.logger=None
        self.package=None
        self.mapPackageToCycles = mapPackageToCycles
        self.listAvailableCyclicPackages = listAvailableCyclicPackages
        self.listNodepsPackages = ["glibc","gmp","zlib","file","binutils","mpfr","mpc","gcc","ncurses","util-linux","groff","perl","texinfo","rpm","openssl","go"]
        self.listBuildOptionPackages=listBuildOptionPackages
        self.pkgBuildOptionFile=pkgBuildOptionFile

    def prepareBuildRoot(self):
        chrootID=None
        chrootName="build-"+self.package
        try:
            chrUtils = ChrootUtils(self.logName,self.logPath)
            returnVal,chrootID = chrUtils.createChroot(chrootName)
            self.logger.debug("Created new chroot: " + chrootID)
            if not returnVal:
                raise Exception("Unable to prepare build root")
            tUtils=ToolChainUtils(self.logName,self.logPath)
            tUtils.installToolChainRPMS(chrootID, self.package, self.logPath)
        except Exception as e:
            if chrootID is not None:
                self.logger.debug("Deleting chroot: " + chrootID)
                chrUtils.destroyChroot(chrootID)
            raise e
        return chrootID

    def findPackageNameFromRPMFile(self,rpmfile):
        rpmfile=os.path.basename(rpmfile)
        releaseindex=rpmfile.rfind("-")
        if releaseindex == -1:
            self.logger.error("Invalid rpm file:"+rpmfile)
            return None
        versionindex=rpmfile[0:releaseindex].rfind("-")
        if versionindex == -1:
            self.logger.error("Invalid rpm file:"+rpmfile)
            return None
        packageName=rpmfile[0:versionindex]
        return packageName

    def findInstalledPackages(self,chrootID):
        pkgUtils = PackageUtils(self.logName,self.logPath)
        listInstalledRPMs=pkgUtils.findInstalledRPMPackages(chrootID)
        listInstalledPackages=[]
        for installedRPM in listInstalledRPMs:
            packageName=self.findPackageNameFromRPMFile(installedRPM)
            if packageName is not None:
                listInstalledPackages.append(packageName)
        return listInstalledPackages

    def buildPackageThreadAPI(self,package,outputMap, threadName,):
        self.package=package
        self.logName="build-"+package
        self.logPath=constants.logPath+"/build-"+package
        if not os.path.isdir(self.logPath):
            cmdUtils = CommandUtils()
            cmdUtils.runCommandInShell("mkdir -p "+self.logPath)
        self.logger=Logger.getLogger(self.logName,self.logPath)

        try:
            self.buildPackage()
            outputMap[threadName]=True
        except Exception as e:
            # TODO: self.logger might be None
            self.logger.error(e)
            outputMap[threadName]=False

    def checkIfPackageIsAlreadyBuilt(self):
        basePkg=SPECS.getData().getSpecName(self.package)
        listRPMPackages=SPECS.getData().getRPMPackages(basePkg)
        packageIsAlreadyBuilt=True
        pkgUtils = PackageUtils(self.logName,self.logPath)
        for pkg in listRPMPackages:
            if pkgUtils.findRPMFileForGivenPackage(pkg) is None:
                packageIsAlreadyBuilt=False
                break
        return packageIsAlreadyBuilt

    def buildPackage(self):
        #do not build if RPM is already built
        #test only if the package is in the testForceRPMS with rpmCheck
        #build only if the package is not in the testForceRPMS with rpmCheck
        if self.checkIfPackageIsAlreadyBuilt():
            if not constants.rpmCheck:
                self.logger.info("Skipping building the package:"+self.package)
                return
            elif constants.rpmCheck and self.package not in constants.testForceRPMS:
                self.logger.info("Skipping testing the package:"+self.package)
                return

        chrUtils = ChrootUtils(self.logName,self.logPath)
        chrootID=None
        try:
            chrootID = self.prepareBuildRoot()
            listInstalledPackages=self.findInstalledPackages(chrootID)
            listDependentPackages=self.findBuildTimeRequiredPackages()
            if constants.rpmCheck and self.package in constants.testForceRPMS:
                listDependentPackages.extend(self.findBuildTimeCheckRequiredPackages())
                testPackages=set(constants.listMakeCheckRPMPkgtoInstall)-set(listInstalledPackages)-set([self.package])
                listDependentPackages.extend(testPackages)
                listDependentPackages=list(set(listDependentPackages))

            pkgUtils = PackageUtils(self.logName,self.logPath)
            if len(listDependentPackages) != 0:
                self.logger.info("Installing the build time dependent packages......")
                for pkg in listDependentPackages:
                    self.installPackage(pkgUtils, pkg,chrootID,self.logPath,listInstalledPackages)
                pkgUtils.installRPMSInAOneShot(chrootID,self.logPath)
                self.logger.info("Finished installing the build time dependent packages......")

            pkgUtils.adjustGCCSpecs(self.package, chrootID, self.logPath)
            pkgUtils.buildRPMSForGivenPackage(self.package,chrootID,self.listBuildOptionPackages,self.pkgBuildOptionFile,self.logPath)
            self.logger.info("Successfully built the package:"+self.package)
        except Exception as e:
            self.logger.error("Failed while building package:" + self.package)
            self.logger.debug("Chroot with ID: " + chrootID + " not deleted for debugging.")
            logFileName = os.path.join(self.logPath, self.package + ".log")
            fileLog = os.popen('tail -n 100 ' + logFileName).read()
            self.logger.debug(fileLog)
            raise e
        if chrootID is not None:
            chrUtils.destroyChroot(chrootID)


    def findRunTimeRequiredRPMPackages(self,rpmPackage):
        listRequiredPackages=SPECS.getData().getRequiresForPackage(rpmPackage)
        return listRequiredPackages

    def findBuildTimeRequiredPackages(self):
        listRequiredPackages=SPECS.getData().getBuildRequiresForPackage(self.package)
        return listRequiredPackages

    def findBuildTimeCheckRequiredPackages(self):
        listRequiredPackages=SPECS.getData().getCheckBuildRequiresForPackage(self.package)
        return listRequiredPackages

    def installPackage(self,pkgUtils,package,chrootID,destLogPath,listInstalledPackages):
        if package in listInstalledPackages:
            return
        self.installDependentRunTimePackages(pkgUtils,package,chrootID,destLogPath,listInstalledPackages)
        noDeps=False
        if self.mapPackageToCycles.has_key(package):
            noDeps = True
        if package in self.listNodepsPackages:
            noDeps=True
        if package in constants.noDepsPackageList:
            noDeps=True
        pkgUtils.installRPM(package,chrootID,noDeps,destLogPath)
        listInstalledPackages.append(package)

    def installDependentRunTimePackages(self,pkgUtils,package,chrootID,destLogPath,listInstalledPackages):
        listRunTimeDependentPackages=self.findRunTimeRequiredRPMPackages(package)
        if len(listRunTimeDependentPackages) != 0:
            for pkg in listRunTimeDependentPackages:
                if self.mapPackageToCycles.has_key(pkg) and pkg not in self.listAvailableCyclicPackages:
                    continue
                if pkg in listInstalledPackages:
                    continue
                self.installPackage(pkgUtils,pkg,chrootID,destLogPath,listInstalledPackages)