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)