/** -*- Mode: C++; tab-width: 4 -*-
 * vim: sw=4 ts=4:
 *
 * Gnome Apt package tree
 *
 * 	(C) 1998 Havoc Pennington <hp@pobox.com>
 * 	    2002-2004 Filip Van Raemdonck <mechanix@debian.org>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 * 	$Id$
 *
 **/

using namespace std;

#include <algorithm>
#include <map>

#include <apt-pkg/configuration.h>
#include <apt-pkg/strutl.h>

#include "gadefs.h"
#include "pkgtree.h"
#include "pkgutil.h"

/**
 * Embedded GObject derived class, to attach signals to
 **/
static void gapt_object_class_init (GAptObjectClass*);
static void gapt_object_init (GAptObject*);

GType
gapt_object_get_type (void) {
	static GType gobj_type = 0;

	if (!gobj_type) {
		static const GTypeInfo gobj_info = {
			sizeof (GAptObjectClass), NULL, NULL,
			(GClassInitFunc) gapt_object_class_init,
			NULL, NULL, sizeof (GAptObject), 0,
			(GInstanceInitFunc) gapt_object_init
		};

		gobj_type = g_type_register_static (G_TYPE_OBJECT, "GAptObject", &gobj_info, (GTypeFlags) 0);
	}
	return gobj_type;
}

enum {
	GOBJ_MODEL_CHANGED, GOBJ_SELECTION_LOST, GOBJ_STATE_CHANGED, GOBJ_STATUS_CHANGED,
	GOBJ_LAST_SIGNAL
};
static guint gapt_object_signals[GOBJ_LAST_SIGNAL] = { 0 };

static void
gapt_object_class_init (GAptObjectClass* klass) {
	gapt_object_signals[GOBJ_MODEL_CHANGED] = g_signal_new (
	      "model-changed", G_TYPE_FROM_CLASS (klass),
	      GSignalFlags (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
	      G_STRUCT_OFFSET (GAptObjectClass, model_changed), NULL, NULL,
	      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
	gapt_object_signals[GOBJ_SELECTION_LOST] = g_signal_new (
	      "selection-lost", G_TYPE_FROM_CLASS (klass),
	      GSignalFlags (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
	      G_STRUCT_OFFSET (GAptObjectClass, selection_lost), NULL, NULL,
	      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
	gapt_object_signals[GOBJ_STATE_CHANGED] = g_signal_new (
	      "state-changed", G_TYPE_FROM_CLASS (klass),
	      GSignalFlags (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
	      G_STRUCT_OFFSET (GAptObjectClass, state_changed), NULL, NULL,
	      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
	gapt_object_signals[GOBJ_STATUS_CHANGED] = g_signal_new (
	      "status-changed", G_TYPE_FROM_CLASS (klass),
	      GSignalFlags (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
	      G_STRUCT_OFFSET (GAptObjectClass, status_changed), NULL, NULL,
	      g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
}

static void
gapt_object_init (GAptObject* obj) {
	/* Nothing to do */
}

GObject*
gapt_object_new (void) {
	return G_OBJECT (g_object_new (GAPT_OBJECT_TYPE, NULL));
}
/**
 * End Embedded GObject derived class, to attach signals to
 **/


///////////////////////////////////////////////
// Sort objects

typedef GAptPkgTree::Item * pred_target;

class NamePredicate  {
public:  
  bool operator() (pred_target a, pred_target b) {
    return (strcmp(a->name(),b->name()) < 0);
  }
	bool operator() (TreeNode* a, TreeNode* b) {
	  return operator() (dynamic_cast<pred_target>(a), dynamic_cast<pred_target>(b));
  }
    
} name_predicate;

class SectionPredicate  {
public:  
  bool operator() (pred_target a, pred_target b) {
    // apparently section can be 0 - we're going to say that 0 is greater 
    //  than any real string.
    const char* as = a->section();
    const char* bs = b->section();
    if (as && (bs == 0)) return true;// a < b 
    else if (as == 0) return false;      // a is >= b
    else if (bs == 0) return true;      // b > a
    else return (strcmp(as, bs) < 0);
  }
	bool operator() (TreeNode* a, TreeNode* b) {
	  return operator() (dynamic_cast<pred_target>(a), dynamic_cast<pred_target>(b));
  }

} section_predicate;

class PriorityPredicate  {
public:  
  bool operator() (pred_target a, pred_target b) {
    // This could be done with the enum, but it isn't quite as nice
    //  and the speed benefit is not user visible
    return (strcmp(a->priority(), b->priority()) < 0);
  }
	bool operator() (TreeNode* a, TreeNode* b) {
	  return operator() (dynamic_cast<pred_target>(a), dynamic_cast<pred_target>(b));
  }
} priority_predicate;


class StatusPredicate  {
public:  
  bool operator() (pred_target a, pred_target b) {
    return (a->status() < b->status());
  }
	bool operator() (TreeNode* a, TreeNode* b) {
	  return operator() (dynamic_cast<pred_target>(a), dynamic_cast<pred_target>(b));
  }
} status_predicate;

//// 

class FilterPredicate {
public:
  FilterPredicate(Filter* f) : filter_(f) {}

  bool operator() (pred_target a) {
    return a->filter(filter_);
  }  

	bool operator() (TreeNode* a) {
	  return operator() (dynamic_cast<pred_target>(a));
  }

private:
  Filter* filter_;

};


///////////////////// 
// GAptPkgTree

GAptPkgTree::GAptPkgTree (void) :
      cache_ (0), sort_ (SortNone), category_ (CategoryNone), filter_ (0) {
	cat_list = new Category (_("Software"), this);
	gapt_object = gapt_object_new();

	set_sort (SortAlpha);
	set_category (CategoryStatus);
}

GAptPkgTree::~GAptPkgTree()
{
  if (filter_) filter_->remove_view(this);

	delete cat_list;
}

void
GAptPkgTree::set_cache (GAptCache* cache) {
	cat_list->clear_nodes();

  cache_ = cache;

  if (cache_ == 0) return;

  update_status();

	create_category (category_);

	g_signal_emit (G_OBJECT (gaptObject()), gapt_object_signals[GOBJ_SELECTION_LOST], 0);
	g_signal_emit (G_OBJECT (gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
}

void
GAptPkgTree::filter_changed (void) {
	vector<TreeNode*>::iterator j = cat_list->begin();
	while (j != cat_list->end()) {
		Item* i = (Item*) (*j);
		if (!i->filter (filter_)) {
			i->hide();
		} else {
			i->show();
		}
		++j;
	}

	g_signal_emit (G_OBJECT (gapt_object), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
}

void 
GAptPkgTree::set_filter(Filter* f)
{
  if (filter_) {
    filter_->remove_view(this);
  }
  filter_ = f;
  if (filter_) {
    filter_->add_view(this);
  }
  filter_changed();
}

void 
GAptPkgTree::set_sort(SortType st)
{
  g_return_if_fail(st < SortTypeEnd);

  if (st == sort_) return;

  sort_ = st;

	TreeNode::iterator j = cat_list->begin();
	while (j != cat_list->end()) {
		Item* i = (Item*) (*j);
		i->sort (sort_);
		++j;
	}

	/* Needs STATE_CHANGED as the package children are sorted, too. */
	g_signal_emit (G_OBJECT (gapt_object), gapt_object_signals[GOBJ_STATE_CHANGED], 0);
}

void
GAptPkgTree::set_category (CategoryType ct, gboolean force) {
	g_return_if_fail (ct < CategoryTypeEnd);

	if (ct != category_ || force) {
		cat_list->clear_nodes();
		category_ = ct;
		if (cache_) {
			create_category (ct);
		}

		g_signal_emit (G_OBJECT (gaptObject()), gapt_object_signals[GOBJ_SELECTION_LOST], 0);
		g_signal_emit (G_OBJECT (gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
	}
}

static void
group_alphabetic (TreeNode* cat_list, pkgDepCache* cache, GAptPkgTree* tree) {
	map<char,GAptPkgTree::Category*> letters;

	pkgCache::PkgIterator i = cache->PkgBegin();
	while (!i.end()) {
		if (!i.VersionList()) {
			/* Virtual package, or non-existing one mentioned in a dependency. */
			++i;
			continue;
		}

		const gchar* pkgname = i.Name();
        gchar first = pkgname[0];
		if (!first) {
			++i;
          continue;
        }
        
        char upper = isalpha(first) ? toupper(first) : first;

		map<char,GAptPkgTree::Category*>::iterator cat = letters.find (upper);

		GAptPkgTree::Category* c = 0;
        if (cat == letters.end()) {
          char buf[2];
          buf[0] = upper;
          buf[1] = '\0';
			c = new GAptPkgTree::Category (buf, tree);
          letters[upper] = c;           // temporary quick tree
			cat_list->add_node (c);
			g_signal_emit (G_OBJECT (tree->gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
		} else {
          c = cat->second;
        }

		GAptPkgTree::Item* item = new GAptPkgTree::Pkg (GAptPkgTree::Item::PackageItem, i, tree, c);
		c->add_node (item);
		++i;
	}

	std::stable_sort ((vector<TreeNode*>::iterator) cat_list->begin(),
	      (vector<TreeNode*>::iterator) cat_list->end(), name_predicate);
}

static void
group_sectionwise (TreeNode* cat_list, pkgDepCache* cache, GAptPkgTree* tree) {
	map<string,GAptPkgTree::Category*> sections;
	GAptPkgTree::Category* nosection_bucket = 0;

	pkgCache::PkgIterator i = cache->PkgBegin();
	while (!i.end()) {
		if (!i.VersionList()) {
			/* Virtual package, or non-existing one mentioned in a dependency. */
			++i;
			continue;
		}

		GAptPkgTree::Category * c = 0;
		const gchar* section = i.Section();

		if (!section) {
			/* Shouldn't happen, but apparently can. */
			if (!nosection_bucket) {
				nosection_bucket = new GAptPkgTree::Category (_("no section"), tree);
				cat_list->add_node (nosection_bucket);
				g_signal_emit (G_OBJECT (tree->gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
			}
			c = nosection_bucket;
		} else {
			map<string,GAptPkgTree::Category*>::iterator sec = sections.find (string (section));

			if (sec == sections.end()) {
				c = new GAptPkgTree::Category (section, tree);
				sections[string (section)] = c;
				cat_list->add_node (c);
				g_signal_emit (G_OBJECT (tree->gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
			} else {
            c = sec->second;
          }
        }

		g_assert (c);
		GAptPkgTree::Item* item = new GAptPkgTree::Pkg (GAptPkgTree::Item::PackageItem, i, tree, c);
		c->add_node (item);

			++i;
		}

		std::stable_sort ((vector<TreeNode*>::iterator) cat_list->begin(),
		      (vector<TreeNode*>::iterator) cat_list->end(), section_predicate);
}

static void
group_prioritywise (TreeNode* cat_list, GAptCache* cache, GAptPkgTree* tree) {
	/* A map is overkill since there's only a few groups,
	   but hey, it works... (and scales) */
	map<pkgCache::State::VerPriority,GAptPkgTree::Category*> priorities;

	pkgCache::PkgIterator i = cache->PkgBegin();
	while (!i.end()) {
		if (!i.VersionList()) {
			/* Virtual package, or non-existing one mentioned in a dependency. */
			++i;
			continue;
		}

		GAptPkgTree::Category * c = 0;

		pkgCache::State::VerPriority priority;
		const gchar* pri_string = cache->priorityString (i, &priority);
		map<pkgCache::State::VerPriority,GAptPkgTree::Category*>::iterator pri = priorities.find (priority);

		if (pri == priorities.end()) {
			c = new GAptPkgTree::Category (pri_string, tree);
			priorities[priority] = c;
			cat_list->add_node (c);
			g_signal_emit (G_OBJECT (tree->gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
		} else {
          c = pri->second;
        }

		g_assert (c);
		GAptPkgTree::Item* item = new GAptPkgTree::Pkg (GAptPkgTree::Item::PackageItem, i, tree, c);
		c->add_node (item);

			++i;
		}
		std::stable_sort ((vector<TreeNode*>::iterator) cat_list->begin(),
		      (vector<TreeNode*>::iterator) cat_list->end(), priority_predicate);
}

static void
group_statuswise (TreeNode* cat_list, GAptCache* cache, GAptPkgTree* tree) {
	map<GAptCache::PkgStatusType,GAptPkgTree::Category*> statuses;

	pkgCache::PkgIterator i = cache->PkgBegin();
	while (!i.end()) {
		if (!i.VersionList()) {
			/* Virtual package, or non-existing one mentioned in a dependency. */
			++i;
			continue;
		}

		GAptPkgTree::Category* c = 0;
		GAptCache::PkgStatusType status = cache->pkgStatus (i);
		map<GAptCache::PkgStatusType,GAptPkgTree::Category*>::iterator sec = statuses.find (status);

		if (sec == statuses.end()) {
			c = new GAptPkgTree::Category (GAptCache::statusText (status), tree);
			statuses[status] = c;
			cat_list->add_node (c);
			g_signal_emit (G_OBJECT (tree->gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
		} else {
          c = sec->second;
        }

		g_assert (c);
		GAptPkgTree::Item* item = new GAptPkgTree::Pkg (GAptPkgTree::Item::PackageItem, i, tree, c);
		c->add_node (item);

			++i;
		}
		std::stable_sort ((vector<TreeNode*>::iterator) cat_list->begin(),
		      (vector<TreeNode*>::iterator) cat_list->end(), status_predicate);
}

static void
group_none (TreeNode* cat_list, pkgDepCache* cache, GAptPkgTree* tree) {
	/* CategoryNone _and_ default fall thru */
	GAptPkgTree::Category* c = new GAptPkgTree::Category (_("All Packages"), tree);
	cat_list->add_node (c);

	pkgCache::PkgIterator i = cache->PkgBegin();

	while (!i.end()) {
		if (!i.VersionList()) {
			/* Virtual package, or non-existing one mentioned in a dependency. */
			++i;
			continue;
		}

		GAptPkgTree::Item* item = new GAptPkgTree::Pkg (GAptPkgTree::Item::PackageItem, i, tree, c);
		c->add_node (item);

		++i;
	}

	g_signal_emit (G_OBJECT (tree->gaptObject()), gapt_object_signals[GOBJ_MODEL_CHANGED], 0);
}

void
GAptPkgTree::create_category (CategoryType ct) {
	g_assert (cache_);

	cat_list->clear_nodes();

	/* Callbacks would be a cleaner way...
	   (make them so we can loop once and call the right group cb) */
	if (ct == CategoryAlpha) {
		group_alphabetic (cat_list, cache_, this);
	} else if (ct == CategorySection) {
		group_sectionwise (cat_list, cache_, this);
	} else if (ct == CategoryPriority) {
		group_prioritywise (cat_list, cache_, this);
	} else if (ct == CategoryStatus) {
		group_statuswise (cat_list, cache_, this);
	} else {
		group_none (cat_list, cache_, this);
	}

  // Now sort the category using current sort
	vector<TreeNode*>::iterator j = cat_list->begin();
	while (j != cat_list->end()) {
		Item* it = (Item*) (*j);
		it->sort (sort_);
		++j;
	}

  // and filter
  filter_changed();
}

//////////////////////////////////////////////// Item

GAptPkgTree::Item::Item(ItemRelationshipType rel,
                        GAptPkgTree* t)
  : tree_(t),
    relation_(rel)
{

}

GAptPkgTree::Item::~Item()
{

}

void 
GAptPkgTree::Item::sort(GAptPkgTree::SortType st)
{
  if (children_.empty()) return; // minor optimization

	vector<TreeNode*>::iterator b = children_.begin();
	vector<TreeNode*>::iterator e = children_.end();

  switch (st) {
  case SortAlpha:
    std::stable_sort(b, e, name_predicate);
    break;

  case SortSection:
    std::stable_sort(b, e, section_predicate);
    break;

  case SortPriority:
    std::stable_sort(b, e, priority_predicate);
    break;

  case SortStatus:
    std::stable_sort(b, e, status_predicate);
    break;

	case SortNone:
	default:
		ga_debug (__FUNCTION__);
		break;
	}

	/* Sort child nodes as well */
	vector<TreeNode*>::iterator i = children_.begin();
	while (i != children_.end()) {
		Item* it = (Item*) (*i);
		it->sort (st);
		++i;
	}
}

void
GAptPkgTree::update_status (void) {
	if (cache_) {
    string s;
    char buf[100];
    g_snprintf(buf,100,_("%lu to install; "),
               cache_->InstCount());
    s += buf;
    g_snprintf(buf,100,_("%lu to delete; "),cache_->DelCount());
    s += buf;
    if (cache_->UsrSize() >= 0)
      g_snprintf(buf,100,_("%s will be used."), SizeToStr(cache_->UsrSize()).c_str());
    else
      g_snprintf(buf,100,_("%s will be freed."), SizeToStr(-1*cache_->UsrSize()).c_str());
    s += buf;

    if (cache_->BrokenCount() != 0)
      {
        g_snprintf(buf,100,_("  *** %lu broken packages ***"), cache_->BrokenCount());
        s += buf;
      }

		g_signal_emit (G_OBJECT (gapt_object), gapt_object_signals[GOBJ_STATUS_CHANGED], 0, s.c_str());
	}
}

///////////////////////////////////////// Category


GAptPkgTree::Category::Category(const char* name, GAptPkgTree* t)
  : Item(CategoryItem, t), name_(name), expanded_(false)
{

}

GAptPkgTree::Category::~Category()
{

}

void
GAptPkgTree::Category::expand()
{
  // we keep children around always
  expanded_ = true;
}

void
GAptPkgTree::Category::collapse()
{
  // we keep children around always
  expanded_ = false;
}

bool 
GAptPkgTree::Category::filter(Filter* filter)
{  
  if (filter == 0) return true;

	vector<TreeNode*>::iterator b = children_.begin();
	vector<TreeNode*>::iterator e = children_.end();

  FilterPredicate fp(filter);
  
  bool one_shown = false;

  while (b != e) {
    if (fp(*b)){
      (*b)->show();
      one_shown = true;
    }
    else (*b)->hide();
    ++b;
  }

  return one_shown;
}

////////////////////////////////////////////////// Pkg

static GAptPkgTree::Item::ItemRelationshipType
DepType_2_ItemRelationshipType(pkgCache::Dep::DepType dt)
{
  // This could maybe sped up with an array, but not as safe.
  //  compiler should make a jump table out of the switch
  switch (dt) {
  case pkgCache::Dep::Depends:
    return GAptPkgTree::Item::DependencyItem;
    break;
  case pkgCache::Dep::PreDepends:    
    return GAptPkgTree::Item::PreDependencyItem;
    break;
  case pkgCache::Dep::Suggests:
    return GAptPkgTree::Item::SuggestedItem;
    break;
  case pkgCache::Dep::Recommends:
    return GAptPkgTree::Item::RecommendedItem;
    break;
  case pkgCache::Dep::Conflicts:
    return GAptPkgTree::Item::ConflictingItem;
    break;
  case pkgCache::Dep::Replaces:
    return GAptPkgTree::Item::ReplacedItem;
    break;
  default:
    g_warning("Bad DepType %s", __FUNCTION__);
    return GAptPkgTree::Item::InvalidItem;
  }
}

GAptPkgTree::Pkg::Pkg(ItemRelationshipType rel,
                      pkgCache::Package* pkg, 
                      GAptPkgTree* t, 
                      Item* p)
  : Item(rel, t), pkg_(pkg), parent_(p)
{
  g_assert(tree_);
  g_assert(tree_->cache());
  
}

GAptPkgTree::Pkg::~Pkg()
{

}

static pkgCache::DepIterator
pkg_deps (pkgCache::PkgIterator& i, GAptCache* cache) {
	pkgDepCache::StateCache& state = (*cache)[i];
	pkgCache::VerIterator vi = state.InstVerIter (*cache);

	GAptCache::PkgStatusType status = cache->pkgStatus (i);
	if (status == GAptCache::StatusNotInstalled) {
		/* Not currently installed; show deps for the default version that would be installed */
		vi = state.CandidateVerIter (*cache);
	} else if (status == GAptCache::StatusDelete) {
/* FIXME */
		/* The InstallVersion is nothing, so we use the current version */
		vi = i.CurrentVer();
	}

	if (!vi.end()) {
		pkgCache::DepIterator di = vi.DependsList();
		return di;
	} else {
		pkgCache::DepIterator di;
		g_return_val_if_fail (di.end(), di);
		return di;	/* di.end() == true in this case */
	}
}

void
GAptPkgTree::Pkg::expand (void) {
  // Create our children
  
  // Avoid infinite travel around dependency graph
  // It would be much cleaner to save a flag, but we 
  //  are worried about RAM 
  if (parent_ &&           // have at least category...
      parent_->parent()) return;  // we appear to be a dependency...
  // need to allow another level for virtual packages provides

  pkgCache::PkgIterator pi = package(*(tree_->cache()));
  g_assert(!pi.end());

	pkgCache::DepIterator di = pkg_deps (pi, tree_->cache());
	while (!di.end()) {
    ItemRelationshipType irt = 
      DepType_2_ItemRelationshipType(static_cast<pkgCache::Dep::DepType>(di->Type));
    Pkg* np = new Pkg(irt, di.TargetPkg(), tree_, this);
    children_.push_back(np);
    ++di;
  }

  // sort children
  sort(tree_->sort());
  
  // FIXME, add provides also?
}

void
GAptPkgTree::Pkg::collapse()
{
  // Nuke children
	Nodes::iterator i = children_.begin();
	while (i != children_.end()) {
    delete *i;
    ++i;
  }
  children_.clear();
}


bool
GAptPkgTree::Pkg::expandable()
{
  // Eventually we'll expand to a another level (provides)
  if (parent_ &&           // have at least category...
      parent_->parent()) return false; // we appear to be a dependency...

	pkgCache::PkgIterator pi = package (*(tree_->cache()));

	return (!pkg_deps (pi, tree_->cache()).end());
}

// The problem is that if the installed version changes, we have to
// change the dependency list. This is majorly slowing down our life,
// but not much help for it.
void 
GAptPkgTree::Pkg::refresh_expansion()
{
  if (!children_.empty())
    {
      collapse();
      expand();
    }
}

const char*
GAptPkgTree::Pkg::priority()
{
  pkgCache::PkgIterator i = package(*(tree_->cache()));
  pkgCache::VerIterator vi = i.CurrentVer();
  if (!vi.end()) {
    return vi.PriorityType();
  }
  else return _("No current version");
}

GAptCache::PkgStatusType
GAptPkgTree::Pkg::status (void) {
  pkgCache::PkgIterator i = package(*(tree_->cache()));
	return tree_->cache()->pkgStatus (i);
}

bool 
GAptPkgTree::Pkg::filter(Filter* filter)
{
  if (filter == 0) return true;

  pkgCache::PkgIterator i = package(*(tree_->cache()));
  // if we aren't included, return false
  if (filter->include_package(i, tree_->cache()) == false) return false;
  else return true;
}

/*************
 * Tree Node *
 *************/

TreeNode::~TreeNode (void) {
	clear_nodes();
}

void
TreeNode::clear_nodes (void) {
	iterator i = children_.begin();
	while (i != children_.end()) {
		delete *i;
		++i;
	}

	children_.clear();
}

static void
check_orphaned (GAptPkgTree::Pkg* pkg) {
	pkgCache::PkgIterator i = pkg->package (*(pkg->tree()->cache()));
	pkgCache::DepIterator dep = i.RevDependsList();
	if (i.CurrentVer()) {
		while (!dep.end()) {
			if (dep.ParentPkg().CurrentVer()) return;
			dep++;
		}
		pkg->setOrphan (true);
	}
}

void
TreeNode::add_node (TreeNode* child) {
	if (((GAptPkgTree::Item*) child)->relation() != GAptPkgTree::Item::CategoryItem) {
		check_orphaned ((GAptPkgTree::Pkg*) child);
	}
	children_.push_back (child);
}
