#!/usr/bin/python
import sys
import fnmatch

import rpm

sys.path.append("/usr/share/rhn/")
from up2date_client import config
from up2date_client import rpmUtils
from up2date_client import rhnChannel
from up2date_client import rpcServer
from up2date_client import up2dateLog
from up2date_client import repoDirector






class GenericSolveDep:
    def __init__(self):
	self.selectedPkgs = []
        pass

    def solveDep(self, unknowns,availList,
                 msgCallback = None,
                 progressCallback = None,
                 refreshCallback = None):
        self.cfg = config.initUp2dateConfig()
        self.log =  up2dateLog.initLog()
        retList = []
        self.log.log_me("solving dep for: %s" % unknowns)

        self.refreshCallback = refreshCallback
        self.progressCallback = progressCallback
        self.availList = availList
#        if not refreshCallback:
#            refreshCallback = 0
        
        self.retList = []
        self.getSolutions(unknowns)
        reslist = []


        #FIXME: this should be cached, I dont really need to query the db
        # for this everytime
        installedPkgList = rpmUtils.getInstalledPackageList(getArch=1)
        installedPkgHash = {}
        for pkg in installedPkgList:
            if installedPkgHash.has_key(pkg[0]):
                installedPkgHash[pkg[0]].append(pkg)
	    else:
            	installedPkgHash[pkg[0]] = [pkg]


        if len(self.retList):
            availList.sort()


            availListHash = {}
            for p in self.availList:
                if availListHash.has_key(tuple(p[:4])):
                    availListHash[tuple(p[:4])].append(p)
                else:
                    availListHash[tuple(p[:4])] = [p]


            newList = []
            availListNVRE = map(lambda p: p[:4], self.availList)

            failedDeps = []
            solutionPkgs = []
            pkgs = []
            for dep in self.retList.keys():
                # skip the rest if we didnt get a result
                if len(self.retList[dep]) == 0:
                    continue

                solutions = self.retList[dep]
                # fixme, grab the first package that satisfies the dep
                #   but make sure we match nvre against the list of avail packages
                #   so we grab the right version of the package
                # if we only get one soltution, use it. No point in jumping
                # though other hoops
                if len(solutions) == 1:
                    p = solutions[0]
                    if installedPkgHash.has_key(p[0]):
                        iList = installedPkgHash[p[0]]
                        for iPkg in iList:
                            if availListHash.has_key(tuple(p[:4])):
                                for i in  availListHash[tuple(p[:4])]:
                                    if self.cfg['forcedArch']:
                                        arches = self.cfg['forcedArch']
                                        if i[4] in arches:
                                            pkgs.append(i)
					    self.selectedPkgs.append(i)
                                    else:
					pkgs.append(i)
					self.selectedPkgs.append(i)

                    else:
                        # FIXME: add code to allow specifying arch
                        # pick the one of the approriate arch
                        if availListHash.has_key(tuple(p[:4])):
			    bestArchP = None
			    useNextBestArch = None
                            for i in  availListHash[tuple(p[:4])]:
                                if self.cfg['forcedArch']:
                                    arches = self.cfg['forcedArch']
                                    if i[4] in arches:
                                        bestArchP = i
                                else:
                                    bestArchP = None
                                    avail = availListHash[tuple(p[:4])]
                                    for i in avail:
                                        archScore  = rpm.archscore(i[4])
                                        if archScore < 0:
                                            continue
                                        if bestArchP and rpm.archscore(bestArchP[4]) <= archScore:
                                            # The currently best arch is better than (or the same as) 
                                            # this one
                                            continue
					if i in self.selectedPkgs:
					    useNextBestArch = 1
					    
				    	bestArchP = i
				    bestArchP2 = None
				    if useNextBestArch:
					avail = availListHash[tuple(p[:4])]
					for j in avail:
					    archScore  = rpm.archscore(j[4])
					    if archScore < 0:
                                            	continue
					    if j == bestArchP:
					        continue
					    if bestArchP2 and rpm.archscore(bestArchP[4]) <= archScore:
                                            # The currently best arch is better than (or the same as)
                                            # this one
                                            	continue
					    bestArchP2 = j
                            # if we dont have a workable arch
                            if bestArchP2:
                                pkgs.append(bestArchP2)
				self.selectedPkgs.append(bestArchP2)
			    elif bestArchP:
				pkgs.append(bestArchP)
				self.selectedPkgs.append(bestArchP)
                # we've got more than one possible solution, do some work
                # to figure out if I want one, some, or all of them
                elif len(solutions) > 1:
                    solutionsInstalled = []
                    # step 1, see if we have any of the suggest packages that
                    # solve this dep installed
                    for p in solutions:
                        if installedPkgHash.has_key(p[0]):
                            iList = installedPkgHash[p[0]]
                            for iPkg in iList:
                                if availListHash.has_key(tuple(iPkg[:4])):
                                    # find the avail packages as the same arch
                                    # as the installed ones
                                    for i in  availListHash[tuple(p[:4])]:
					solutionsInstalled.append(p)
                                #for i in  availListHash[tuple(p[:4])]:
                                 #   iList = installedPkgHash[
#                        else:
#                            print "++++++++++++++++++++++++++++++p[:4]: %s"  % p[:4]

                    # we have one or more of the possible solutions
                    # already installed, lets go with the newer version of
                    # it
                    found = 0
                    if len(solutionsInstalled):
                        for p in solutionsInstalled:
                            #print "\n\n\n\np: %s: \n\n\nn" % p
                            pkgs.append(p)
			    self.selectedPkgs.append(p)
                            found = 1
                        if found:
                            break


                    # we dont have any of possible solutions installed, pick one
                    else:
                        # this is where we could do all sort of heuristics to pick
                        # best one. For now, grab the first one in the list thats
                        # available

                        #FIXME: we need to arch score here for multilib/kernel
                        # packages that dont have a version installed

                        # This tends to happen a lot when isntalling into
                        # empty chroots (aka, pick which of the kernels to
                        # install).

                        # ie, this is the pure heuristic approach...
                        shortest = solutions[0]
                        for solution in solutions:
                            if len(shortest[0]) > len(solution[0]):
                                shortest = solution
                        if availListHash.has_key(tuple(shortest[:4])):
                            avail = availListHash[tuple(shortest[:4])]                            
                            bestArchP = None
			    useNextBestArch = None
                            for i in avail:
                                archScore  = rpm.archscore(i[4])
                                if archScore < 0:
                                    continue
                                if bestArchP and rpm.archscore(bestArchP[4]) <= archScore:
                                    # The currently best arch is better than (or the same as) 
                                    # this one
                                    continue
		
				if i in self.selectedPkgs:
				    useNextBestArch = 1
				bestArchP = i
                                bestArchP2 = None

                                if useNextBestArch:
                                    for j in avail:
                                        archScore  = rpm.archscore(j[4])
                                        if archScore < 0:
                                            continue
                                        if j == bestArchP:
                                            continue
                                        if bestArchP2 and rpm.archscore(bestArchP[4]) <= archScore:
                                            # The currently best arch is better than (or the same as)
                                            # this one
                                            continue
                                        bestArchP2 = j

                           # if we dont have a workable arch
                            if bestArchP2:
                                pkgs.append(bestArchP2)
				self.selectedPkgs.append(bestArchP2)
                            elif bestArchP:
                                pkgs.append(bestArchP)
				self.selectedPkgs.append(bestArchP)

#                            pkgs.append(bestArchP)
                        else:
                            keys = availListHash.keys()
                            keys.sort()
                            
                else:
                    # FIXME: in an ideal world, I could raise an exception here, but that will break the current gui
                    pkgs.append(p)                    
		    self.selectedPpkgs.append(p)
                    # raise UnsolvedDependencyError("Packages %s provide dep %s but are not available for install based on client config" % (pkgs,dep), dep, pkgs )

	    #print "pkgs: %s" % pkgs
            for pkg in pkgs:
                if pkg[:4] in availListNVRE:
                    newList.append(pkg)
                else:
                    newList.append(pkg)
            reslist = newList
        return reslist

class SolveByHeadersSolveDep(GenericSolveDep):
    def __init__(self):
        GenericSolveDep.__init__(self)

    def getHeader(self, pkg,
                  msgCallback = None,
                  progressCallback = None ):
        self.repos = repoDirector.initRepoDirector()
        hdr, type = rpcServer.doCall(self.repos.getHeader, pkg,
                                     msgCallback = msgCallback,
                                     progressCallback = progressCallback)
        return hdr
        
    def getSolutions(self, unknowns):
        channels = rhnChannel.getChannels()
        repoChannels = channels.getByType(self.type)
        repoPackages = []
        channelNames = []

        for channel in repoChannels:
            channelNames.append(channel['label'])

        for pkg in self.availList:
            if pkg[6] in channelNames:
                repoPackages.append(pkg)

        solutions = {}
        for pkg in repoPackages:
            hdr = self.getHeader(pkg)
            # this bit basically straight out of yum/pkgaction.py GPL Duke Univeristy 2002
            fullprovideslist = hdr[rpm.RPMTAG_PROVIDES]
            if hdr[rpm.RPMTAG_FILENAMES] != None:
                fullprovideslist = fullprovideslist + hdr[rpm.RPMTAG_FILENAMES]
            if hdr[rpm.RPMTAG_DIRNAMES] != None:
                fullprovideslist = fullprovideslist + hdr[rpm.RPMTAG_DIRNAMES]
            unknownsCopy = unknowns[:]
            for unknown in unknowns:
                for item in fullprovideslist:
                    if unknown == item:
                        if solutions.has_key(unknown):
                            solutions[unknown].append(pkg)
                        else:
                            solutions[unknown] = [pkg]
                        try:
                            unknownsCopy.remove(unknown)
                        except ValueError:
                            # already removed from list
                            pass
                        if len(unknownsCopy) == 0:
                            break
            del fullprovideslist
                

        self.retList = solutions


##class YumSolveDep(SolveByHeadersSolveDep):
##    def __init__(self):
##        SolveByHeadersSolveDep.__init__(self)
##        self.type = "yum"
        


class AptSolveDep(SolveByHeadersSolveDep):
    def __init__(self):
        SolveByHeadersSolveDep.__init__(self)
        self.type = "apt"

##class DirSolveDep(SolveByHeadersSolveDep):
##    def __init__(self):
##        SolveByHeadersSolveDep.__init__(self)
##        self.type = "dir"
