/** -*- Mode: C++; tab-width: 4 -*-
 * vim: sw=4 ts=4:
 *
 * Gnome Apt package tree
 *
 * 	(C) 1998 Havoc Pennington <hp@pobox.com>
 * 	    2002, 2003 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$
 *
 **/


#ifndef GNOME_APT_PKGTREE_H
#define GNOME_APT_PKGTREE_H

#include <vector>
#include <glib-object.h>

#include "cache.h"
#include "filter.h"

/**
 * Embedded GObject derived class, to attach signals to
 **/
G_BEGIN_DECLS
#define GAPT_OBJECT_TYPE	(gapt_object_get_type())
#define GAPT_OBJECT(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), GAPT_OBJECT_TYPE, GAptObject))
#define GAPT_OBJECT_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GAPT_OBJECT_TYPE, GAptObjectClass))
#define IS_GAPT_OBJECT(obj)	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAPT_OBJECT_TYPE))
#define IS_GAPT_OBJECT_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GAPT_OBJECT_TYPE))

typedef struct _GAptObject GAptObject;
typedef struct _GAptObjectClass GAptObjectClass;

struct _GAptObject {
	GObject obj;
};

struct _GAptObjectClass {
	GObjectClass parent_class;

	void (*model_changed) (GAptObject*);
	void (*selection_lost) (GAptObject*);
	void (*state_changed) (GAptObject*);
	void (*status_changed) (GAptObject*, const gchar*);
};

GType gapt_object_get_type (void);
GObject* gapt_object_new (void);
G_END_DECLS
/**
 * End Embedded GObject derived class, to attach signals to
 **/

class TreeNode;
typedef vector<TreeNode*> Nodes;

class TreeNode {
public:
	TreeNode (void) : hidden_ (false), _orphan (false) {}
	virtual ~TreeNode (void);

	typedef Nodes::iterator iterator;
	iterator begin (void) { return children_.begin(); }
	iterator end (void) { return children_.end(); }

	void add_node (TreeNode*);
	void clear_nodes (void);

	virtual TreeNode* parent (void) = 0;

	virtual void expand (void) = 0;	/* fill children_ here if necessary */
	virtual void collapse (void) = 0;	/* may delete children_ */
	virtual bool expanded (void) = 0;
	virtual bool expandable (void) = 0;
	virtual void refresh_expansion (void) = 0; /* update children_ if appropriate */

	bool hidden (void) { return hidden_; }
	void hide (void) { hidden_ = true; }
	void show (void) { hidden_ = false; }

	virtual pkgCache::Package* package (void) { return 0; }
	bool Orphaned (void) { return _orphan; }
	virtual void setOrphan (bool) { /* NOP */ }

	iterator cur;

protected:
	Nodes children_;
	guint hidden_ : 1;
	bool _orphan;
};

class GAptPkgTree : public GAptCacheFile::CacheView,
                    public Filter::View {
public:
  class Item;
  class Category;
  class Pkg;

  GAptPkgTree();
  virtual ~GAptPkgTree();

  GAptCache* cache() { return cache_; }

  void set_cache(GAptCache* cache);

	void filter_changed (void);
  void set_filter(Filter* f);
  Filter* filter() { return filter_; }

	/* We have toplevel categories, with packages sorted within these. */

	typedef enum {
		SortNone,
		SortAlpha, SortSection, SortStatus, SortPriority,
		SortSource, SortTypeEnd
	} SortType;

	typedef enum {
		CategoryNone,
		CategoryAlpha, CategorySection, CategoryStatus, CategoryPriority,
		CategorySource, CategoryTypeEnd
	} CategoryType;

  // Eventually we could allow setting the sort
  //  for individual categories, rather than the 
  //  whole list.
  void set_sort    (SortType st);
	void set_category (CategoryType, gboolean force = FALSE);

  CategoryType get_category() const { return category_; }
  SortType     get_sort() const { return sort_; }

  SortType sort() const { return sort_; }

	void update_status (void);

	GObject* gaptObject (void) { return gapt_object; }

	vector<TreeNode*>::iterator begin (void) { return cat_list->begin(); }
	vector<TreeNode*>::iterator end   (void) { return cat_list->end();   }

	TreeNode* root (void) { return cat_list; }

private:
  GAptCache* cache_;

  SortType sort_;
  CategoryType category_;

	TreeNode* cat_list;

  Filter* filter_;

	void create_category (CategoryType);
	GAptPkgTree (const GAptPkgTree&);

	GObject* gapt_object;
};

class GAptPkgTree::Item : public TreeNode {
public:

  typedef enum {
    CategoryItem,
    PackageItem,
    DependencyItem,
    PreDependencyItem,    
    RecommendedItem,
    SuggestedItem,
    ConflictingItem,
    ReplacedItem,
    InvalidItem
  } ItemRelationshipType;

  Item(ItemRelationshipType rel, GAptPkgTree* t);
  virtual ~Item();

	virtual TreeNode* parent (void) { return 0; }

  ItemRelationshipType relation() { return relation_; }
  GAptPkgTree* tree() { return tree_; }
  
  void sort(GAptPkgTree::SortType st);
  virtual bool filter(Filter* filter) = 0;

  // Stuff for sorting
  virtual const char* name() = 0;
  virtual const char* section() = 0;
  virtual const char* priority() = 0;
	virtual GAptCache::PkgStatusType status (void) = 0;

protected:
  GAptPkgTree* tree_;

  ItemRelationshipType relation_;
  
private:
  Item();
  Item(const Item &);
};

class GAptPkgTree::Category : public GAptPkgTree::Item {
private:
  const string name_;
  bool expanded_;
public:
  Category(const char* name, GAptPkgTree* t);
  virtual ~Category();

  virtual void expand();
  virtual void collapse();
  virtual bool expanded() { return expanded_; }
  virtual bool expandable() { return !children_.empty(); }
  virtual void refresh_expansion() {} // do nothing, hasn't changed

  virtual bool filter(Filter* f);

  // for the sort routines
  const char* name() { return name_.c_str(); }
  const char* section() { return name_.c_str(); } // has no section
  const char* priority() { return name_.c_str(); } // no priority
	GAptCache::PkgStatusType status (void) { return GAptCache::StatusTypeEnd; }
};

class GAptPkgTree::Pkg : public GAptPkgTree::Item {
private:
  pkgCache::Package* pkg_;
  Item* parent_;

public:
  Pkg(ItemRelationshipType rel,
      pkgCache::Package* pkg, 
      GAptPkgTree* t, Item* p);
  virtual ~Pkg();

  pkgCache::PkgIterator 
  package(pkgCache &cache) {
    return pkgCache::PkgIterator(cache,pkg_); // pkg_ == 0 is OK here
  };

	virtual TreeNode* parent (void) { return parent_; }

  virtual bool filter(Filter* f);

  virtual void expand();
  virtual void collapse();
  virtual bool expanded() { return !children_.empty(); } // only valid if expandable
  virtual bool expandable();
  virtual void refresh_expansion();

  // for the sort routines
  const char* name() { return package(*(tree_->cache())).Name(); }
  const char* section() { return package(*(tree_->cache())).Section(); }
  const char* priority();
	GAptCache::PkgStatusType status (void);

  // For finding a node by Package*
  bool match(pkgCache::Package* p) const {
    return p == pkg_;
  }

	virtual pkgCache::Package* package (void) { return pkg_; }
	virtual void setOrphan (bool orphan) { _orphan = orphan; }

};

#endif
