using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Practices.WizardFramework;
using Howest.Guidance.DataAccess.CodeGeneration.ObjectModel;
using SD = Howest.SchemaDiscovery.DbObjects;
using System.ComponentModel.Design;
using Howest.Guidance.DataAccess.CustomWizardPages.UI;
using Microsoft.Practices.RecipeFramework.Extensions.CommonHelpers;
using System.Windows.Forms.Design;
using System.Collections;

namespace Howest.Guidance.DataAccess.CustomWizardPages
{
	public partial class EntitiesSelectorCP : CustomWizardPage
	{
		const string DBO_SCHEMA = "dbo";
		private List<SD.Table> _tables;
		private List<SD.View> _views;

		public EntitiesSelectorCP(WizardForm parent)
			: base(parent)
		{
			InitializeComponent();
		}

		private ArrayList _entities;

		[RecipeArgument()]
		public ArrayList Entities
		{
			get { return _entities; }
			set { _entities = value; }
		}

		public override void OnActivated()
		{
			IDictionaryService dictservice = (IDictionaryService)GetService(typeof(IDictionaryService));

			if (dictservice.GetValue("Entities") == null)
			{
				trvEntities.Nodes.Clear();
			}

			GetTablesAndViews();
			LoadEntityTreeView();
			propBrowser.SelectedObject = null;
			SaveArgument();
			base.OnActivated();
		}

		#region Private Implementation

		private void GetTablesAndViews()
		{
			IDictionaryService dictservice = (IDictionaryService)GetService(typeof(IDictionaryService));
			ArrayList tabs = (ArrayList)dictservice.GetValue("Tables");
			ArrayList vws = (ArrayList)dictservice.GetValue("Views");
			SD.Table[] tabArray = (SD.Table[])tabs.ToArray(typeof(SD.Table));
			SD.View[] vwArray = (SD.View[])vws.ToArray(typeof(SD.View));
			_tables = new List<SD.Table>(tabArray);
			_views = new List<SD.View>(vwArray);
		}

		private void LoadEntityTreeView()
		{
			if (trvEntities.Nodes.Count > 0)
			{
				SyncEntities<SD.Table>(_tables);
				SyncEntities<SD.View>(_views);
			}
			else
			{
				List<Entity> entities = GetSelectedEntities();
				TreeNode parentNode = new TreeNode("Entities");
				foreach (Entity entity in entities)
				{
					EntityNode node = CreateEntityNode(entity);
					parentNode.Nodes.Add(node);
				}
				trvEntities.Nodes.Add(parentNode);
			}
			trvEntities.Sort();
		}
		
		private EntityNode CreateEntityNode(Entity entity)
		{
			EntityNode node = new EntityNode(entity);
			foreach (Property prop in entity.Properties)
			{
				node.Nodes.Add(new PropertyNode(prop));
			}
			return node;
		}

		private List<Entity> GetSelectedEntities()
		{
			List<Entity> entities = new List<Entity>();
			foreach (SD.Table table in _tables)
			{
				entities.Add(CreateEntity<SD.Table>(table));
			}
			foreach (SD.View view in _views)
			{
				entities.Add(CreateEntity<SD.View>(view));
			}
			return entities;
		}

		private Entity CreateEntity<T>(T dbObject) where T : SD.Table
		{
			Entity entity = new Entity();

			if (string.Compare(dbObject.Schema, DBO_SCHEMA, StringComparison.InvariantCultureIgnoreCase) == 0)
			{
				entity.Name = CodeDomHelper.GetValidIdentifier(dbObject.Name);
			}
			else
			{
				entity.Name = CodeDomHelper.GetValidIdentifier(dbObject.Schema + dbObject.Name);
			}

			entity.TableName = dbObject.Fullname;

			foreach (SD.Column column in dbObject.Columns)
			{
				Property property = new Property();
				property.Name = CodeDomHelper.GetValidIdentifier(column.Name);
				property.ColumnName = column.Name;
				property.Type = column.DbDataType.Type;
				property.IsNullable = column.IsNullable;
				property.IsPrimaryKey = column.IsPrimaryKey;

				entity.Properties.Add(property);
			}
			return entity;
		}

		private void SyncEntities<T>(List<T> list) where T : SD.Table
		{

			foreach (EntityNode entityNode in trvEntities.Nodes[0].Nodes)
			{
				Entity entity = (Entity)entityNode.Tag;

				T tableTemp = list.Find(delegate(T table)
				{
					return string.Compare(entity.TableName, table.Fullname, StringComparison.InvariantCultureIgnoreCase) == 0;
				});

				if (tableTemp != null)
				{
					foreach (PropertyNode propertyNode in entityNode.Nodes)
					{
						Property property = (Property)propertyNode.Tag;

						SD.Column col = tableTemp.Columns.Find(delegate(SD.Column columnTemp)
						{
							return string.Compare(property.ColumnName, columnTemp.Name, StringComparison.InvariantCultureIgnoreCase) == 0;
						});
						if (col == null)
						{
							propertyNode.Remove();
							entity.Properties.Remove(property);
						}

					}
					foreach (SD.Column columnTemp in tableTemp.Columns)
					{
						bool founded1 = false;

						foreach (PropertyNode propertyNode in entityNode.Nodes)
						{
							Property property = (Property)propertyNode.Tag;
							if (string.Compare(property.ColumnName, columnTemp.Name, StringComparison.InvariantCultureIgnoreCase) == 0)
							{
								founded1 = true;
								break;
							}
						}

						if (!founded1)
						{
							Property property = CreateProperty((SD.Column)columnTemp);
							entityNode.Nodes.Add(new PropertyNode(property));
							entity.Properties.Add(property);
						}
					}
				}
				else
				{
					entityNode.Remove();
				}
				//						founded = true;
				//						break;
			}

			foreach (SD.Table tableTemp in _tables)
			{
				bool founded = false;
				foreach (EntityNode node in trvEntities.Nodes[0].Nodes)
				{
					Entity entity = (Entity)node.Tag;
					if (string.Compare(entity.TableName, tableTemp.Fullname, StringComparison.InvariantCultureIgnoreCase) == 0)
					{
						founded = true;
						break;
					}
				}
				if (!founded)
				{
					Entity entity = CreateEntity<SD.Table>(tableTemp);
					EntityNode entityNode = CreateEntityNode(entity);
					trvEntities.Nodes[0].Nodes.Add(entityNode);
				}
			}

			//}
		}

		private Property CreateProperty(SD.Column column)
		{
			Property property = new Property();
			property.Name = column.Name;
			property.ColumnName = column.Name;
			property.Type = column.DbDataType.Type;
			property.IsNullable = column.IsNullable;
			property.IsPrimaryKey = column.IsPrimaryKey;

			return property;
		}

		private ArrayList GetEntities()
		{
			ArrayList entities = new ArrayList();

			foreach (EntityNode entityNode in trvEntities.Nodes[0].Nodes)
			{
				Entity entity = (Entity)entityNode.Tag;
				entities.Add(entity);
			}

			return entities;
		}

		private void SaveArgument()
		{
			_entities = GetEntities();
			IDictionaryService dictservice = (IDictionaryService)GetService(typeof(IDictionaryService));
			dictservice.SetValue("Entities", _entities);
		}

		private void RefreshProperties(object target)
		{
			propBrowser.SelectedObject = target;
		}

		private void RefreshNodes()
		{
			foreach (EntityNode entityNode in trvEntities.Nodes[0].Nodes)
			{
				entityNode.Text = ((Entity)entityNode.Tag).Name;

				foreach (PropertyNode propertyNode in entityNode.Nodes)
				{
					Property property = (Property)propertyNode.Tag;
					propertyNode.Text = property.Name;
				}
			}

			trvEntities.Sort();
		}

		#endregion

		#region Treeview events

		private void trvEntities_MouseLeave(object sender, EventArgs e)
		{
			SaveArgument();
		}

		private void trvEntities_Leave(object sender, EventArgs e)
		{
			SaveArgument();
		}

		private void trvEntities_AfterSelect(object sender, TreeViewEventArgs e)
		{
			RefreshProperties(e.Node.Tag);
		}

		private void trvEntities_KeyUp(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.F2)
			{
				trvEntities.LabelEdit = true;
				trvEntities.SelectedNode.BeginEdit();
			}
		}

		private void trvEntities_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
		{
			if (string.IsNullOrEmpty(e.Label))
			{
				e.CancelEdit = true;
				return;
			}

			if (e.Node is EntityNode)
			{
				try
				{
					((Entity)e.Node.Tag).Name = e.Node.Text = CodeDomHelper.ConvertToValidIdentifier(e.Label);
				}
				catch (ArgumentException ex)
				{
					IUIService uiService = (IUIService)this.GetService(typeof(IUIService));
					uiService.ShowError(ex);
				}
				RefreshProperties(e.Node.Tag);
			}
			else if (e.Node is PropertyNode)
			{
				try
				{
					((Property)e.Node.Tag).Name = e.Node.Text = CodeDomHelper.ConvertToValidIdentifier(e.Label);
				}
				catch (ArgumentException ex)
				{
					IUIService uiService = (IUIService)this.GetService(typeof(IUIService));
					uiService.ShowError(ex);
				}
				RefreshProperties(e.Node.Tag);
			}
			e.CancelEdit = true;
		}

		#endregion

		#region Property Browser Events

		private void propBrowser_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			RefreshNodes();
		}
		#endregion
	}
}