//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.IO;
using System.Web.UI;
using CommunityServer.Components;
using CA = ComponentArt.Web.UI;
using System.Text.RegularExpressions;
using System.Collections;
using System.Text;
using System.Collections.Specialized;

namespace CommunityServer.Controls
{
	public class ContentSelector : TemplatedWebControl
	{
		#region AJAX properties

		protected string AjaxQueryStringKey
		{
			get
			{
				return "___" + this.ClientID;
			}
		}

		private StringCollection _selectedPath;
		protected StringCollection SelectedPath
		{
			get
			{
				if (_selectedPath == null && SelectedCombinedListKey != null && !Globals.IsNullorEmpty(SelectedCombinedListKey.Value))
				{
					IBrowseableList browseableList = this.GetBrowseableListFromCombinedListKey();
					if (browseableList != null)
					{
						if (CombinedListKeyComponents.Length == 2)
							_selectedPath = browseableList.GetPathToNode(CombinedListKeyComponents[1]);
						else
							_selectedPath = new StringCollection(); 
					}
				}

				return _selectedPath;
			}
		}

		private string _combinedListKey;
		protected string CombinedListKey
		{
			get
			{
				if (_combinedListKey == null)
				{
					_combinedListKey = Context.Request.QueryString[AjaxQueryStringKey];
					if (_combinedListKey == null)
						_combinedListKey = "";
				}

				return _combinedListKey;
			}
			set
			{
				_combinedListKey = value;
				_combinedListKeyComponents = null;
				_selectedPath = null;
			}
		}

		private string[] _combinedListKeyComponents = null;
		public string[] CombinedListKeyComponents
		{
			get
			{
				if (_combinedListKeyComponents == null)
				{
					_combinedListKeyComponents = CombinedListKey.Split('&');
					for (int i = 0; i < _combinedListKeyComponents.Length; i++)
					{
						_combinedListKeyComponents[i] = Context.Server.UrlDecode(_combinedListKeyComponents[i]);
					}
				}

				return _combinedListKeyComponents;
			}
		}

		protected string AjaxQueryStringValue = "{0}";
		protected string AjaxQueryStringValuePattern = "(\\d+):([\\w\\.-]*)";

		#endregion

		#region Public Properties

		public virtual int NumberOfItemListColumns
		{
			get
			{
				object state = ViewState["NumberOfItemListColumns"];
				if (state == null)
					return 3;
				else
					return (int) state;
			}
			set
			{
				ViewState["NumberOfItemListColumns"] = value;
			}
		}

		public virtual string ItemNameCssClass
		{
			get
			{
				string state = (string) ViewState["ItemNameCssClass"];
				if (state == null)
					return String.Empty;
				else
					return state;
			}
			set
			{
				ViewState["ItemNameCssClass"] = value;
			}
		}

		public virtual string SelectedItemNameCssClass
		{
			get
			{
				string state = (string) ViewState["SelectedItemNameCssClass"];
				if (state == null)
					return String.Empty;
				else
					return state;
			}
			set
			{
				ViewState["SelectedItemNameCssClass"] = value;
			}
		}

		public virtual string ItemCssClass
		{
			get
			{
				string state = (string) ViewState["ItemCssClass"];
				if (state == null)
					return String.Empty;
				else
					return state;
			}
			set
			{
				ViewState["ItemCssClass"] = value;
			}
		}

		public virtual string SelectedItemCssClass
		{
			get
			{
				string state = (string) ViewState["SelectedItemCssClass"];
				if (state == null)
					return String.Empty;
				else
					return state;
			}
			set
			{
				ViewState["SelectedItemCssClass"] = value;
			}
		}

		public virtual string ItemsLoadingHTML
		{
			get
			{
				string state = (string) ViewState["ItemsLoadingHTML"];
				if (state == null)
					return "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\" height=\"100%\"><tr><td align=\"center\" valign=\"middle\" width=\"100%\" height=\"100%\"><img src=\"" + Context.Response.ApplyAppPathModifier("~/utility/spinner.gif") + "\"></td></tr></table>";
				else
					return state;
			}
			set
			{
				ViewState["ItemsLoadingHTML"] = value;
			}
		}

		public virtual string ContentSelectedScript
		{
			get
			{
				string state = (string) ViewState["ContentSelectedScript"];
				if (state == null)
					return String.Empty;
				else
					return state;
			}
			set
			{
				ViewState["ContentSelectedScript"] = value;
			}
		}


		#endregion

		#region Child Controls

		private CA.TreeView Tree;
		private System.Web.UI.HtmlControls.HtmlGenericControl ItemsArea, OptionsArea;
		protected System.Web.UI.HtmlControls.HtmlInputHidden SelectedCombinedListKey;
		private IButton SelectButton;

		#endregion

		#region Skin

		protected override void AttachChildControls()
		{
			Tree = (CA.TreeView)FindControl("Tree");
			ItemsArea = (System.Web.UI.HtmlControls.HtmlGenericControl) FindControl("ItemsArea");
			OptionsArea = (System.Web.UI.HtmlControls.HtmlGenericControl) FindControl("OptionsArea");
			SelectedCombinedListKey = (System.Web.UI.HtmlControls.HtmlInputHidden) FindControl("SelectedCombinedListKey");
			SelectButton = FindButton("SelectButton");
		}

		#endregion

		#region Event Handlers

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
			AjaxManager.Register(this,"ContentSelectorAJAX");

			if (!Globals.IsNullorEmpty(CombinedListKey))
			{
				HandleXmlRequest();
			}
			else if(!Page.IsPostBack)
			{
				EnsureChildControls();
				BuildTree();
				SelectButton.Attributes.Add("onclick", string.Format("window.{0}.RenderItem(); return false;", this.ClientID));
				Head.AddTitle(ResourceManager.GetString("ContentSelector_Title"), Context);
			}
			else if (Page.IsPostBack)
			{
				EnsureChildControls();
//				string message = "";
//				if(SelectedCombinedListKey != null)
//					message = "got a hidden value" + SelectedCombinedListKey.Value;
//				this.Page.RegisterStartupScript("postbackScript", string.Format("<script language=\"javascript\">alert('Postback');alert('{0} : {1}');</script>", message, Tree.Nodes.Count));

				if (SelectedCombinedListKey != null && !Globals.IsNullorEmpty(SelectedCombinedListKey.Value))
					this.CombinedListKey = SelectedCombinedListKey.Value;

				BuildTree();
			}
		}

		protected override void OnPreRender(EventArgs e)
		{
			if (!this.Page.IsClientScriptBlockRegistered(this.GetType().FullName))
				this.Page.RegisterClientScriptBlock(this.GetType().FullName, string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>",this.ResolveUrl("~/utility/contentselector.js")));

			if (!this.Page.IsStartupScriptRegistered(this.ClientID))
				this.Page.RegisterStartupScript(this.ClientID, string.Format("<script language=\"javascript\">var {0} = new ContentSelector('{0}', '{1}', '{8}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{9}');</script>", this.ClientID, this.ItemsArea.ClientID, this.ItemCssClass, this.SelectedItemCssClass, this.ItemNameCssClass, this.SelectedItemNameCssClass, this.ItemsLoadingHTML, this.ContentSelectedScript.Replace("'", "\\'"), this.OptionsArea.ClientID, this.SelectedCombinedListKey.ClientID));

			if (!Globals.IsNullorEmpty(this.CombinedListKey) && !this.Page.IsStartupScriptRegistered(this.ClientID + "_GetItems"))
				Page.RegisterStartupScript(this.ClientID + "_GetItems", string.Format("<script language=\"javascript\">window.{0}.GetItems('{1}');</script>", ClientID, CombinedListKey));

			base.OnPreRender(e);
		}

		#endregion

		#region Tree Generation Methods

		private void BuildTree()
		{
			if (Tree == null)
				return;

			Tree.Nodes.Clear();

			CA.TreeViewNode node;
			String combinedListKey;
			foreach (IBrowseableList bl in BrowseableLists.Lists)
			{
				if (bl.Enabled)
				{
					node = new CA.TreeViewNode();
					node.Text = bl.Name;
					if (node.Text.Length > 30)
						node.Text = node.Text.Substring(0, 27) + "...";

					combinedListKey = MakeListKey(bl.GetType().FullName, "");
					node.ClientSideCommand = CreateNodeViewCommand(combinedListKey);

					Tree.Nodes.Add(node);

					if (bl.GetType().FullName == CombinedListKeyComponents[0])
					{
						node.Expanded = true;
						BuildTree(bl.GetType().FullName, bl.Containers, node.Nodes);
					}
					else
						node.ContentCallbackUrl = CreateNodeExpansionCallbackUrl(combinedListKey);

					if (combinedListKey == CombinedListKey)
						Tree.SelectedNode = node;
				}
			}
		}

		private void BuildTree(string browseableListType, ArrayList containers, CA.TreeViewNodeCollection nodes)
		{
			if (containers == null)
				return;

			CA.TreeViewNode node;
			string combinedListKey;
			foreach (IBrowseableListContainer blc in containers)
			{
				node = new CA.TreeViewNode();
				node.Text = blc.Name;
				if (node.Text.Length > 30)
					node.Text = node.Text.Substring(0, 27) + "...";

				combinedListKey = MakeListKey(browseableListType, blc.ListKey);
				node.ID = combinedListKey;
				node.ClientSideCommand = CreateNodeViewCommand(combinedListKey);

				nodes.Add(node);

				if (this.SelectedPath != null && this.SelectedPath.Contains(blc.ListKey))
				{
					node.Expanded = true;
					BuildTree(browseableListType, blc.Containers, node.Nodes);
				}
				else
					node.ContentCallbackUrl = CreateNodeExpansionCallbackUrl(combinedListKey);

				if (combinedListKey == CombinedListKey)
					Tree.SelectedNode = node;
			}
		}

		protected void HandleXmlRequest()
		{
			CA.TreeView tree = new CA.TreeView();
			object node = GetNodeFromCombinedListKey();
			if (node != null)
			{
				if (node is IBrowseableList)
					BuildTree(CombinedListKeyComponents[0], ((IBrowseableList) node).Containers, tree.Nodes);
				else if (node is IBrowseableListContainer)
					BuildTree(CombinedListKeyComponents[0], ((IBrowseableListContainer) node).Containers, tree.Nodes);
			}

			Page.Response.Clear();
			Page.Response.ContentType = "text/xml";
			Page.Response.Write(tree.GetXml());
			Page.Response.End();
		}
		
		[AjaxMethod(IncludeControlValuesWithCallBack=false)]
		public virtual string[] GetItemsListHTML(string combinedListKey)
		{
			this.CombinedListKey = combinedListKey;
			object node = GetNodeFromCombinedListKey();
			IBrowseableList browseableList = GetBrowseableListFromCombinedListKey();
			ArrayList items = null;
			if (node != null)
			{
				if (node is IBrowseableList)
					items = ((IBrowseableList) node).Items;
				else if (node is IBrowseableListContainer)
					items = ((IBrowseableListContainer) node).Items;
			}

			#region Content Generation

			StringBuilder content = new StringBuilder();
			if (items != null)
			{
				int colNum = 0;
				int widthPercent = 100 / this.NumberOfItemListColumns;
				content.Append("<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>");

				foreach (IBrowseableListItem item in items)
				{
					if (colNum == this.NumberOfItemListColumns)
					{
						content.Append("</tr><tr>");
						colNum = 0;
					}

					content.Append("<td onselectstart=\"return false;\" width=\"");
					content.Append(widthPercent.ToString());
					content.Append("%\" onclick=\"window.");
					content.Append(this.ClientID);
					content.Append(".SelectItem(this, '");
					content.Append(this.MakeListKey(CombinedListKeyComponents[0], item.ListKey));
					content.Append("');\" ondblclick=\"window.");
					content.Append(this.ClientID);
					content.Append(".SelectItem(this, '");
					content.Append(this.MakeListKey(CombinedListKeyComponents[0], item.ListKey));
					content.Append("'); window.");
					content.Append(this.ClientID);
					content.Append(".RenderItem();\" title=\"");
					content.Append(Context.Server.HtmlEncode(item.Name));
					content.Append("\"><div class=\"");
					content.Append(this.ItemCssClass);
					content.Append("\" align=\"center\" valign=\"middle\">");
					content.Append(item.PreviewHTML);
					content.Append("</div><div class=\"");
					content.Append(this.ItemNameCssClass);
					content.Append("\">");
					content.Append(Context.Server.HtmlEncode(item.Name));
					content.Append("</div></td>");

					colNum++;
				}

				content.Append("</tr></table>");
			}

			if (items == null || items.Count == 0)
			{
				content.Append(ResourceManager.GetString("ContentSelector_NoItems"));
			}

			#endregion

			#region Options Generation

			StringBuilder options = new StringBuilder();

            ArrayList itemOptions = browseableList.GetBrowseableListOptions();
			if (itemOptions != null && itemOptions.Count > 0)
			{
                options.Append("<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\"><tr><td>");

                options.Append("<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" width=\"100%\">");
                foreach (BrowseableListItemOption o in itemOptions)
				{
					if (o.HasSelectableOptions)
					{
						// render a select list
						options.Append("<tr><td class=\"CommonFormFieldName\">");
						options.Append(Page.Server.HtmlEncode(o.FieldName));
						options.Append("</td><td class=\"CommonFormField\"><select name=\"");
						options.Append(browseableList.GetType().FullName);
						options.Append("_");
						options.Append(o.FieldID);
						options.Append("\">");

						foreach (BrowseableListItemOptionSelectableValue v in o.SelectableOptions)
						{
							options.Append("<option value=\"");
							options.Append(Page.Server.HtmlEncode(v.Value));
							options.Append("\">");
							options.Append(Page.Server.HtmlEncode(v.Name));
							options.Append("</option>");
						}

						options.Append("</select></td></tr>");
					}
					else
					{
						switch (o.ValueType)
						{
							case BrowseableListItemOptionType.Int:
							case BrowseableListItemOptionType.Float:
							case BrowseableListItemOptionType.String:
							case BrowseableListItemOptionType.DateTime:
								// render a text box
								options.Append("<tr><td class=\"CommonFormFieldName\">");
								options.Append(Page.Server.HtmlEncode(o.FieldName));
								options.Append("</td><td class=\"CommonFormField\"><input type=\"text\" maxlength=\"255\" size=\"20\" name=\"");
								options.Append(browseableList.GetType().FullName);
								options.Append("_");
								options.Append(o.FieldID);
								options.Append("\" /></td></tr>");
								break;
						}
					}
	
				}

				options.Append("</table></td><td>");
                options.Append("<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" >");
				if (node is IBrowseableListContainer)
				{
				    IBrowseableListContainer blc = node as IBrowseableListContainer;
                    if (blc.AddItemUrl != null && blc.UserCanPost(CSContext.Current.User))
                    {
                        options.Append("<tr><td class=\"CommonFormFieldName\">");
                        options.Append("</td><td class=\"CommonFormField\">");
			            StringWriter sw = new StringWriter();
			            HtmlTextWriter tw = new HtmlTextWriter(sw);
                        blc.AddItemUrl.RenderControl(tw);
                        options.Append(sw.ToString());
                        options.Append("</td></tr>");
                    }
				}
                options.Append("</table></td></tr></table>");

			}
			else
			{
				options.Append(ResourceManager.GetString("ContentSelector_NoOptions"));
			}

			#endregion

			return new string[] {content.ToString(), options.ToString()};
		}

		[AjaxMethod(IncludeControlValuesWithCallBack=true)]
		public virtual string GetRenderHTML(string combinedListKey)
		{
			this.CombinedListKey = combinedListKey;
			IBrowseableListItem node = GetNodeFromCombinedListKey() as IBrowseableListItem;
			
			IBrowseableList browseableList = GetBrowseableListFromCombinedListKey();
			NameValueCollection optionValues = new NameValueCollection();
			ArrayList itemOptions = browseableList.GetBrowseableListOptions();
			if (itemOptions != null && itemOptions.Count > 0)
			{
				foreach (BrowseableListItemOption o in itemOptions)
				{
					string v = Page.Request.Form[browseableList.GetType().FullName + "_" + o.FieldID];
					if (v != null)
					{
						VerifyDataType(o.FieldName, o.ValueType, v);
						optionValues[o.FieldID] = v;
					}
				}
			}

			if (node != null)
				return node.GetRenderHTML(optionValues);
			else
				throw new IndexOutOfRangeException("The node could not be identified or is not of the appropriate type");				
		}

		#endregion

		#region Helper Methods

		public void VerifyDataType(string fieldName, BrowseableListItemOptionType type, string value)
		{
			switch (type)
			{
				case BrowseableListItemOptionType.Int:
					try
					{
						int.Parse(value);
					}
					catch
					{
						throw new InvalidCastException("The value for '" + fieldName + "' is not a valid integer.");
					}

					break;

				case BrowseableListItemOptionType.Float:
					try
					{
						float.Parse(value);
					}
					catch
					{
						throw new InvalidCastException("The value for '" + fieldName + "' is not a valid number.");
					}

					break;

				case BrowseableListItemOptionType.DateTime:
					try
					{
						DateTime.Parse(value);
					}
					catch
					{
						throw new InvalidCastException("The value for '" + fieldName + "' is not a valid date.");
					}

					break;
			}
		}

		protected virtual string MakeListKey(string browseableListType, string listKey)
		{
			if (Globals.IsNullorEmpty(listKey))
				return Context.Server.UrlEncode(browseableListType);
			else
				return Context.Server.UrlEncode(browseableListType) + "&" + Context.Server.UrlEncode(listKey);
		}

		protected virtual string CreateNodeExpansionCallbackUrl(string combinedListKey)
		{
			string value = Context.Server.UrlEncode(String.Format(AjaxQueryStringValue, combinedListKey));
			string url = CSContext.Current.CurrentUri.AbsoluteUri;

			if (url.IndexOf("?") == -1)
			{
				url += "?" + AjaxQueryStringKey + "=" + value;
			}
			else
			{
				if (url.IndexOf(AjaxQueryStringKey + "=") == -1)
					url += "&" + AjaxQueryStringKey + "=" + value;
				else
				{
					url = Regex.Replace(url, AjaxQueryStringKey + "=([^&]+)", AjaxQueryStringKey + "=" + value);
				}
			}

			return url;
		}

		protected virtual string CreateNodeViewCommand(string combinedListKey)
		{
			return string.Format("window.{0}.GetItems('{1}');", this.ClientID, combinedListKey.Replace("'", "\\'"));
		}

		protected object GetNodeFromCombinedListKey()
		{
			IBrowseableList browseableList = GetBrowseableListFromCombinedListKey();			
			if (browseableList != null)
			{
				if (CombinedListKeyComponents.Length == 2)
					return browseableList.GetNode(CombinedListKeyComponents[1]);
				else
					return browseableList;
			}
			else
				return null;
		}

		protected IBrowseableList GetBrowseableListFromCombinedListKey()
		{
			foreach (IBrowseableList bl in BrowseableLists.Lists)
			{
				if (bl.GetType().FullName == CombinedListKeyComponents[0])
					return bl;
			}

			return null;
		}


		#endregion

		public static string GetOpenScript(string setContentScript)
		{
			return GetOpenScript("", setContentScript);			
		}

		public static string GetOpenScript(string initializeScript, string setContentScript)
		{
			return "window.ContentSelectorCallbackFunction = new Function('content', '" + setContentScript.Replace("'", "\\'") + "');" + initializeScript + "; Telligent_Modal.Open('" + SiteUrls.Instance().ContentSelectorModal + "', 600, 420, null);";
		}

		public static new bool Enabled
		{
			get
			{
				foreach (IBrowseableList bl in BrowseableLists.Lists)
				{
					if (bl.Enabled)
						return true;
				}

				return false;
			}
		}
	}
}
