//
// System.Web.UI.WebControls.SiteMapPath.cs
//
// Authors:
//	Lluis Sanchez Gual (lluis@novell.com)
//
// (C) 2005 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
//


using System;
using System.Collections;
using System.ComponentModel;
using System.Web.UI.HtmlControls;

namespace System.Web.UI.WebControls
{
	[Designer ("System.Web.UI.Design.WebControls.SiteMapPathDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
	public class SiteMapPath: CompositeControl
	{
		SiteMapProvider provider;
		
		Style currentNodeStyle;
		Style nodeStyle;
		Style pathSeparatorStyle;
		Style rootNodeStyle;
		
		ITemplate currentNodeTemplate;
		ITemplate nodeTemplate;
		ITemplate pathSeparatorTemplate;
		ITemplate rootNodeTemplate;

		static readonly object ItemCreatedEvent = new object();
		static readonly object ItemDataBoundEvent = new object();
		
		public event SiteMapNodeItemEventHandler ItemCreated {
			add { Events.AddHandler (ItemCreatedEvent, value); }
			remove { Events.RemoveHandler (ItemCreatedEvent, value); }
		}
		
		public event SiteMapNodeItemEventHandler ItemDataBound {
			add { Events.AddHandler (ItemDataBoundEvent, value); }
			remove { Events.RemoveHandler (ItemDataBoundEvent, value); }
		}
		
		protected virtual void OnItemCreated (SiteMapNodeItemEventArgs e)
		{
			if (Events != null) {
				SiteMapNodeItemEventHandler eh = (SiteMapNodeItemEventHandler) Events [ItemCreatedEvent];
				if (eh != null) eh (this, e);
			}
		}
		
		protected virtual void OnItemDataBound (SiteMapNodeItemEventArgs e)
		{
			if (Events != null) {
				SiteMapNodeItemEventHandler eh = (SiteMapNodeItemEventHandler) Events [ItemDataBoundEvent];
				if (eh != null) eh (this, e);
			}
		}
		
		[DefaultValueAttribute (null)]
		[DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
		[NotifyParentPropertyAttribute (true)]
		[PersistenceModeAttribute (PersistenceMode.InnerProperty)]
		public Style CurrentNodeStyle {
			get {
				if (currentNodeStyle == null) {
					currentNodeStyle = new Style ();
					if (IsTrackingViewState)
						((IStateManager)currentNodeStyle).TrackViewState ();
				}
				return currentNodeStyle;
			}
		}
	
		[DefaultValue (null)]
		[TemplateContainer (typeof(SiteMapNodeItem), BindingDirection.OneWay)]
		[PersistenceMode (PersistenceMode.InnerProperty)]
		[Browsable (false)]
		public virtual ITemplate CurrentNodeTemplate {
			get { return currentNodeTemplate; }
			set { currentNodeTemplate = value; UpdateControls (); }
		}
		
		[DefaultValueAttribute (null)]
		[DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
		[NotifyParentPropertyAttribute (true)]
		[PersistenceModeAttribute (PersistenceMode.InnerProperty)]
		public Style NodeStyle {
			get {
				if (nodeStyle == null) {
					nodeStyle = new Style ();
					if (IsTrackingViewState)
						((IStateManager)nodeStyle).TrackViewState ();
				}
				return nodeStyle;
			}
		}
	
		[DefaultValue (null)]
		[TemplateContainer (typeof(SiteMapNodeItem), BindingDirection.OneWay)]
		[PersistenceMode (PersistenceMode.InnerProperty)]
		[Browsable (false)]
		public virtual ITemplate NodeTemplate {
			get { return nodeTemplate; }
			set { nodeTemplate = value; UpdateControls (); }
		}
		
		[DefaultValueAttribute (-1)]
		[ThemeableAttribute (false)]
		public virtual int ParentLevelsDisplayed {
			get { return ViewState.GetInt ("ParentLevelsDisplayed", -1); }
			set {
				if (value < -1) throw new ArgumentOutOfRangeException ("value");
				ViewState ["ParentLevelsDisplayed"] = value;
				UpdateControls ();
			}
		}
		
		[DefaultValueAttribute (PathDirection.RootToCurrent)]
		public virtual PathDirection PathDirection {
			get { return (PathDirection)ViewState.GetInt ("PathDirection", (int)PathDirection.RootToCurrent); }
			set {
				if (value != PathDirection.RootToCurrent && value != PathDirection.CurrentToRoot)
					throw new ArgumentOutOfRangeException ("value");
				ViewState ["PathDirection"] = value;
				UpdateControls ();
			}
		}
		
		[DefaultValueAttribute (" > ")]
		[LocalizableAttribute (true)]
		public virtual string PathSeparator {
			get { return ViewState.GetString ("PathSeparator", " > "); }
			set {
				ViewState ["PathSeparator"] = value;
				UpdateControls ();
			}
		}
		
		[DefaultValueAttribute (null)]
		[DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
		[NotifyParentPropertyAttribute (true)]
		[PersistenceModeAttribute (PersistenceMode.InnerProperty)]
		public Style PathSeparatorStyle {
			get {
				if (pathSeparatorStyle == null) {
					pathSeparatorStyle = new Style ();
					if (IsTrackingViewState)
						((IStateManager)pathSeparatorStyle).TrackViewState ();
				}
				return pathSeparatorStyle;
			}
		}
	
		[DefaultValue (null)]
		[TemplateContainer (typeof(SiteMapNodeItem), BindingDirection.OneWay)]
		[PersistenceMode (PersistenceMode.InnerProperty)]
		[Browsable (false)]
		public virtual ITemplate PathSeparatorTemplate {
			get { return pathSeparatorTemplate; }
			set { pathSeparatorTemplate = value; UpdateControls (); }
		}
		
		[BrowsableAttribute (false)]
		[DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
		public SiteMapProvider Provider {
			get {
				if (provider == null) {
					if (this.SiteMapProvider.Length == 0) {
						provider = SiteMap.Provider;
						if (provider == null)
							throw new HttpException ("There is no default provider configured for the site.");
					} else {
						provider = SiteMap.Providers [this.SiteMapProvider];
						if (provider == null)
							throw new HttpException ("SiteMap provider '" + this.SiteMapProvider + "' not found.");
					}
				}
				return provider;
			}
			set {
				provider = value;
				UpdateControls ();
			}
		}
		
		[DefaultValueAttribute (false)]
		public virtual bool RenderCurrentNodeAsLink {
			get { return ViewState.GetBool ("RenderCurrentNodeAsLink", false); }
			set {
				ViewState ["RenderCurrentNodeAsLink"] = value;
				UpdateControls ();
			}
		}
		
		[DefaultValueAttribute (null)]
		[DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
		[NotifyParentPropertyAttribute (true)]
		[PersistenceModeAttribute (PersistenceMode.InnerProperty)]
		public Style RootNodeStyle {
			get {
				if (rootNodeStyle == null) {
					rootNodeStyle = new Style ();
					if (IsTrackingViewState)
						((IStateManager)rootNodeStyle).TrackViewState ();
				}
				return rootNodeStyle;
			}
		}
	
		[DefaultValue (null)]
		[TemplateContainer (typeof(SiteMapNodeItem), BindingDirection.OneWay)]
		[PersistenceMode (PersistenceMode.InnerProperty)]
		[Browsable (false)]
		public virtual ITemplate RootNodeTemplate {
			get { return rootNodeTemplate; }
			set { rootNodeTemplate = value; UpdateControls (); }
		}
		
		[DefaultValueAttribute (true)]
		[ThemeableAttribute (false)]
		public virtual bool ShowToolTips {
			get { return ViewState.GetBool ("ShowToolTips", true); }
			set {
				ViewState ["ShowToolTips"] = value;
				UpdateControls ();
			}
		}
		
		[DefaultValueAttribute ("")]
		[ThemeableAttribute (false)]
		public virtual string SiteMapProvider {
			get { return ViewState.GetString ("SiteMapProvider", ""); }
			set {
				ViewState ["SiteMapProvider"] = value;
				UpdateControls ();
			}
		}

		[Localizable (true)]
		public virtual string SkipLinkText 
		{
			get { return ViewState.GetString ("SkipLinkText", "Skip Navigation Links"); }
			set { ViewState["SkipLinkText"] = value; }
		}
		
		
		void UpdateControls ()
		{
			ChildControlsCreated = false;
		}

		public override void DataBind ()
		{
			base.DataBind ();

			/* the child controls get bound in
			 * base.DataBind */
			foreach (Control c in Controls) {
				if (c is SiteMapNodeItem) {
					SiteMapNodeItem it = (SiteMapNodeItem)c;
					OnItemDataBound (new SiteMapNodeItemEventArgs (it));
				}
			}
		}
		
		protected internal override void CreateChildControls ()
		{
			Controls.Clear ();
			CreateControlHierarchy ();
			DataBind ();
		}

		protected virtual void CreateControlHierarchy ()
		{
			ArrayList nodes = new ArrayList ();
			SiteMapNode node = Provider.CurrentNode;
			if (node == null) return;
			
			int levels = ParentLevelsDisplayed != -1 ? ParentLevelsDisplayed + 1 : int.MaxValue;
			
			while (node != null && levels > 0) {
				if (nodes.Count > 0) {
					SiteMapNodeItem sep = new SiteMapNodeItem (nodes.Count, SiteMapNodeItemType.PathSeparator);
					InitializeItem (sep);
					SiteMapNodeItemEventArgs sargs = new SiteMapNodeItemEventArgs (sep);
					OnItemCreated (sargs);
					nodes.Add (sep);
				}

				SiteMapNodeItemType nt;
				if (nodes.Count == 0)
					nt = SiteMapNodeItemType.Current;
				else if (node.ParentNode == null)
					nt = SiteMapNodeItemType.Root;
				else
					nt = SiteMapNodeItemType.Parent;
					
				SiteMapNodeItem it = new SiteMapNodeItem (nodes.Count, nt);
				it.SiteMapNode = node;
				InitializeItem (it);
				
				SiteMapNodeItemEventArgs args = new SiteMapNodeItemEventArgs (it);
				OnItemCreated (args);
				
				nodes.Add (it);
				node = node.ParentNode;
				levels--;
			}
			
			if (PathDirection == PathDirection.RootToCurrent) {
				for (int n=nodes.Count - 1; n>=0; n--)
					Controls.Add ((Control)nodes[n]);
			} else {
				for (int n=0; n<nodes.Count; n++)
					Controls.Add ((Control)nodes[n]);
			}
		}
		
		protected virtual void InitializeItem (SiteMapNodeItem item)
		{
			switch (item.ItemType) {
				case SiteMapNodeItemType.Root:
					if (RootNodeTemplate != null) {
						item.ApplyStyle (NodeStyle);
						item.ApplyStyle (RootNodeStyle);
						RootNodeTemplate.InstantiateIn (item);
					}
					else if (NodeTemplate != null) {
						item.ApplyStyle (NodeStyle);
						item.ApplyStyle (RootNodeStyle);
						NodeTemplate.InstantiateIn (item);
					}
					else {
						WebControl c = CreateHyperLink (item);
						c.ApplyStyle (NodeStyle);
						c.ApplyStyle (RootNodeStyle);
						item.Controls.Add (c);
					}
					break;

				case SiteMapNodeItemType.Current:
					if (CurrentNodeTemplate != null) {
						item.ApplyStyle (NodeStyle);
						item.ApplyStyle (CurrentNodeStyle);
						CurrentNodeTemplate.InstantiateIn (item);
					}
					else if (NodeTemplate != null) {
						item.ApplyStyle (NodeStyle);
						item.ApplyStyle (CurrentNodeStyle);
						NodeTemplate.InstantiateIn (item);
					} else if (RenderCurrentNodeAsLink) {
						HyperLink c = CreateHyperLink (item);
						c.ApplyStyle (NodeStyle);
						c.ApplyStyle (CurrentNodeStyle);
						item.Controls.Add (c);
					} else {
						Literal c = CreateLiteral (item);
						item.ApplyStyle (NodeStyle);
						item.ApplyStyle (CurrentNodeStyle);
						item.Controls.Add (c);
					}
					break;
					
				case SiteMapNodeItemType.Parent:
					if (NodeTemplate != null) {
						item.ApplyStyle (NodeStyle);
						NodeTemplate.InstantiateIn (item);
					}
					else {
						WebControl c = CreateHyperLink (item);
						c.ApplyStyle (NodeStyle);
						item.Controls.Add (c);
					}
					break;
					
				case SiteMapNodeItemType.PathSeparator:
					if (PathSeparatorTemplate != null) {
						item.ApplyStyle (PathSeparatorStyle);
						PathSeparatorTemplate.InstantiateIn (item);
					} else {
						Literal h = new Literal ();
						h.Text = HttpUtility.HtmlEncode (PathSeparator);
						item.ApplyStyle (PathSeparatorStyle);
						item.Controls.Add (h);
					}
					break;
			}
		}
		
		HyperLink CreateHyperLink (SiteMapNodeItem item)
		{
			HyperLink h = new HyperLink ();
			h.Text = item.SiteMapNode.Title;
			h.NavigateUrl = item.SiteMapNode.Url;
			if (ShowToolTips)
				h.ToolTip = item.SiteMapNode.Description;
			return h;
		}

		Literal CreateLiteral (SiteMapNodeItem item)
		{
			Literal h = new Literal ();
			h.Text = item.SiteMapNode.Title;
			return h;
		}
		
		protected override void LoadViewState (object savedState)
		{
			if (savedState == null) {
				base.LoadViewState (null);
				return;
			}
			
			object[] states = (object[]) savedState;
			base.LoadViewState (states [0]);
			
			if (states[1] != null) ((IStateManager)CurrentNodeStyle).LoadViewState (states[1]);
			if (states[2] != null) ((IStateManager)NodeStyle).LoadViewState (states[2]);
			if (states[3] != null) ((IStateManager)PathSeparatorStyle).LoadViewState (states[3]);
			if (states[4] != null) ((IStateManager)RootNodeStyle).LoadViewState (states[4]);
		}

		[MonoTODO ("why override?")]
		protected override void OnDataBinding (EventArgs e)
		{
			base.OnDataBinding (e);
		}
		
		[MonoTODO ("why override?")]
		protected internal override void Render (HtmlTextWriter writer)
		{
			base.Render (writer);
		}

		protected internal override void RenderContents (HtmlTextWriter writer)
		{
			string skip_id = ClientID + "_SkipLink";
			string altText = SkipLinkText;
			bool needAnchor = !String.IsNullOrEmpty (altText);
			
			if (needAnchor) {
				// Anchor start
				writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + skip_id);
				writer.RenderBeginTag (HtmlTextWriterTag.A);

				// Image
				writer.AddAttribute (HtmlTextWriterAttribute.Alt, altText);
				writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
				writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
				writer.AddAttribute (HtmlTextWriterAttribute.Src, Page.ClientScript.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
				writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
				writer.RenderBeginTag (HtmlTextWriterTag.Img);
				writer.RenderEndTag ();

				writer.RenderEndTag ();
			}

			base.RenderContents (writer);

			if (needAnchor) {
				 writer.AddAttribute(HtmlTextWriterAttribute.Id, skip_id);
				 writer.RenderBeginTag(HtmlTextWriterTag.A);
				 writer.RenderEndTag();
			}
		}
		
		protected override object SaveViewState ()
		{
			object[] state = new object [5];
			state [0] = base.SaveViewState ();
			
			if (currentNodeStyle != null) state [1] = ((IStateManager)currentNodeStyle).SaveViewState ();
			if (nodeStyle != null) state [2] = ((IStateManager)nodeStyle).SaveViewState ();
			if (pathSeparatorStyle != null) state [3] = ((IStateManager)pathSeparatorStyle).SaveViewState ();
			if (rootNodeStyle != null) state [4] = ((IStateManager)rootNodeStyle).SaveViewState ();
			
			for (int n=0; n<state.Length; n++)
				if (state [n] != null) return state;
			return null;
		}
		
		protected override void TrackViewState ()
		{
			base.TrackViewState();
			if (currentNodeStyle != null) ((IStateManager)currentNodeStyle).TrackViewState();
			if (nodeStyle != null) ((IStateManager)nodeStyle).TrackViewState();
			if (pathSeparatorStyle != null) ((IStateManager)pathSeparatorStyle).TrackViewState();
			if (rootNodeStyle != null) ((IStateManager)rootNodeStyle).TrackViewState();
		}
	}
}