# GLINT graphical package management
# Copyright (C) 1995 Red Hat, Inc
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import string
from package import *
from packageset import *

class Group:

    def selectAllPackages(self):
	self.packages.selectAll()
	for g in self.subgroups.values():
	    (g.selectAllPackages())

    def unselectAllPackages(self):
	self.packages.unselectAll()
	for g in self.subgroups.values():
	    (g.unselectAllPackages())

    # These methods maintaing information on selected packages in both
    # this groups and it's subgroups. Callbacks are used to allow others
    # to keep updated. It also keeps track of "redraw" callbacks, which
    # are set when a window is watching a group. Installs and uninstalls
    # add and remove packages asynchronously, so this is necessary

    def callRedrawCallback(self):
	for owner in self.redrawCallbacks.keys():
	    (cb, data) = self.redrawCallbacks[owner]
	    cb(self, data)

    def addRedrawCallback(self, cb, owner, data):
	self.redrawCallbacks[owner] = (cb, data)

    def removeRedrawCallback(self, owner):
	del self.redrawCallbacks[owner]

    def addSelectCallback(self, cb, owner, data):
	self.selectCallbacks[owner] = (cb, data)

    def removeSelectCallback(self, owner):
	del self.selectCallbacks[owner]

    def packageSelectCallback(self, selected, package):
	if (selected):
	    self.incrSelected(1, package.getSize())
	else:
	    self.incrSelected(-1, package.getSize())

    def incrSelected(self, d, size):
	self.childrenSelected = self.childrenSelected + d
	self.sizeSelected = self.sizeSelected + (d * size)
	if (not self.parent == None): self.parent.incrSelected(d, size)
	self.resetSelectedString()

    def resetSelectedString(self):
	for owner in self.selectCallbacks.keys():
	    (cb, data) = self.selectCallbacks[owner]
	    cb(self, data)

    # Convience functions to find out basic information about the packages

    def getSelectedSize(self):
	return self.sizeSelected

    def getPackageSet(self):
        return self.packages

    def topGroup(self):
	if (self.parent == None): return self
	return self.parent.topGroup()

    def getPackages(self):
        res = PackageSet()
	res.concat(self.packages)
	for g in self.subgroups.values():
	    res.concat(g.getPackages())
	return res

    def getSelectedPackages(self):
        res = PackageSet()
	for g in self.subgroups.values():
	    res.concat(g.getSelectedPackages())
	res.concat(self.packages.selectedPackages())
	return res

    def getSelectedString(self):
	if (self.childrenSelected == 0):
	    return ""
	elif (self.childrenSelected == self.children):
	    return "All"
	else:
	    return "%d" % (self.childrenSelected)

    def getNumberSelected(self):
	return self.childrenSelected

    def getSubgroups(self):
        return self.subgroups.values()

    def getName(self):
        return self.name

    def findSubgroup(self, name):
	for g in self.subgroups.values:
	    if (g.getName() == name):
		return g
	return None

    def getPath(self):
	return self.path

    def getParent(self):
        return self.parent

    def getRoot(self):
	if (self.root == None):
	    self.root = self.topGroup()
	return self.root

    # Initialization routines

    def addPackage(self, p):
	self.packages.addPackage(p)
	self.children = self.children + 1
	# when should this callback be removed? ***
	p.addSelectCallback(self.packageSelectCallback, self, p)

	if (p.isSelected()):
	    self.incrSelected(-1, p.getSize())

	self.callRedrawCallback()

    def addSubgroup(self, name, g):
	self.subgroups[name] = g
	self.children = self.children + g.children
	self.callRedrawCallback()

    def packageSetChange(self, pset, data, changes):
	for change in changes:
	    (dir, package) = change
	    pathList = string.splitfields(package.getGroup(), "/")
	    if (dir == '-'):
		self.removePackageWithPath(pathList, package)
	    else:
		self.addPackageWithPath(pathList, package)

    def removePackageWithPath(self, pathList, package):
	self.children = self.children - 1

	if (not len(pathList) or (len(pathList) == 1 and 
				  len(pathList[0]) == 0)):
	    # the package should be in this group
	    if (not self.packages.containsLabel(package.getLabel())):
		print "No package w/ label", package.getLabel(), "in this group!"
		return

	    if (package.isSelected()):
		self.incrSelected(-1, package.getSize())

	    self.packages.removePackage(package.getLabel())
	    self.callRedrawCallback()
	else:
	    subgroupName = pathList[0]
	    del pathList[0]

	    if (not self.subgroups.has_key(subgroupName)):
		print "No subgroup", subgroupName, "in", self.path
		print "You just found a bug in glint! Please report it\n"
		print "to support@redhat.com\n"
		return
	    
	    self.subgroups[subgroupName].removePackageWithPath(pathList, 
			package)
	    if (self.subgroups[subgroupName].empty()):
		del self.subgroups[subgroupName]
	        self.callRedrawCallback()

    def empty(self):
	return (self.packages.numPackages() == 0 and len(self.subgroups) == 0)

    def addPackageWithPath(self, pathList, package):
	if (not len(pathList) or (len(pathList) == 1 and 
				  len(pathList[0]) == 0)):
	    # add the package to this group

	    self.addPackage(package)
	else:
	    self.children = self.children + 1

	    subgroupName = pathList[0]
	    del pathList[0]

	    if (not self.subgroups.has_key(subgroupName)):
		if (len(self.path)):
		    g = Group(None, self.path + "/" + subgroupName, self)
		else:
		    g = Group(None, subgroupName, self)
		self.addSubgroup(subgroupName, g)

	    self.subgroups[subgroupName].addPackageWithPath(pathList, package)

    def initialize(self, allPackages, path):
	for package in allPackages.getPackages():
	    pathList = string.splitfields(package.getGroup(), "/")
	    self.addPackageWithPath(pathList, package)

    def __init__(self,  allPackages, path = "", parent = None):
	if (len(path) == 0):
	    self.name = ""
	else:
	    pathList = string.splitfields(path, "/")
	    self.name = pathList[len(pathList) - 1]
	    
	self.path = path
	self.packages = PackageSet()
	self.subgroups = {}          # An array of Group objects
	self.parent = parent         # the parent Group
	self.children = 0
	self.childrenSelected = 0
	self.sizeSelected = 0
	self.selectCallbacks = {}
	self.redrawCallbacks = {}
	self.root = None
	if (allPackages != None):
	    self.initialize(allPackages, path)
	    if (not self.parent):
		allPackages.addChangeCallback(self.packageSetChange, self, None)


