context: using System;
  using System.Collections;
  using System.Collections.Specialized;
  using System.ComponentModel;
  using System.Data.Bindings;
  using System.Data.Bindings.DebugInformation;
  using System.Data.Bindings.Cached;
  using System.Data.Bindings.Collections;
  using System.Runtime.InteropServices;
  
  namespace Gtk.DataBindings
  {
  	public class MappingsImplementor : CustomTreeModel
  	{
  		internal enum ModelChainType {
  			Normal = 0,
  			Sort = 1,
  			Filter = 2
  		}
  				
  		private bool[] modelActivity = new bool[3] {true, false, false};
  		private TreeModel[] models = new TreeModel[3] {null, null, null};
  		
  		private bool needsclearing = false;
  		private bool justcleared = false;
  		private GtkListAdaptor listadaptor = null; 
  		private object lastItems = null;
  
  		#region Filtering
  
  		protected TreeModelFilter FilterModel {
  			get { return ((TreeModelFilter) models[(int) ModelChainType.Filter]); }
  		}
  		
  		private bool showDeletedItems = true;
  		public bool ShowDeletedItems {
  			get { return (showDeletedItems); }
  			set {
  				if (showDeletedItems == value)
  					return;
  				bool oldfiltering = Filtering;
  				showDeletedItems = value;
  				if (oldfiltering != Filtering)
  					CheckModelChain();
  			}
  		}
  
  		private bool IsDeletedItemVisible (object aObject)
  		{
  			if (query == null)
  				return (false);
  			if ((query.HasDeletedItems == false) || ((query.HasDeletedItems == true) && (ShowDeletedItems == false)))
  				return (true);
  			return (query.IsItemDeleted(aObject) == false);
  		}
  		
  		public bool Filtering {
  			get { return ((isVisibleInFilter != null) || 
  				          ((query != null) && (query.HasDeletedItems == true))); }
  		}
  		
  		private event IsVisibleInFilterEvent isVisibleInFilter = null;
  		/// <summary>
  		/// Filtering event
  		/// </summary>
  		public event IsVisibleInFilterEvent IsVisibleInFilter {
  			add { 
  				bool wasnull = false;
  				if (isVisibleInFilter == null)
  					wasnull = true;
  				isVisibleInFilter += value; 
  				if ((wasnull == true) && (isVisibleInFilter != null))
  					CheckModelChain();
  			}
  			remove { 
  				bool wasnull = false;
  				if (isVisibleInFilter == null)
  					wasnull = true;
  				isVisibleInFilter -= value; 
  				if ((wasnull == false) && (isVisibleInFilter == null))
  					CheckModelChain();
  			}
  		}
  		
  		protected bool OnIsVisibleInFilter (object aObject)
  		{
  			if (isVisibleInFilter != null)
  				foreach (IsVisibleInFilterEvent method in isVisibleInFilter.GetInvocationList())
  					if (method(aObject) == false)
  						return (false);
  			return (true);
  		}
  		
  		private bool FilterTreeModel (Gtk.TreeModel aModel, Gtk.TreeIter aIter)
  		{
  //System.Console.WriteLine("Filter: Model {0}", aModel);
  //System.Console.WriteLine("filter?" + NodeFromIter(aIter));
  			if (isVisibleInFilter != null) {
  				object o = NodeFromIter (aIter);
  				return (OnIsVisibleInFilter (o));
  			}
  //System.Console.WriteLine("seems visible");
  			return (true);
  		}
  		#endregion Filtering
  		
  		#region PROPERTIES
  		private WeakReference owner = null;
  		public Gtk.Widget Owner {
  			get {
  				if (owner != null)
  					return (owner.Target as Gtk.Widget);
  				return (null);
  			}
  			internal set {
  				if (value == Owner)
  					return;
  				if (value == null)
  					owner = null;
  				else
  					if (value is Gtk.Widget)
  						owner = new WeakReference (value);
  					else
  						owner = null;
  			}
  		}
  
  		private bool EditingIsPossible {
  			get { 
  				return (TypeValidator.IsCompatible(Owner.GetType(), typeof(TreeView)) == true);
  			}
  		}
  		
  		private GtkAdaptor columnadaptor = null; 
  		/// <summary>
  		/// Resolves adaptor and it simplifies the simple string
  		/// </summary>
  		private IAdaptor ColumnAdaptor {
  			get {
  				if (columnadaptor == null)
  					columnadaptor = new GtkAdaptor();
  				// Enforce string values if type is compatible so it doesn't need to be specified
  				if (columnadaptor.Mappings == "")
  					if (ListItems != null)
  						if ((TypeValidator.IsCompatible(ListItems.GetType(), typeof(string[])) == true) ||
  						    (TypeValidator.IsCompatible(ListItems.GetType(), typeof(StringCollection)) == true) ||
  						    (TypeValidator.IsCompatible(ListItems.GetType(), typeof(ObserveableStringCollection)) == true))
  							return (TypeValidator.StringColumnAdaptor);
  				return (columnadaptor);
  			}
  		}
  
  		private TreeModelAdapter modeladapter = null;
  		protected TreeModelAdapter _ModelAdapter {
  			get { 
  				if (modeladapter == null) {
  					modeladapter = new TreeModelAdapter (this);
  					models[(int) ModelChainType.Normal] = modeladapter;
  				}
  				return (modeladapter); 
  			}
  		}
  
  		private TreeModel activeModel = null;
  		public TreeModel ActiveModel {
  			get { return (activeModel); }
  		}
  		
  		private bool[] multicolumns = null;
  		public bool[] MultiColumns {
  			get { return (multicolumns); }
  		}
  		
  		private CachedProperty[] propertyInfos = null;
  		public CachedProperty[] PropertyInfos {
  			get { return (propertyInfos); }
  		}
  		
  		private Type[] types = null;
  		public Type[] Types {
  			get { return (types); }
  		}
  		
  		private string[] names = null;
  		public string[] Names {
  			get { return (names); }
  		}
  		
  //		private System.Type listItemType = null;
  		public System.Type ListItemType {
  			get { 
  				if (ColumnAdaptor != null)
  					return (ColumnAdaptor.DataSourceType);
  				return (null);
  //				return (listItemType); 
  			}
  		}
  
  		private bool simpleMapping = false;
  		public bool SimpleMapping {
  			get { return (simpleMapping); }
  		}
  		
  		private bool respectHierarchy = true;
  		public bool RespectHierarchy {
  			get { return ((respectHierarchy == true) && (SimpleMapping == false)); }
  		}
  		
  		private QueryImplementor query = null;
  		internal QueryImplementor Query {
  			get { return (query); }
  		}
  		
  		private string mappings = "";
  		public string Mappings {
  			get { return (mappings); }
  			set { 
  				if (mappings == value)
  					return;
  				needsclearing = true;
  				_ClearTypeDescriptions();
  				mappings = value;
  				ColumnAdaptor.Mappings = value;
  				ProcessTypeDescriptions();
  			}
  		}
  
  		/// <summary>
  		/// DataSource object control is connected to
  		/// </summary>
  		[Browsable (false), Category ("Data Binding")]
  		public object ItemsDataSource {
  			get { return (listadaptor.Target); }
  			set { 
  				object check = ConnectionProvider.ResolveTargetForObject(value);
  				if (check != null)
  					if (ObserveableList.IsValidListStore(check) == false)
  						if (ModelSelector.QueryModelExists (check.GetType()) == false)
  							throw new ExceptionWrongListType (this, value);
  				needsclearing = true;
  				PrepareWidget();
  				if (clearSelection != null)
  					clearSelection();
  				_ClearTypeDescriptions();
  				isVisibleInFilter -= IsDeletedItemVisible;
  				if (query != null)
  					query.Disconnect();
  				
  				listadaptor.Target = value;
  				if (listadaptor.FinalTarget == null)
  					query = new NullTreeModel (this);
  				else {
  					query = ModelSelector.CreateModelFor (this, listadaptor.FinalTarget);
  					if (query == null) {
  						if (listadaptor.FinalTarget.GetType().IsArray == true)
  							query = new ArrayQueryModel(this);
  						else if (TypeValidator.IsCompatible(listadaptor.FinalTarget.GetType(), typeof(IList)) == true)
  							query = new IListTreeModel(this);
  						else
  							throw new NotSupportedException ("Type [{0}] is not in the list of supported query implementors");
  					}
  				}
  				// Connect visible filter
  				if (query != null)
  					if (query.HasDeletedItems == true)
  						isVisibleInFilter += IsDeletedItemVisible;
  			
  				CheckModelChain();
  				
  				ProcessTypeDescriptions();
  				if (Owner is IChangeableControl)
  					(Owner as IChangeableControl).Adaptor.CheckControlState();
  				check = null;
  				ResetWidgetModel();
  			}
  		}
  		
  		private object cachedItems = null;
  		/// <summary>
  		/// Provides access to IList with items for this control
  		/// </summary>
  		public virtual object ListItems {
  			get {
  				if (cachedItems != null)
  					return (cachedItems);
  				cachedItems = listadaptor.FinalTarget;
  				return (cachedItems);
  			}
  		}
  		
  		private NamedCellRendererList namedCellRenderers = new NamedCellRendererList();
  		/// <summary>
  		/// Provides a list of CellRenderers which can be accessed by name
  		/// Usage of these should go trough specifying mappings of this TreeView
  		/// where 
  		///    Property[Column Name]&lt;&gt;CellRendererName
  		/// is the rule of how and when to use them
  		/// </summary>
  		public NamedCellRendererList NamedCellRenderers {
  			get { return (namedCellRenderers); }
  		}
  		
  		private event ClearColumnsEvent clearColumns = null;
  		public event ClearColumnsEvent ClearColumns {
  			add { clearColumns += value; }
  			remove { clearColumns -= value; }
  		}
  		
  		private event ResetModelEvent resetModel = null;
  		public event ResetModelEvent ResetModel {
  			add { resetModel += value; }
  			remove { resetModel -= value; }
  		}
  		
  		private event CellDescriptionEvent cellDescription = null;
  		public event CellDescriptionEvent CellDescription {
  			add { cellDescription += value; }
  			remove { cellDescription -= value; }
  		}
  		
  		private event ListElementCellParamsEvent cellLayoutDescription = null;
  		public event ListElementCellParamsEvent CellLayoutDescription {
  			add { cellLayoutDescription += value; }
  			remove { cellLayoutDescription -= value; }
  		}
  		
  		internal void OnCellLayoutDescription (object aList, int[] aPath, object aObject, Gtk.CellRenderer aCell)
  		{
  			if (cellLayoutDescription != null)
  				cellLayoutDescription (aList, aPath, aObject, aCell);
  		}
  		
  		private event ClearSelectionEvent clearSelection = null;
  		public event ClearSelectionEvent ClearSelection {
  			add { clearSelection += value; }
  			remove { clearSelection -= value; }
  		}
  		
  		private event CheckControlEvent checkControl = null;
  		public event CheckControlEvent CheckControl {
  			add { checkControl += value; }
  			remove { checkControl -= value; }
  		}
  		#endregion PROPERTIES
  
  		#region WIDGET_MODEL
  		private void CheckModelChain()
  		{
  			if (ItemsDataSource == null)
  				return;
  			modelActivity[(int) ModelChainType.Filter] = Filtering;
  			if (models[(int) ModelChainType.Filter] == null) {
  				//System.Console.WriteLine("Creating filter for {0}", _ModelAdapter);
  				if (_ModelAdapter != null)
  					models[(int) ModelChainType.Filter] = new TreeModelFilter (models[(int) ModelChainType.Normal], null);
  				FilterModel.VisibleFunc = new TreeModelFilterVisibleFunc (FilterTreeModel);
  			}
  //			if (models[(int) ModelChainType.Sort] == null)
  //				models[(int) ModelChainType.Sort] = new TreeModelSort (models[(int) ModelChainType.Normal]);
  			if (ItemsDataSource == null)
  				activeModel = null;
  			else if (Filtering == true) 
  				activeModel = FilterModel;
  			else
  				activeModel = models[(int) ModelChainType.Normal];
  		}
  		
  /*		private void NullWidgetModel()
  		{
  			if (TypeValidator.IsCompatible(Owner.GetType(), typeof(TreeView)) == true)
  				NullTreeViewModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBox)) == true)
  				NullComboBoxModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBoxEntry)) == true)
  				NullComboBoxEntryModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(IconView)) == true)
  				NullIconViewModel();
  			else
  				throw new Exception ("Widget type " + Owner.GetType() + " is not yet handled in MappingsImplementor");
  		}*/
  		
  		private void ResetWidgetModel()
  		{
  			if (TypeValidator.IsCompatible(Owner.GetType(), typeof(TreeView)) == true)
  				ResetTreeViewModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBox)) == true)
  				ResetComboBoxModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBoxEntry)) == true)
  				ResetComboBoxEntryModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(IconView)) == true)
  				ResetIconViewModel();
  			else
  				throw new Exception ("Widget type " + Owner.GetType() + " is not yet handled in MappingsImplementor");
  		}
  		
  		private void ResetColumns()
  		{
  			if (TypeValidator.IsCompatible(Owner.GetType(), typeof(TreeView)) == true)
  				ResetTreeViewColumns();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBox)) == true)
  				ResetComboBoxColumns();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBoxEntry)) == true)
  				ResetComboBoxEntryColumns();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(IconView)) == true)
  				ResetIconViewColumns();
  			else
  				throw new Exception ("Widget type " + Owner.GetType() + " is not yet handled in MappingsImplementor");
  		}
  		
  		private void ResetWidget()
  		{
  			if (TypeValidator.IsCompatible(Owner.GetType(), typeof(TreeView)) == true)
  				ResetTreeView();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBox)) == true)
  				ResetComboBox();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBoxEntry)) == true)
  				ResetComboBoxEntry();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(IconView)) == true)
  				ResetIconView();
  			else
  				throw new Exception ("Widget type " + Owner.GetType() + " is not yet handled in MappingsImplementor");
  		}
  		
  		private void PrepareWidget()
  		{
  			if (TypeValidator.IsCompatible(Owner.GetType(), typeof(TreeView)) == true)
  				PrepareTreeView();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBox)) == true)
  				PrepareComboBox();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBoxEntry)) == true)
  				PrepareComboBoxEntry();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(IconView)) == true)
  				PrepareIconView();
  			else
  				throw new Exception ("Widget type " + Owner.GetType() + " is not yet handled in MappingsImplementor");
  		}
  
  		public void SetWidgetModel()
  		{
  			if (TypeValidator.IsCompatible(Owner.GetType(), typeof(TreeView)) == true)
  				SetTreeViewModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBox)) == true)
  				SetComboBoxModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(ComboBoxEntry)) == true)
  				SetComboBoxEntryModel();
  			else if (TypeValidator.IsCompatible(Owner.GetType(), typeof(IconView)) == true)
  				SetIconViewModel();
  			else
  				throw new Exception ("Widget type " + Owner.GetType() + " is not yet handled in MappingsImplementor");
  		}
  		#endregion WIDGET_MODEL
  		
  		#region CELL_EDITOR_METHODS
  
  		private static CachedProperty prp = new CachedProperty(null, "");// (Obj, item.MappedTo);
  
  		/// <summary>
  		/// Used to transfer data where multicolumn is happening
  		/// </summary>
  		/// <param name="aColumn">
  		/// Column in question <see cref="TreeViewColumn"/>
  		/// </param>
  		/// <param name="aCell">
  		/// CellRenderer associated with this cell <see cref="CellRenderer"/>
  		/// </param>
  		/// <param name="aModel">
  		/// TreeModel containing this cell <see cref="TreeModel"/>
  		/// </param>
  		/// <param name="aIter">
  		/// TreeIter associated with this cell <see cref="TreeIter"/>
  		/// </param>
  		private void RenderColumnFuncWithData (Gtk.TreeViewColumn aColumn, Gtk.CellRenderer aCell, 
  		                                       Gtk.TreeModel aModel, Gtk.TreeIter aIter)
  		{
  			if (aCell is IMappedColumnItem) {
  				IMappedColumnItem item = (IMappedColumnItem) aCell;
  				object Obj = aModel.GetValue (aIter, item.ColumnIndex);
  				// Commented out to try static cached property
  //				using (CachedProperty prp = new CachedProperty (Obj, item.MappedTo)) {
  					if (prp != null) {
  						prp.SetObject (Obj);
  						// using static cachedproperty this is needed
  						prp.SetProperty (item.MappedTo);
  						object currval;
  						prp.GetValue (out currval, null);
  						item.AssignValue (currval);
  					}
  					prp.Disconnect();
  //				}
  			}
  			else
  				Debug.Error ("TreeView.RenderColumnWithData()", "Didn't know what to assign data to.");
  			RenderColumnFunc (aColumn, aCell, aModel, aIter);
  		}
  		
  		/// <summary>
  		/// Decides wheter some Cell should be visible or not for iterators cell
  		/// </summary>
  		/// <param name="aColumn">
  		/// Column in question <see cref="TreeViewColumn"/>
  		/// </param>
  		/// <param name="aCell">
  		/// CellRenderer associated with this cell <see cref="CellRenderer"/>
  		/// </param>
  		/// <param name="aModel">
  		/// TreeModel containing this cell <see cref="TreeModel"/>
  		/// </param>
  		/// <param name="aIter">
  		/// TreeIter associated with this cell <see cref="TreeIter"/>
  		/// </param>
  		private void RenderColumnFunc (Gtk.TreeViewColumn aColumn, Gtk.CellRenderer aCell, 
  		                               Gtk.TreeModel aModel, Gtk.TreeIter aIter)
  		{
  /*			if (aCell is IMappedColumnItem) {
  				IMappedColumnItem cell = (IMappedColumnItem) aCell;
  				TreeIter iter = aIter;
  				if (aModel == FilterModel)
  					iter = FilterModel.ConvertIterToChildIter (aIter);
  				object obj = NodeFromIter (iter);
  				if (TypeValidator.IsCompatible(obj.GetType(), ListItemType) == false) {
  					bool vis = (ConnectionProvider.ResolveMappingProperty(obj, cell.MappedTo, false) != null);
  
gt: 					if ((vis == false) &&