//
// System.Web.SiteMapNode
//
// Authors:
//	Ben Maurer (bmaurer@users.sourceforge.net)
//
// (C) 2003 Ben Maurer
//

//
// 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.
//

#if NET_2_0
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Resources;

namespace System.Web {
	public class SiteMapNode : IHierarchyData, INavigateUIData, ICloneable {
	
		private SiteMapNode () {}
		
		public SiteMapNode (SiteMapProvider provider, string key)
			: this (provider, key, null, null, null, null, null, null, null) {}
		public SiteMapNode (SiteMapProvider provider, string key, string url)
			: this (provider, key, url, null, null, null, null, null, null) {}
		public SiteMapNode (SiteMapProvider provider, string key, string url, string title)
			: this (provider, key, url, title, null, null, null, null, null) {}
		public SiteMapNode (SiteMapProvider provider, string key, string url, string title, string description)
			: this (provider, key, url, title, description, null, null, null, null) {}
		
		public SiteMapNode (SiteMapProvider provider, string key, string url, string title, string description,
				    IList roles, NameValueCollection attributes, NameValueCollection explicitResourceKeys,
				    string implicitResourceKey)
		{
			if (provider == null)
				throw new ArgumentNullException ("provider");
			if (key == null)
				throw new ArgumentNullException ("key");
			
			this.provider = provider;
			this.key = key;
			this.url = url;
			this.title = title;
			this.description = description;
			this.roles = roles;
			this.attributes = attributes;
			this.resourceKeys = explicitResourceKeys;
			this.resourceKey = implicitResourceKey;
		}

		public SiteMapDataSourceView GetDataSourceView (SiteMapDataSource owner, string viewName)
		{
			return new SiteMapDataSourceView (owner, viewName, this);
		}
		
		public SiteMapHierarchicalDataSourceView GetHierarchicalDataSourceView ()
		{
			return new SiteMapHierarchicalDataSourceView (this);
		}
		
		public virtual bool IsAccessibleToUser (System.Web.HttpContext ctx)
		{
			return provider.IsAccessibleToUser (ctx, this);
		}
		
		public override string ToString()
		{
			return Title;
		}

		public virtual bool HasChildNodes {
			get { return ChildNodes != null && ChildNodes.Count != 0; }
		}

		public SiteMapNodeCollection GetAllNodes ()
		{
			SiteMapNodeCollection ret;
		
			ret = new SiteMapNodeCollection ();
			GetAllNodesRecursive (ret);
			return SiteMapNodeCollection.ReadOnly (ret);
		}
		
		void GetAllNodesRecursive(SiteMapNodeCollection c)
		{
			SiteMapNodeCollection childNodes = this.ChildNodes;

			if (childNodes != null && childNodes.Count > 0) {
				c.AddRange (childNodes);
				foreach (SiteMapNode n in childNodes)
					n.GetAllNodesRecursive (c);
			}
		}

		
		public virtual bool IsDescendantOf (SiteMapNode node)
		{
			for (SiteMapNode n = ParentNode; n != null; n = n.ParentNode)
				if (n == node) return true; 

			return false; 
		}
		
		public virtual SiteMapNode NextSibling {
			get {
				IList siblings = this.SiblingNodes;
				if (siblings == null)
					return null; 
				
				int pos = siblings.IndexOf (this);
				if (pos >= 0 && pos < siblings.Count - 1)
					return (SiteMapNode) siblings [pos + 1]; 
				
				return null; 
			}
		}
		
		public virtual SiteMapNode PreviousSibling {
			get {
				IList siblings = this.SiblingNodes;
				if (siblings == null)
					return null; 
				
				int pos = siblings.IndexOf (this);
				if (pos > 0 && pos < siblings.Count)
					return (SiteMapNode) siblings [pos - 1]; 
				
				return null; 
			}
		}
		
		public virtual SiteMapNode ParentNode {
			get {
				if (parent != null) return parent;
				
				SiteMapProvider provider = this.provider;
				
				do {
					parent = provider.GetParentNode (this);
					if (parent != null)
						return parent; 
					
					provider = provider.ParentProvider;
				} while (provider != null);
				return null;
			}
			set {
				CheckWritable ();
				parent = value;
			}
		}
		
		public virtual SiteMapNodeCollection ChildNodes {
			get {
				if (childNodes != null) return childNodes;
				return provider.GetChildNodes (this);
			} 
			set {
				CheckWritable ();
				childNodes = value;
			}
		}

		public virtual SiteMapNode RootNode { get { return provider.RootProvider.RootNode; }  }
		
		SiteMapNodeCollection SiblingNodes {
			get {
				if (ParentNode != null)
					return ParentNode.ChildNodes;
				
				return null;
			}
		}
		
		protected string GetExplicitResourceString (string attributeName, string defaultValue, bool throwIfNotFound)
		{
			if (attributeName == null)
				throw new ArgumentNullException ("attributeName");
			
			if (resourceKeys != null){
				string[] values = resourceKeys.GetValues (attributeName);
				if (values != null && values.Length == 2) {
					try {
						object o = HttpContext.GetGlobalResourceObject (values [0], values [1]);
						if (o is string)
							return (string) o;
					}
					catch (MissingManifestResourceException) {
					}

					if (throwIfNotFound && defaultValue == null)
						throw new InvalidOperationException (String.Format ("The resource object with classname '{0}' and key '{1}' was not found.", values [0], values [1]));
				}
			}

			return defaultValue;
		}

		protected string GetImplicitResourceString (string attributeName)
		{
			if (attributeName == null)
				throw new ArgumentNullException ("attributeName");

			string resourceKey = ResourceKey;
			if (String.IsNullOrEmpty (resourceKey))
				return null;

			try {
				object o = HttpContext.GetGlobalResourceObject (provider.ResourceKey, resourceKey + "." + attributeName);
				if (o is string)
					return (string) o;
			} catch (MissingManifestResourceException) {
			}
			
			return null;
		}
		
		public virtual string this [string key]
		{
			get {
				if (provider.EnableLocalization) {
					string val = GetImplicitResourceString (key);
					if (val == null)
						val = GetExplicitResourceString (key, null, true);
					if (val != null)
						return val;
				}
				if (attributes != null) return attributes [key];
				return null;
			}
			set {
				CheckWritable ();
				if (attributes == null) attributes = new NameValueCollection ();
				attributes [key] = value;
			}
		}
		
		object ICloneable.Clone ()
		{
			return Clone (false);
		}
		
		public virtual SiteMapNode Clone ()
		{
			return Clone (false);
		}
		
		public virtual SiteMapNode Clone (bool cloneParentNodes)
		{
			SiteMapNode node = new SiteMapNode ();
			node.provider = provider;
			node.key = key;
			node.url = url;
			node.title = title;
			node.description = description;
			if (roles != null)
				node.roles = new ArrayList (roles);
			if (attributes != null)
				node.attributes = new NameValueCollection (attributes);
			if (cloneParentNodes && ParentNode != null)
				node.parent = (SiteMapNode) ParentNode.Clone (true);
			return node;
		}
				
		public override bool Equals (object ob)
		{
			SiteMapNode node = ob as SiteMapNode;
			if (node == null) return false;
			
			if (node.key != key ||
					node.url != url ||
					node.title != title ||
					node.description != description) {
				return false;
			}

			if (roles == null || node.roles == null) {
				if (roles != node.roles)
					return false;
			}
			else {
				if (roles.Count != node.roles.Count)
					return false;

				foreach (object role in roles)
					if (!node.roles.Contains (role)) return false;
			}
			if (attributes == null || node.attributes == null) {
				if (attributes != node.attributes)
					return false;
			}
			else {
				if (attributes.Count != node.attributes.Count)
					return false;

				foreach (string k in attributes)
					if (attributes[k] != node.attributes[k])
						return false;
			}
			return true;
		}
		
		public override int GetHashCode ()
		{
			return (key + url + title + description).GetHashCode ();
		}
		
		void CheckWritable ()
		{
			if (readOnly)
				throw new InvalidOperationException ("Can't modify read-only node");
		}
				
		#region Field Accessors
		
		protected NameValueCollection Attributes {
			get { return attributes; } 
			set { CheckWritable (); attributes = value; }
		}
		
		[Localizable (true)]
		public virtual string Description {
			get {
				string ret = null;
				
				if (provider.EnableLocalization) {
					ret = GetImplicitResourceString ("description");
					if (ret == null)
						ret = GetExplicitResourceString ("description", description, true);
				} else
					ret = description;
				
				return ret != null ? ret : String.Empty;
			}
			set { CheckWritable (); description = value; }
		}
		
		[LocalizableAttribute (true)]
		public virtual string Title {
			get {
				string ret = null;

				if (provider.EnableLocalization) {
					ret = GetImplicitResourceString ("title");
					if (ret == null)
						ret = GetExplicitResourceString ("title", title, true);
				} else
					ret = title;
				
				return ret != null ? ret : String.Empty;
			}
			set { CheckWritable (); title = value; }
		}
		
		public virtual string Url {
			get { return url != null ? url : ""; }
			set { CheckWritable (); url = value; }
		}
		
		public IList Roles {
			get { return roles; }
			set { CheckWritable (); roles = value; }
		}
		
		public bool ReadOnly {
			get { return readOnly; }
			set { readOnly = value; }
		}
		
		public string ResourceKey {
			get { return resourceKey; }
			set {
				if (ReadOnly)
					throw new InvalidOperationException ("The node is read-only.");
				resourceKey = value;
			}
		}
		
		public string Key { get { return key; } }
		public SiteMapProvider Provider { get { return provider; } }
		
		#endregion
		
		#region INavigateUIData
		IHierarchicalEnumerable System.Web.UI.IHierarchyData.GetChildren () { return ChildNodes; }
		IHierarchyData System.Web.UI.IHierarchyData.GetParent ()
		{
			return ParentNode;
		}

		bool System.Web.UI.IHierarchyData.HasChildren { get { return HasChildNodes; } }
		object System.Web.UI.IHierarchyData.Item { get { return this; } }
		string System.Web.UI.IHierarchyData.Path { get { return Url; } }
		string System.Web.UI.IHierarchyData.Type { get { return "SiteMapNode"; } }
		#endregion
		
		#region INavigateUIData
		string INavigateUIData.Name { get { return Title; }  }
		string INavigateUIData.NavigateUrl { get { return Url; } }
		string INavigateUIData.Value { get { return Title; } }
		#endregion

		#region Fields
		SiteMapProvider provider;
		string key;
		string url;
		string title;
		string description;
		IList roles;
		NameValueCollection attributes;
		NameValueCollection resourceKeys;
		bool readOnly;
		string resourceKey;
		SiteMapNode parent;
		SiteMapNodeCollection childNodes;
		#endregion
		
	}
}
#endif

