import sys
import rpm
import os
from string import *
import types
import iutil
import urllib

XFreeServerPackages = { 'XFree86-3DLabs' : 1, 	'XFree86-8514' : 1,
			'XFree86-AGX' : 1, 	'XFree86-I128' : 1,
			'XFree86-Mach32' : 1, 	'XFree86-Mach64' : 1,
			'XFree86-Mach8' : 1, 	'XFree86-Mono' : 1,
			'XFree86-P9000' : 1, 	'XFree86-S3' : 1,
			'XFree86-S3V' : 1, 	'XFree86-SVGA' : 1,
			'XFree86-W32' : 1,	'XFree86-Sun' : 1,
			'XFree86-SunMono' : 1,	'XFree86-Sun24' : 1 }

class Package:
    def __getitem__(self, item):
	return self.h[item]

    def __repr__(self):
	return self.name

    def __init__(self, header):
	self.h = header
	self.name = header[rpm.RPMTAG_NAME]
	self.selected = 0

class HeaderList:
    def selected(self):
	l = []
 	keys = self.packages.keys()
	keys.sort()
	for name in keys:
	    if self.packages[name].selected: l.append(self.packages[name])
	return l

    def has_key(self, item):
	return self.packages.has_key(item)

    def keys(self):
        return self.packages.keys()

    def __getitem__(self, item):
	return self.packages[item]

    def __init__(self, hdlist):
        self.hdlist = hdlist
	self.packages = {}
	for h in hdlist:
	    name = h[rpm.RPMTAG_NAME]
	    score1 = rpm.archscore(h['arch'])
	    if (score1):
		if self.packages.has_key(name):
		    score2 = rpm.archscore(self.packages[name].h['arch'])
		    if (score1 < score2):
			self.packages[name] = Package(h)
		else:
		    self.packages[name] = Package(h)

class HeaderListFromFile (HeaderList):

    def __init__(self, path):
	hdlist = rpm.readHeaderListFromFile(path)
	HeaderList.__init__(self, hdlist)

class HeaderListFD (HeaderList):
    def __init__(self, fd):
	hdlist = rpm.readHeaderListFromFD (fd)
	HeaderList.__init__(self, hdlist)

class Component:
    def __len__(self):
	return len(self.items)

    def __getitem__(self, key):
	return self.items[key]

    def addPackage(self, package):
	self.items[package] = package

    def addConditional (self, condition, package):
        if self.conditional.has_key (condition):
            self.conditional[condition].append (package)
        else:
            self.conditional[condition] = [ package ]

    def addInclude(self, component):
	self.includes.append(component)
	
    def addRequires(self, component):
	self.requires = component

    def conditionalSelect (self, key):
        for pkg in self.conditional[key]:
            pkg.selected = 1

    def conditionalUnselect (self, key):
        for pkg in self.conditional[key]:
            pkg.selected = 0

    def select(self, recurse = 1):
        self.selected = 1
	for pkg in self.items.keys ():
	    self.items[pkg].selected = 1
        # turn on any conditional packages
        for (condition, pkgs) in self.conditional.items ():
            if condition.selected:
                for pkg in pkgs:
                    pkg.selected = 1

        # components that have conditional packages keyed on this
        # component AND are currently selected have those conditional
        # packages turned turned on when this component is turned on.
        if self.dependents:
            for dep in self.dependents:
                if dep.selected:
                    dep.conditionalSelect (self)
                
	if recurse:
	    for n in self.includes:
		if n.requires:
		    if n.requires.selected:
			n.select(recurse)
	        else:
		    n.select(recurse)
		if n.requires:
		    if n.requires.selected:
			n.select(recurse)
	        else:
		    n.select(recurse)

    def unselect(self, recurse = 1):
        self.selected = 0
	for n in self.items.keys ():
	    self.items[n].selected = 0

        # always turn off conditional packages, regardless
        # if the condition is met or not.
        for (condition, pkgs) in self.conditional.items ():
            for pkg in pkgs:
                pkg.selected = 0

        # now we must turn off conditional packages in components that
        # are keyed on this package
        if self.dependents:
            for dep in self.dependents:
                dep.conditionalUnselect (self)

	if recurse:
	    for n in self.includes:
		n.unselect(recurse)

    def __init__(self, name, selected, hidden = 0):
	self.name = name
	self.hidden = hidden
	self.selected = selected
	self.items = {}
        self.conditional = {}
        self.dependents = []
	self.requires = None
	self.includes = []

class ComponentSet:
    def __len__(self):
	return len(self.comps)

    def __getitem__(self, key):
	if (type(key) == types.IntType):
	    return self.comps[key]
	return self.compsDict[key]

    def keys(self):
	return self.compsDict.keys()

    def exprMatch(self, expr, arch1, arch2):
	if os.environ.has_key('LANG'):
	    lang = os.environ['LANG']
	else:
	    lang = None

	if expr[0] != '(':
	    raise ValueError, "leading ( expected"
	expr = expr[1:]
	if expr[len(expr) - 1] != ')':
	    raise ValueError, "bad kickstart file [missing )]"
	expr = expr[:len(expr) - 1]

	exprList = split(expr, 'and')
	truth = 1
	for expr in exprList:
	    l = split(expr)

	    if l[0] == "lang":
		if len(l) != 2:
		    raise ValueError, "too many arguments for lang"
		if l[1] != lang:
		    newTruth = 0
		else:
		    newTruth = 1
	    elif l[0] == "arch":
		if len(l) != 2:
		    raise ValueError, "too many arguments for arch"
		newTruth = self.archMatch(l[1], arch1, arch2)
	    else:
		s = "unknown condition type %s" % (l[0],)
		raise ValueError, s

	    truth = truth and newTruth

	return truth

    # this checks to see if "item" is one of the archs
    def archMatch(self, item, arch1, arch2):
	if item == arch1 or (arch2 and item == arch2): 
	    return 1
	return 0

    def readCompsFile(self, filename, packages):
	arch = iutil.getArch()
	arch2 = None
	if arch == "sparc" and os.uname ()[4] == "sparc64":
	    arch2 = "sparc64"
	file = urllib.urlopen(filename)
	lines = file.readlines()

	file.close()
	top = lines[0]
	lines = lines[1:]
	if (top != "3\n" and top != "4\n"):
	    raise TypeError, "comp file version 3 or 4 expected"
	
	comp = None
        conditional = None
	self.comps = []
	self.compsDict = {}
	for l in lines:
	    l = strip (l)
	    if (not l): continue

	    if (find(l, ":") > -1):
		(archList, l) = split(l, ":", 1)
		if archList[0] == '(':
		    l = strip(l)
		    if not self.exprMatch(archList, arch, arch2):
			continue
		else:
		    while (l[0] == " "): l = l[1:]

		    skipIfFound = 0
		    if (archList[0] == '!'):
			skipIfFound = 1
			archList = archList[1:]
		    archList = split(archList)
		    found = 0
		    for n in archList:
			if self.archMatch(n, arch, arch2):	
			    found = 1
			    break
		    if ((found and skipIfFound) or 
			(not found and not skipIfFound)):
			continue

	    if (find(l, "?") > -1):
                (trash, cond) = split (l, '?', 1)
                (cond, trash) = split (cond, '{', 1)
                conditional = self.compsDict[strip (cond)]
                continue

	    if (comp == None):
		(default, l) = split(l, None, 1)
		hidden = 0
		if (l[0:6] == "--hide"):
		    hidden = 1
		    (foo, l) = split(l, None, 1)
                (l, trash) = split(l, '{', 1)
                l = strip (l)
                if l == "Base":
                    hidden = 1
		comp = Component(l, default == '1', hidden)
	    elif (l == "}"):
                if conditional:
		    if comp.conditional.has_key (conditional):
			conditional.dependents.append (comp)
                    conditional = None
                else:
                    self.comps.append(comp)
                    self.compsDict[comp.name] = comp
                    comp = None
	    else:
		if (l[0] == "@"):
		    (at, l) = split(l, None, 1)
		    comp.addInclude(self.compsDict[l])
		else:
                    if conditional:
                        comp.addConditional (conditional, packages[l])
                    else:
                        comp.addPackage(packages[l])
                    
        everything = Component("Everything", 0, 0)
        for package in packages.keys ():
	    if (packages[package]['name'] != 'kernel' and
	        	packages[package]['name'] != 'kernel-BOOT' and
	        	packages[package]['name'] != 'kernel-smp' and
		  not XFreeServerPackages.has_key(packages[package]['name'])):
		everything.addPackage (packages[package])
        self.comps.append (everything)
        self.compsDict["Everything"] = everything
        
    def __repr__(self):
	s = ""
	for n in self.comps:
	    s = s + "{ " + n.name + " [";
	    for include in n.includes:
		s = s + " @" + include.name
		
	    for package in n:
		s = s + " " + package
	    s = s + " ] } "

	return s

    def __init__(self, file, hdlist):
	self.packages = hdlist
	self.readCompsFile(file, self.packages)
