//
// System.Net.WebPermission.cs
//
// Author:
//   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
//   (based on SocketPermission.cs)
//
// (C) 2003 Andreas Nahr
//

using System;
using System.Collections;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;

namespace System.Net
{
	// (based on SocketPermission.cs - Please look there to implement missing members!)
	[MonoTODO ("Most private members that include functionallity are not implemented!")]
	[Serializable]
	public sealed class WebPermission : CodeAccessPermission, IUnrestrictedPermission
	{
		// Fields
		ArrayList m_acceptList = new ArrayList ();
		ArrayList m_connectList = new ArrayList ();
		bool m_noRestriction = false;

		// Constructors
		public WebPermission () : base () 
		{				
		}

		public WebPermission (PermissionState state) : base () 
		{						
			m_noRestriction = (state == PermissionState.Unrestricted);
		}

		public WebPermission (NetworkAccess access, string uriString) : base () 
		{
			AddPermission (access, uriString);
		}		

		public WebPermission (NetworkAccess access, Regex uriRegex) : base () 
		{
			AddPermission (access, uriRegex);
		}

		// Properties
		public IEnumerator AcceptList {
			get { return m_acceptList.GetEnumerator (); }
		}

		public IEnumerator ConnectList {
			get { return m_connectList.GetEnumerator (); }
		}

		// Methods

		[MonoTODO]
		public void AddPermission (NetworkAccess access, string uriString)
		{
			throw new NotImplementedException ();
		}
		[MonoTODO]
		public void AddPermission (NetworkAccess access, Regex uriRegex)
		{
			throw new NotImplementedException ();
		}

		public override IPermission Copy ()
		{
			WebPermission permission;
			permission = new WebPermission (m_noRestriction ? 
						PermissionState.Unrestricted : 
						PermissionState.None);

			// as EndpointPermission's are immutable it's safe to do a shallow copy.
			permission.m_connectList = (ArrayList) 
			this.m_connectList.Clone ();
			permission.m_acceptList = (ArrayList) this.m_acceptList.Clone ();
			return permission;
		}

		public override IPermission Intersect (IPermission target)
		{
			if (target == null) 
				return null;
			WebPermission perm = target as WebPermission;
			if (perm == null) 
				throw new ArgumentException ("Argument not of type WebPermission");
			if (m_noRestriction) 
				return IntersectEmpty (perm) ? null : perm.Copy ();
			if (perm.m_noRestriction)
				return IntersectEmpty (this) ? null : this.Copy ();
			WebPermission newperm = new WebPermission (PermissionState.None);
			Intersect (this.m_connectList, perm.m_connectList, newperm.m_connectList);
			Intersect (this.m_acceptList, perm.m_acceptList, newperm.m_acceptList);
			return IntersectEmpty (newperm) ? null : newperm;
		}

		private bool IntersectEmpty (WebPermission permission)
		{
			return !permission.m_noRestriction && 
			       (permission.m_connectList.Count == 0) &&
			       (permission.m_acceptList.Count == 0);
		}

		[MonoTODO]
		private void Intersect (ArrayList list1, ArrayList list2, ArrayList result)
		{
			throw new NotImplementedException ();
		}

		public override bool IsSubsetOf (IPermission target) 
		{
			if (target == null)
				return (!m_noRestriction && m_connectList.Count == 0 && m_acceptList.Count == 0);
			WebPermission perm = target as WebPermission;
			if (perm == null) 
				throw new ArgumentException ("Parameter target must be of type WebPermission");
			if (perm.m_noRestriction)
				return true;
			if (this.m_noRestriction)
				return false;
			if (this.m_acceptList.Count == 0 && this.m_connectList.Count == 0)
				return true;
			if (perm.m_acceptList.Count == 0 && perm.m_connectList.Count == 0)
				return false;
			return IsSubsetOf (this.m_connectList, perm.m_connectList)
			    && IsSubsetOf (this.m_acceptList, perm.m_acceptList);
		}


		[MonoTODO]
		private bool IsSubsetOf (ArrayList list1, ArrayList list2)
		{
			throw new NotImplementedException ();
		}

		public bool IsUnrestricted ()
		{
			return m_noRestriction;
		}

		public override SecurityElement ToXml ()
		{
			SecurityElement root = new SecurityElement ("IPermission");
			root.AddAttribute ("class", this.GetType ().AssemblyQualifiedName);
			root.AddAttribute ("version", "1");
			if (m_noRestriction) {
				root.AddAttribute ("Unrestricted", "true");
				return root;
			}				
			if (this.m_connectList.Count > 0)
				ToXml (root, "ConnectAccess", m_connectList.GetEnumerator ());
			if (this.m_acceptList.Count > 0) 
				ToXml (root, "AcceptAccess", m_acceptList.GetEnumerator ());
			return root;
		}

		[MonoTODO]
		private void ToXml (SecurityElement root, string childName, IEnumerator enumerator)
		{
			throw new NotImplementedException ();
		}

		public override void FromXml (SecurityElement securityElement)
		{
			if (securityElement == null)
				throw new ArgumentNullException ("securityElement");

			// LAMESPEC: it says to throw an ArgumentNullException in this case
			if (securityElement.Tag != "IPermission")
				throw new ArgumentException ("securityElement");
			string classStr = securityElement.Attribute ("class");
			if (classStr == null || !classStr.StartsWith (this.GetType ().FullName + ","))
				throw new ArgumentException ("securityElement");
			string unrestricted = securityElement.Attribute ("Unrestricted");
			if (unrestricted != null) {
				this.m_noRestriction = (String.Compare (unrestricted, "true", true) == 0);
				if (this.m_noRestriction)
					return;
			}
			this.m_noRestriction = false;
			this.m_connectList = new ArrayList ();
			this.m_acceptList = new ArrayList ();
			ArrayList children = securityElement.Children;
			foreach (SecurityElement child in children) {
				if (child.Tag == "ConnectAccess") 
					FromXml (child.Children, NetworkAccess.Connect);
				else if (child.Tag == "AcceptAccess")
					FromXml (child.Children, NetworkAccess.Accept);
			}
		}

		[MonoTODO]
		private void FromXml (ArrayList endpoints, NetworkAccess access)
		{
			throw new NotImplementedException ();
		}

		public override IPermission Union (IPermission target) 
		{
			// LAMESPEC: according to spec we should throw an 
			// exception when target is null. We'll follow the
			// behaviour of MS.Net instead of the spec, also
			// because it matches the Intersect behaviour.

			if (target == null)
				return null;
				// throw new ArgumentNullException ("target");

			WebPermission perm = target as WebPermission;
			if (perm == null)
				throw new ArgumentException ("Argument not of type WebPermission");
			if (this.m_noRestriction || perm.m_noRestriction) 
				return new WebPermission (PermissionState.Unrestricted);

			WebPermission copy = (WebPermission) perm.Copy ();
			copy.m_acceptList.InsertRange (copy.m_acceptList.Count, this.m_acceptList);
			copy.m_connectList.InsertRange (copy.m_connectList.Count, this.m_connectList);

			return copy;
		}
	}
} 

