// TreeModelSample.cs - TreeModelSample application.
//
// Author: Mike Kestner  <mkestner@novell.com>
//
// Copyright (c) 2007 Novell, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the Lesser GNU General 
// Public License as published by the Free Software Foundation.
//
// 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser 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.

namespace GtkSamples {

	using System;
	using System.Collections;
	using System.Reflection;
	using System.Runtime.InteropServices;
	using Gtk;

	public class TreeModelDemo : Gtk.Window {

		public TreeModelDemo () : base ("TreeModel demo")
		{
			DefaultSize = new Gdk.Size (640,480);
			ScrolledWindow sw = new ScrolledWindow ();
			TreeView view = new TreeView (new TreeModelAdapter (new MyTreeModel ()));
			view.HeadersVisible = true;
			view.AppendColumn ("Name", new CellRendererText (), "text", 0);
			view.AppendColumn ("Type", new CellRendererText (), "text", 1);
			sw.Add (view);
			sw.ShowAll ();
			Add (sw);
		}

		protected override bool OnDeleteEvent (Gdk.Event ev)
		{
			Application.Quit ();
			return true;
		}

		public static void Main (string[] args)
		{
			Application.Init ();
			Gtk.Window win = new TreeModelDemo ();	
			win.Show ();
			Application.Run ();
		}

	}

	public class MyTreeModel : GLib.Object, ITreeModelImplementor {

		Assembly[] assemblies;

		public MyTreeModel ()
		{
			assemblies = AppDomain.CurrentDomain.GetAssemblies ();
		}

		object GetNodeAtPath (TreePath path)
		{
			if (path.Indices.Length > 0) {
				Assembly assm = assemblies [path.Indices [0]];
				if (path.Indices.Length > 1) {
					Type t = assm.GetTypes ()[path.Indices [1]];
					if (path.Indices.Length > 2)
						return t.GetMembers () [path.Indices [2]];
					else
						return t;
				} else
					return assm;
			} else
				return null;
		}

		Hashtable node_hash = new Hashtable ();

		public TreeModelFlags Flags {
			get {
				return TreeModelFlags.ItersPersist;
			}
		}

		public int NColumns {
			get {
				return 2;
			}
		}

		public GLib.GType GetColumnType (int col)
		{
			GLib.GType result = GLib.GType.String;
			return result;
		}

		TreeIter IterFromNode (object node)
		{
			GCHandle gch;
			if (node_hash [node] != null) {
				gch = (GCHandle) node_hash [node];
			} else {
				gch = GCHandle.Alloc (node);
				node_hash [node] = gch;
			}
			TreeIter result = TreeIter.Zero;
			result.UserData = (IntPtr) gch;
			return result;
		}

		object NodeFromIter (TreeIter iter)
		{
			GCHandle gch = (GCHandle) iter.UserData;
			return gch.Target;
		}

		TreePath PathFromNode (object node)
		{
			if (node == null)
				return new TreePath ();

			object work = node;
			TreePath path = new TreePath ();

			if ((work is MemberInfo) && !(work is Type)) {
				Type parent = (work as MemberInfo).ReflectedType;
				path.PrependIndex (Array.IndexOf (parent.GetMembers (), work));
				work = parent;
			}
				
			if (work is Type) {
				Assembly assm = (work as Type).Assembly;
				path.PrependIndex (Array.IndexOf (assm.GetTypes (), work));
				work = assm;
			}

			if (work is Assembly) {
				path.PrependIndex (Array.IndexOf (assemblies, work));
			}
				
			return path;
		}

		public bool GetIter (out TreeIter iter, TreePath path)
		{
			if (path == null)
				throw new ArgumentNullException ("path");

			iter = TreeIter.Zero;

			object node = GetNodeAtPath (path);
			if (node == null)
				return false;

			iter = IterFromNode (node);
			return true;
		}

		public TreePath GetPath (TreeIter iter)
		{
			object node = NodeFromIter (iter);
			if (node == null) 
				throw new ArgumentException ("iter");

			return PathFromNode (node);
		}

		public void GetValue (TreeIter iter, int col, ref GLib.Value val)
		{
			object node = NodeFromIter (iter);
			if (node == null)
				return;

			if (node is Assembly)
				val = new GLib.Value (col == 0 ? (node as Assembly).GetName ().Name : "Assembly");
			else if (node is Type)
				val = new GLib.Value (col == 0 ? (node as Type).Name : "Type");
			else
				val = new GLib.Value (col == 0 ? (node as MemberInfo).Name : "Member");
		}

		public bool IterNext (ref TreeIter iter)
		{
			object node = NodeFromIter (iter);
			if (node == null)
				return false;

			int idx;
			if (node is Assembly) {
				idx = Array.IndexOf (assemblies, node) + 1;
				if (idx < assemblies.Length) {
					iter = IterFromNode (assemblies [idx]);
					return true;
				}
			} else if (node is Type) {
				Type[] siblings = (node as Type).Assembly.GetTypes ();
				idx = Array.IndexOf (siblings, node) + 1;
				if (idx < siblings.Length) {
					iter = IterFromNode (siblings [idx]);
					return true;
				}
			} else {
				MemberInfo[] siblings = (node as MemberInfo).ReflectedType.GetMembers ();
				idx = Array.IndexOf (siblings, node) + 1;
				if (idx < siblings.Length) {
					iter = IterFromNode (siblings [idx]);
					return true;
				}
			}
			return false;
		}

		public bool IterPrevious (ref TreeIter iter)
		{
			object node = NodeFromIter (iter);
			if (node == null)
				return false;

			int idx;
			if (node is Assembly) {
				idx = Array.IndexOf (assemblies, node) - 1;
				if (idx >= 0) {
					iter = IterFromNode (assemblies [idx]);
					return true;
				}
			} else if (node is Type) {
				Type[] siblings = (node as Type).Assembly.GetTypes ();
				idx = Array.IndexOf (siblings, node) - 1;
				if (idx >= 0) {
					iter = IterFromNode (siblings [idx]);
					return true;
				}
			} else {
				MemberInfo[] siblings = (node as MemberInfo).ReflectedType.GetMembers ();
				idx = Array.IndexOf (siblings, node) - 1;
				if (idx >= 0) {
					iter = IterFromNode (siblings [idx]);
					return true;
				}
			}
			return false;
		}

		int ChildCount (object node)
		{
			if (node is Assembly)
				return (node as Assembly).GetTypes ().Length;
			else if (node is Type)
				return (node as Type).GetMembers ().Length;
			else
				return 0;
		}

		public bool IterChildren (out TreeIter child, TreeIter parent)
		{
			child = TreeIter.Zero;

			if (parent.UserData == IntPtr.Zero) {
				child = IterFromNode (assemblies [0]);
				return true;
			}

			object node = NodeFromIter (parent);
			if (node == null || ChildCount (node) <= 0)
				return false;

			if (node is Assembly)
				child = IterFromNode ((node as Assembly).GetTypes () [0]);
			else if (node is Type)
				child = IterFromNode ((node as Type).GetMembers () [0]);
			return true;
		}

		public bool IterHasChild (TreeIter iter)
		{
			object node = NodeFromIter (iter);
			if (node == null || ChildCount (node) <= 0)
					return false;

			return true;
		}

		public int IterNChildren (TreeIter iter)
		{
			if (iter.UserData == IntPtr.Zero)
				return assemblies.Length;
				
			object node = NodeFromIter (iter);
			if (node == null)
				return 0;

			return ChildCount (node);
		}

		public bool IterNthChild (out TreeIter child, TreeIter parent, int n)
		{
			child = TreeIter.Zero;

			if (parent.UserData == IntPtr.Zero) {
				if (assemblies.Length <= n)
					return false;
				child = IterFromNode (assemblies [n]);
				return true;
			}
				
			object node = NodeFromIter (parent);
			if (node == null || ChildCount (node) <= n)
				return false;

			if (node is Assembly)
				child = IterFromNode ((node as Assembly).GetTypes () [n]);
			else if (node is Type)
				child = IterFromNode ((node as Type).GetMembers () [n]);
			return true;
		}

		public bool IterParent (out TreeIter parent, TreeIter child)
		{
			parent = TreeIter.Zero;
			object node = NodeFromIter (child);
			if (node == null || node is Assembly)
				return false;

			if (node is Type)
				parent = IterFromNode ((node as Type).Assembly);
			else if (node is MemberInfo)
				parent = IterFromNode ((node as MemberInfo).ReflectedType);
			return true;
		}

		public void RefNode (TreeIter iter)
		{
		}

		public void UnrefNode (TreeIter iter)
		{
		}
	}
}
