//
// System.Web.UI.WebControls.MenuItem.cs
//
// Authors:
//	Lluis Sanchez Gual (lluis@novell.com)
//
// (C) 2004 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) 2004 Novell, Inc (http://www.novell.com)
//

#if NET_2_0

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

namespace System.Web.UI.WebControls
{
	[ParseChildrenAttribute (true, "ChildItems")]
	public sealed class MenuItem: IStateManager, ICloneable
	{
		StateBag ViewState = new StateBag ();
		MenuItemCollection items;
		bool marked;
		Menu menu;
		MenuItem parent;
		int index;
		string path;
		int depth = -1;
		
		object dataItem;
		IHierarchyData hierarchyData;
		
		bool gotBinding;
		MenuItemBinding binding;
		PropertyDescriptorCollection boundProperties;
		
		public MenuItem ()
		{
		}
		
		public MenuItem (string text)
		{
			Text = text;
		}
		
		public MenuItem (string text, string value)
		{
			Text = text;
			Value = value;
		}
		
		public MenuItem (string text, string value, string imageUrl)
		{
			Text = text;
			Value = value;
			ImageUrl = imageUrl;
		}
		
		public MenuItem (string text, string value, string imageUrl, string navigateUrl)
		{
			Text = text;
			Value = value;
			ImageUrl = imageUrl;
			NavigateUrl = navigateUrl;
		}
		
		public MenuItem (string text, string value, string imageUrl, string navigateUrl, string target)
		{
			Text = text;
			Value = value;
			ImageUrl = imageUrl;
			NavigateUrl = navigateUrl;
			Target = target;
		}
		
		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
		[Browsable (false)]
		public int Depth {
			get {
				if (depth != -1) return depth;
				if (Parent == null) depth = 0;
				else depth = Parent.Depth + 1;
				return depth;
			}
		}
		
		void ResetPathData ()
		{
			path = null;
			depth = -1;
			gotBinding = false;
		}
		
		internal Menu Menu {
			get { return menu; }
			set {
				menu = value;
				if (items != null)
					items.SetMenu (menu);
				ResetPathData ();
			}
		}
		
		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
		[DefaultValue (false)]
		[Browsable (false)]
		public bool DataBound {
			get { return ViewState ["DataBound"] == null ? false : (bool) ViewState ["DataBound"]; }
			private set { ViewState ["DataBound"] = value; }
		}
		
		[DefaultValue (null)]
		[Browsable (false)]
		public object DataItem {
			get {
				if (!DataBound) throw new InvalidOperationException ("MenuItem is not data bound.");
				return dataItem;
			}
		}
		
		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
		[DefaultValue ("")]
		[Browsable (false)]
		public string DataPath {
			get {
				return ViewState ["DataPath"] == null ? String.Empty : (String) ViewState ["DataPath"];
			}
			private set {
				ViewState ["DataPath"] = value;
			}
		}
		
		[MergableProperty (false)]
		[Browsable (false)]
		[PersistenceMode (PersistenceMode.InnerDefaultProperty)]
		public MenuItemCollection ChildItems {
			get {
				if (items == null) {
					items = new MenuItemCollection (this);
						
					if (((IStateManager)this).IsTrackingViewState)
						((IStateManager)items).TrackViewState();
				}
				return items;
			}
		}
		
		[DefaultValue ("")]
		[UrlProperty]
		[Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
		public string ImageUrl {
			get {
				return ViewState ["ImageUrl"] == null ? String.Empty : (String) ViewState ["ImageUrl"];
			}
			set {
				ViewState ["ImageUrl"] = value;
			}
		}

		[DefaultValue ("")]
		[UrlProperty]
		[Editor ("System.Web.UI.Design.UrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
		public string NavigateUrl {
			get {
				return ViewState ["NavigateUrl"] == null ? String.Empty : (String) ViewState ["NavigateUrl"];
			}
			set {
				ViewState ["NavigateUrl"] = value;
			}
		}

		[DefaultValue ("")]
		[UrlProperty]
		[Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
		public string PopOutImageUrl {
			get {
				return ViewState ["PopOutImageUrl"] == null ? String.Empty : (String) ViewState ["PopOutImageUrl"];
			}
			set {
				ViewState ["PopOutImageUrl"] = value;
			}
		}

		[DefaultValue ("")]
		public string Target {
			get {
				return ViewState ["Target"] == null ? String.Empty : (String) ViewState ["Target"];
			}
			set {
				ViewState ["Target"] = value;
			}
		}

		[Localizable (true)]
		[DefaultValue ("")]
		public string Text {
			get {
				return ViewState ["Text"] == null ? String.Empty : (String) ViewState ["Text"];
			}
			set {
				ViewState ["Text"] = value;
			}
		}

		[Localizable (true)]
		[DefaultValue ("")]
		public string ToolTip {
			get {
				return ViewState ["ToolTip"] == null ? String.Empty : (String) ViewState ["ToolTip"];
			}
			set {
				ViewState ["ToolTip"] = value;
			}
		}

		[Localizable (true)]
		[DefaultValue ("")]
		public string Value {
			get {
				return ViewState ["Value"] == null ? String.Empty : (String) ViewState ["Value"];
			}
			set {
				ViewState ["Value"] = value;
			}
		}
		
		[DefaultValue ("")]
		[UrlProperty]
		[Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
		public string SeparatorImageUrl {
			get {
				return ViewState ["SeparatorImageUrl"] == null ? String.Empty : (String) ViewState ["SeparatorImageUrl"];
			}
			set {
				ViewState ["SeparatorImageUrl"] = value;
			}
		}
		
	    [BrowsableAttribute (true)]
	    [DefaultValueAttribute (true)]
		public bool Selectable {
			get {
				return ViewState ["Selectable"] == null ? true : (bool) ViewState ["Selectable"];
			}
			set {
				ViewState ["Selectable"] = value;
			}
		}
		
	    [BrowsableAttribute (true)]
	    [DefaultValueAttribute (true)]
		public bool Enabled {
			get {
				return ViewState ["Enabled"] == null ? true : (bool) ViewState ["Enabled"];
			}
			set {
				ViewState ["Enabled"] = value;
			}
		}
		
		internal bool BranchEnabled {
			get { return Enabled && (parent == null || parent.BranchEnabled); }
		}

		[DefaultValue (false)]
		[Browsable (true)]
		public bool Selected {
			get {
				if (menu != null)
					return menu.SelectedItem == this;
				else
					return false;
			}
			set {
				if (menu != null) {
					if (!value && menu.SelectedItem == this)
						menu.SetSelectedItem (null);
					else if (value)
						menu.SetSelectedItem (this);
				}
			}
		}
		
		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
		[Browsable (false)]
		public MenuItem Parent {
			get { return parent; }
		}
		
		[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
		[Browsable (false)]
		public string ValuePath {
			get {
				if (menu == null) return Value;
				
				StringBuilder sb = new StringBuilder (Value);
				MenuItem item = parent;
				while (item != null) {
					sb.Insert (0, menu.PathSeparator);
					sb.Insert (0, item.Value);
					item = item.Parent;
				}
				return sb.ToString ();
			}
		}
		
		internal int Index {
			get { return index; }
			set { index = value; ResetPathData (); }
		}
		
		internal void SetParent (MenuItem item) {
			parent = item;
			ResetPathData ();
		}
		
		internal string Path {
			get {
				if (path != null) return path;
				StringBuilder sb = new StringBuilder (index.ToString());
				MenuItem item = parent;
				while (item != null) {
					sb.Insert (0, '_');
					sb.Insert (0, item.Index.ToString ());
					item = item.Parent;
				}
				path = sb.ToString ();
				return path;
			}
		}
		
		internal bool HasChildData {
			get { return items != null; }
		}
		
		void IStateManager.LoadViewState (object savedState)
		{
			if (savedState == null)
				return;

			object[] states = (object[]) savedState;
			ViewState.LoadViewState (states [0]);
			
			if (states [1] != null)
				((IStateManager)ChildItems).LoadViewState (states [1]);
		}
		
		object IStateManager.SaveViewState ()
		{
			object[] states = new object[2];
			states[0] = ViewState.SaveViewState();
			states[1] = (items == null ? null : ((IStateManager)items).SaveViewState());
			
			for (int i = 0; i < states.Length; i++) {
				if (states [i] != null)
					return states;
			}
			return null;
		}
		
		void IStateManager.TrackViewState ()
		{
			if (marked) return;
			marked = true;
			ViewState.TrackViewState();

			if (items != null)
				((IStateManager)items).TrackViewState ();
		}
		
		bool IStateManager.IsTrackingViewState
		{
			get { return marked; }
		}
		
		internal void SetDirty ()
		{
			ViewState.SetDirty (true);
			if (items != null)
				items.SetDirty ();
		}
		
		object ICloneable.Clone ()
		{
			MenuItem nod = new MenuItem ();
			foreach (DictionaryEntry e in ViewState)
				nod.ViewState [(string)e.Key] = e.Value;
				
			foreach (ICloneable c in ChildItems)
				nod.ChildItems.Add ((MenuItem)c.Clone ());
				
			return nod;
		}
		
		internal void Bind (IHierarchyData hierarchyData)
		{
			this.hierarchyData = hierarchyData;
			DataBound = true;
			DataPath = hierarchyData.Path;
			dataItem = hierarchyData.Item;

			MenuItemBinding bin = GetBinding ();
			if (bin != null) {

				// Bind Enabled property

				if (bin.EnabledField != "")
					try { Enabled = Convert.ToBoolean (GetBoundPropertyValue (bin.EnabledField)); }
					catch { Enabled = bin.Enabled; }
				else
					Enabled = bin.Enabled;

				// Bind ImageUrl property

				if (bin.ImageUrlField.Length > 0) {
					ImageUrl = Convert.ToString (GetBoundPropertyValue (bin.ImageUrlField));
					if (ImageUrl.Length == 0)
						ImageUrl = bin.ImageUrl;
				}
				else if (bin.ImageUrl.Length > 0)
					ImageUrl = bin.ImageUrl;

				// Bind NavigateUrl property

				if (bin.NavigateUrlField.Length > 0) {
					NavigateUrl = Convert.ToString (GetBoundPropertyValue (bin.NavigateUrlField));
					if (NavigateUrl.Length == 0)
						NavigateUrl = bin.NavigateUrl;
				}
				else if (bin.NavigateUrl.Length > 0)
					NavigateUrl = bin.NavigateUrl;

				// Bind PopOutImageUrl property

				if (bin.PopOutImageUrlField.Length > 0) {
					PopOutImageUrl = Convert.ToString (GetBoundPropertyValue (bin.PopOutImageUrlField));
					if (PopOutImageUrl.Length == 0)
						PopOutImageUrl = bin.PopOutImageUrl;
				}
				else if (bin.PopOutImageUrl.Length > 0)
					PopOutImageUrl = bin.PopOutImageUrl;

				// Bind Selectable property

				if (bin.SelectableField != "")
					try { Selectable = Convert.ToBoolean (GetBoundPropertyValue (bin.SelectableField)); }
					catch { Selectable = bin.Selectable; }
				else
					Selectable = bin.Selectable;

				// Bind SeparatorImageUrl property

				if (bin.SeparatorImageUrlField.Length > 0) {
					SeparatorImageUrl = Convert.ToString (GetBoundPropertyValue (bin.SeparatorImageUrlField));
					if (SeparatorImageUrl.Length == 0)
						SeparatorImageUrl = bin.SeparatorImageUrl;
				}
				else if (bin.SeparatorImageUrl.Length > 0)
					SeparatorImageUrl = bin.SeparatorImageUrl;

				// Bind Target property

				if (bin.TargetField.Length > 0) {
					Target = Convert.ToString (GetBoundPropertyValue (bin.TargetField));
					if (Target.Length == 0)
						Target = bin.Target;
				}
				else if (bin.Target.Length > 0)
					Target = bin.Target;

				// Bind ToolTip property

				if (bin.ToolTipField.Length > 0) {
					ToolTip = Convert.ToString (GetBoundPropertyValue (bin.ToolTipField));
					if (ToolTip.Length == 0)
						ToolTip = bin.ToolTip;
				}
				else if (bin.ToolTip.Length > 0)
					ToolTip = bin.ToolTip;

				// Bind Value property

				if (bin.ValueField.Length > 0) {
					Value = Convert.ToString (GetBoundPropertyValue (bin.ValueField));
					if (Value.Length == 0)
						Value = bin.Value;
					if (Value.Length == 0)
						Value = bin.Text;
				}
				else if (bin.Value.Length > 0)
					Value = bin.Value;
				else if (bin.Text.Length > 0)
					Value = bin.Text;
				else
					Value = GetDefaultBoundText ();
				
				// Bind Text property

				if (bin.TextField.Length > 0) {
					Text = Convert.ToString (GetBoundPropertyValue (bin.TextField));
					if (bin.FormatString.Length > 0)
						Text = string.Format (bin.FormatString, Text);
					if (Text.Length == 0)
						Text = bin.Text;
					if (Text.Length == 0)
						Text = bin.Value;
				}
				else if (bin.Text.Length > 0)
					Text = bin.Text;
				else if (bin.Value.Length > 0)
					Text = bin.Value;
				else
					Text = GetDefaultBoundText ();

			}
			else {
				Text = Value = GetDefaultBoundText ();
			}

			INavigateUIData navigateUIData = hierarchyData as INavigateUIData;
			if (navigateUIData != null) {
				Text = navigateUIData.ToString ();
				NavigateUrl = navigateUIData.NavigateUrl;
			}
		}
		
		internal void SetDataItem (object item)
		{
			dataItem = item;
		}
		
		internal void SetDataPath (string path)
		{
			DataPath = path;
		}
		
		internal void SetDataBound (bool bound)
		{
			DataBound = bound;
		}
		
		string GetDefaultBoundText ()
		{
			if (hierarchyData != null) return hierarchyData.ToString ();
			else if (dataItem != null) return dataItem.ToString ();
			else return string.Empty;
		}
		
		string GetDataItemType ()
		{
			if (hierarchyData != null) return hierarchyData.Type;
			else if (dataItem != null) return dataItem.GetType().ToString ();
			else return string.Empty;
		}
		
		MenuItemBinding GetBinding ()
		{
			if (menu == null) return null;
			if (gotBinding) return binding;
			binding = menu.FindBindingForItem (GetDataItemType (), Depth);
			gotBinding = true;
			return binding;
		}
		
		object GetBoundPropertyValue (string name)
		{
			if (boundProperties == null) {
				if (hierarchyData != null)
					boundProperties = TypeDescriptor.GetProperties (hierarchyData);
				else
					boundProperties = TypeDescriptor.GetProperties (dataItem);
			}
			
			PropertyDescriptor prop = boundProperties.Find (name, true);
			if (prop == null)
				throw new InvalidOperationException ("Property '" + name + "' not found in data bound item");
				
			if (hierarchyData != null)
				return prop.GetValue (hierarchyData);
			else
				return prop.GetValue (dataItem);
		}
	}
}

#endif
