#undef DEMO

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Howest.SchemaDiscovery.DbObjects;

using VW = Howest.SchemaDiscovery.DbObjects.View;
using System.Diagnostics;
using Aga.Controls.Tree;
using System.Configuration;

namespace Howest.SchemaDiscovery.UI.Controls
{
	public partial class ObjectTree : UserControl
	{
		TreeModel _model;
		ObjectNode _tablesNode;
		ObjectNode _viewsNode;

		public ObjectTree()
		{
			InitializeComponent();
			_model = new TreeModel();
			tvw.Model = _model;
			DisplayTree();
		}

		#region TreeView plumbing
		
		void tvwCheckbox_CheckStateChanged(object sender, TreePathEventArgs e)
		{
			TreeNodeAdv node = tvw.FindNode(e.Path, true);
			CheckState state = (CheckState)tvwCheckbox.GetValue(node);
			if (state == CheckState.Indeterminate)
			{
				Debug.WriteLine("Undeterminate");
				tvwCheckbox.SetValue(node, CheckState.Unchecked);
				state = CheckState.Unchecked;
			}
			SetChildNodes(node, state);
			SetParentNode(node);
		}

		private void tvw_NodeMouseDoubleClick(object sender, TreeNodeAdvMouseEventArgs e)
		{
			TreeNodeAdv node = e.Node;
			if (node.Children.Count > 0)
				return;
			CheckState state = (CheckState)tvwCheckbox.GetValue(node);
			switch (state)
			{
				case CheckState.Checked:
					tvwCheckbox.SetValue(node, CheckState.Unchecked);
					break;
				case CheckState.Unchecked:
					tvwCheckbox.SetValue(node, CheckState.Checked);
					break;
				case CheckState.Indeterminate:
				default:
					Debug.WriteLine("Node state should not be indeterminate or illegal value");
					break;
			}
			SetParentNode(node);
		}

		void SetChildNodes(TreeNodeAdv node, CheckState state)
		{
			foreach (TreeNodeAdv subNode in node.Children)
			{
				tvwCheckbox.SetValue(subNode, state);
				SetChildNodes(subNode, state);
			}
		}

		void SetParentNode(TreeNodeAdv node)
		{
			TreeNodeAdv parent = node.Parent;
			if (parent != null)
			{
				CheckState parentState = VerifyChildren(parent);
				tvwCheckbox.SetValue(parent, parentState);
				SetParentNode(parent);
			}
		}

		CheckState VerifyChildren(TreeNodeAdv node)
		{
			int count = node.Children.Count;
			int index = 1;
			Debug.Assert(count > 0, "Checking parent without children");
			CheckState firstChildState = (CheckState)tvwCheckbox.GetValue(node.Children[0]);
			while (index < count)
			{
				CheckState current = (CheckState)tvwCheckbox.GetValue(node.Children[index]);
				if (current != firstChildState)
				{
					return CheckState.Indeterminate;
				}
				index++;
			} 
			return firstChildState;
		}

		#endregion

		#region Public Properties

		private ConnectionStringSettings _settings;

		public ConnectionStringSettings Settings
		{
			get { return _settings; }
			set
			{
				_settings = value;
				tvw.BeginUpdate();
				Cursor.Current = Cursors.WaitCursor;
				ClearTree();
				DisplayTree();
				Cursor.Current = Cursors.Default;
				tvw.EndUpdate();
			}
		}

		public List<Table> SelectedTables
		{
			get
			{
				List<Table> selectedTables = new List<Table>();
				if (_tablesNode != null)
				{
					TreePath path = _model.GetPath(_tablesNode);
					TreeNodeAdv tvwNode = tvw.FindNode(path, true);
					CheckState state = (CheckState)tvwCheckbox.GetValue(tvwNode);
					if (state != CheckState.Unchecked)
					{
						List<TreeNodeAdv> selectedTableNodes = GetSelectedChildNodes(tvwNode);
						foreach (TreeNodeAdv tabNode in selectedTableNodes)
						{
							Table tab = CloneTableWithoutColumns((Table)((ObjectNode)tabNode.Tag).Tag);
							tab.Columns.AddRange(GetSelectedColumns(tab, tabNode));
							selectedTables.Add(tab);
						}
					}
				}
				return selectedTables;
			}
		}

		public List<VW> SelectedViews
		{
			get
			{
				List<VW> selectedViews = new List<VW>();
				if (_viewsNode != null)
				{
					TreePath path = _model.GetPath(_viewsNode);
					TreeNodeAdv tvwNode = tvw.FindNode(path, true);
					CheckState state = (CheckState)tvwCheckbox.GetValue(tvwNode);
					if (state != CheckState.Unchecked)
					{
						List<TreeNodeAdv> selectedViewNodes = GetSelectedChildNodes(tvwNode);
						foreach (TreeNodeAdv vwNode in selectedViewNodes)
						{
							VW vw = CloneViewWithoutColumns((VW)((ObjectNode)vwNode.Tag).Tag);
							vw.Columns.AddRange(GetSelectedColumns(vw, vwNode));
							selectedViews.Add(vw);
						}
					}
				}
				return selectedViews;
			}
		}

		#endregion

		#region Private Implementation

		private void DisplayTree()
		{
			string databaseName = null;
			
#if !DEMO
			if (_settings != null)
			{
				DbSchemaDiscoverer discoverer = new DbSchemaDiscoverer(_settings);
				databaseName = discoverer.DatabaseName;
#endif

				if (string.IsNullOrEmpty(databaseName))
				{
					databaseName = "[Database]";
				}

				ObjectNode node = new ObjectNode(databaseName);
				node.Icon = imgTree.Images[0];
				_model.Nodes.Add(node);

				_tablesNode = new ObjectNode("Tables");
				_tablesNode.Icon = imgTree.Images[1];
				node.Nodes.Add(_tablesNode);
				// Enumerate tables
#if DEMO
				List<Table> tables = new List<Table>();
				Table t = new Table();
				t.Name = "Sometable";
				t.Fullname = "dbo.Sometable";
				t.Schema = "dbo";
				tables.Add(t);
				Column col = new Column(t);
				col.Name = "col_norm";
				t.Columns.Add(col);
				col = new Column(t);
				col.Name = "col_pk";
				col.IsPrimaryKey = true;
				t.Columns.Add(col);
				col = new Column(t);
				col.Name = "col_fk";
				col.IsForeignKey = true;
				t.Columns.Add(col);
#else
				List<Table> tables = discoverer.DiscoverTables();
#endif
				foreach (Table tab in tables)
				{
					string name = string.IsNullOrEmpty(tab.Fullname) ? tab.Name : tab.Fullname;
					ObjectNode tabNode = new ObjectNode(name);
					tabNode.Tag = tab;
					tabNode.Icon = imgTree.Images[2];
					_tablesNode.Nodes.Add(tabNode);
					FillColumns(tabNode, tab.Columns);
				}

				_viewsNode = new ObjectNode("Views");
				_viewsNode.Icon = imgTree.Images[1];
				node.Nodes.Add(_viewsNode);

#if DEMO
				List<VW> views = new List<VW>();
				VW v = new VW();
				v.Name = "someview";
				v.Schema = "dbo";
				v.Fullname = "dbo.someview";
				views.Add(v);
				col = new Column(v);
				col.Name = "col_norm";
				v.Columns.Add(col);
#else
				List<VW> views = discoverer.DiscoverViews();
#endif
				foreach (VW vw in views)
				{
					string name = string.IsNullOrEmpty(vw.Fullname) ? vw.Name : vw.Fullname;
					ObjectNode vwNode = new ObjectNode(name);
					vwNode.Tag = vw;
					vwNode.Icon = imgTree.Images[3];
					_viewsNode.Nodes.Add(vwNode);
					FillColumns(vwNode, vw.Columns);
				}
#if !DEMO
			}
			else
			{
				ObjectNode node = new ObjectNode("[No settings]");
				node.Icon = imgTree.Images[0];
				_model.Nodes.Add(node);
				_tablesNode = null;
				_viewsNode = null;
			}
#endif
		}

		private void FillColumns(ObjectNode node, List<Column> columns)
		{
			foreach (Column col in columns)
			{
				int imgIndex = col.IsPrimaryKey ? 5 : col.IsForeignKey ? 6 : 4;
				ObjectNode colNode = new ObjectNode(col.Name);
				colNode.Tag = col;
				colNode.Icon = imgTree.Images[imgIndex];
				node.Nodes.Add(colNode);
			}
		}

		private void ClearTree()
		{
			_model.Nodes.Clear();
		}


		private List<TreeNodeAdv> GetSelectedChildNodes(TreeNodeAdv node)
		{
			List<TreeNodeAdv> selectedNodes = new List<TreeNodeAdv>();
			foreach (TreeNodeAdv subNode in node.Children)
			{
				CheckState state = (CheckState)tvwCheckbox.GetValue(subNode);
				if (state != CheckState.Unchecked)
				{
					selectedNodes.Add(subNode);
				}
			}
			return selectedNodes;
		}

		private List<Column> GetSelectedColumns(Table tab, TreeNodeAdv node)
		{
			List<Column> columns = new List<Column>();
			List<TreeNodeAdv> selectedColumnNodes = GetSelectedChildNodes(node);
			foreach (TreeNodeAdv subNode in selectedColumnNodes)
			{
				columns.Add(CloneColumnToNewTable(tab, (Column)((ObjectNode)subNode.Tag).Tag));
			}
			return columns;
		}

		private Table CloneTableWithoutColumns(Table tab)
		{
			Table clone = new Table();
			clone.Name = tab.Name;
			clone.Schema = tab.Schema;
			clone.Fullname = tab.Fullname;
			return clone;
		}

		private VW CloneViewWithoutColumns(VW vw)
		{
			VW clone = new VW();
			clone.Name = vw.Name;
			clone.Schema = vw.Schema;
			clone.Fullname = vw.Fullname;
			return clone;
		}

		private Column CloneColumnToNewTable(Table tab, Column col)
		{
			Column clone = new Column(tab);
			clone.Name = col.Name;
			clone.DbDataType = col.DbDataType;
			clone.IsNullable = col.IsNullable;
			clone.IsReadOnly = col.IsReadOnly;
			clone.IsIdentity = col.IsIdentity;
			clone.IsPrimaryKey = col.IsPrimaryKey;
			clone.IsForeignKey = col.IsForeignKey;
			clone.IsUnique = col.IsUnique;
			return clone;
		}

		#endregion

		private void tvw_Leave(object sender, EventArgs e)
		{
			base.OnLeave(e);
		}

		private void tvw_MouseLeave(object sender, EventArgs e)
		{
			base.OnMouseLeave(e);
		}
	}
}
