//
// System.Security.PermissionSet.cs
//
// Authors:
//	Nick Drochak(ndrochak@gol.com)
//	Sebastien Pouliot (spouliot@motus.com)
//
// (C) Nick Drochak
// Portions (C) 2003, 2004 Motus Technologies Inc. (http://www.motus.com)
//

using System;
using System.Collections;
using System.Security.Permissions;
using System.Security;
using System.Runtime.Serialization;

namespace System.Security {

	[Serializable]
	public class PermissionSet: ISecurityEncodable, ICollection, IEnumerable, IStackWalk, IDeserializationCallback {

		private PermissionState state;
		private ArrayList list;

		// constructors

		// for PolicyLevel (to avoid validation duplication)
		internal PermissionSet () 
		{
			list = new ArrayList ();
		}

		public PermissionSet (PermissionState state)
		{
			if (!Enum.IsDefined(typeof(System.Security.Permissions.PermissionState), state))
				throw new System.ArgumentException(); // state is not a valid System.Security.Permissions.PermissionState value.
			this.state = state;
			list = new ArrayList ();
		}

		public PermissionSet (PermissionSet permSet) : this (PermissionState.None)
		{
			// LAMESPEC: This would be handled by the compiler.  No way permSet is not a PermissionSet.
			//if (!(permSet is PermissionSet))
			//	throw new System.ArgumentException(); // permSet is not an instance of System.Security.PermissionSet.
			if (permSet == null)
				state = PermissionState.Unrestricted;
			else {
				foreach (IPermission p in permSet.list)
					list.Add (p);
			}
		}

		// methods

		public virtual IPermission AddPermission (IPermission perm)
		{
			if (perm == null)
				return null;

			IPermission existing = GetPermission (perm.GetType ());
			if (existing != null)
				perm = perm.Union (existing);

			list.Add (perm);
			return perm;
		}

		[MonoTODO()]
		public virtual void Assert ()
		{
		}

		public virtual PermissionSet Copy ()
		{
			return new PermissionSet (this);
		}

		public virtual void CopyTo (Array array, int index)
		{
			if (array.Rank > 1)
				throw new System.ArgumentException("Array has more than one dimension"); // array has more than one dimension.
			if (index < 0 || index >= array.Length)
				throw new System.IndexOutOfRangeException(); // index is outside the range of allowable values for array.
			if (null == array)
				throw new System.ArgumentNullException(); // array is null.
			list.CopyTo (array, index);
		}

		[MonoTODO()]
		public virtual void Demand ()
		{
		}

		[MonoTODO()]
		public virtual void Deny ()
		{
		}

		// to be re-used by NamedPermissionSet (and other derived classes)
		internal void FromXml (SecurityElement et, string className) 
		{
			if (et == null)
				throw new ArgumentNullException ("et");
			if (et.Tag != "PermissionSet")
				throw new ArgumentException ("not PermissionSet");
			if (!(et.Attributes ["class"] as string).EndsWith (className))
				throw new ArgumentException ("not " + className);
// version isn't checked
//			if ((et.Attributes ["version"] as string) != "1")
//				throw new ArgumentException ("wrong version");

			if ((et.Attributes ["Unrestricted"] as string) == "true")
				state = PermissionState.Unrestricted;
			else
				state = PermissionState.None;
		}

		public virtual void FromXml (SecurityElement et)
		{
			list.Clear ();
			FromXml (et, "PermissionSet");
			if (et.Children != null) {
				foreach (SecurityElement se in et.Children) {
					string className = (se.Attributes ["class"] as string);
					Type classType = Type.GetType (className);
					object [] psNone = new object [1] { PermissionState.None };
					IPermission p = (IPermission) Activator.CreateInstance (classType, psNone);
					p.FromXml (se);
					list.Add (p);
				}
			}
		}

		public virtual IEnumerator GetEnumerator ()
		{
			return list.GetEnumerator ();
		}

		public virtual bool IsSubsetOf (PermissionSet target)
		{
			// if target is empty we must be empty too
			if ((target == null) || (target.IsEmpty ()))
				return this.IsEmpty ();
			// if we're unrestricted then target must also be unrestricted
			if (this.IsUnrestricted () && target.IsUnrestricted ())
				return true;

			// if each of our permission is (a) present and (b) a subset of target
			foreach (IPermission p in list) {
				// for every type in both list
				IPermission i = target.GetPermission (p.GetType ());
				if (i == null)
					return false; // not present (condition a)
				if (!p.IsSubsetOf (i))
					return false; // not a subset (condition b)
			}
			return true;
		}

		[MonoTODO()]
		public virtual void PermitOnly ()
		{
		}

		public bool ContainsNonCodeAccessPermissions () 
		{
			foreach (IPermission p in list) {
				if (! p.GetType ().IsSubclassOf (typeof(CodeAccessPermission)))
					return true;
			}
			return false;
		}

		// undocumented behavior
		[MonoTODO()]
		public static byte[] ConvertPermissionSet (string inFormat, byte[] inData, string outFormat) 
		{
			return null;
		}

		public virtual IPermission GetPermission (Type permClass) 
		{
			foreach (object o in list) {
				if (o.GetType ().Equals (permClass))
					return (IPermission) o;
			}
			return null;
		}

		public virtual PermissionSet Intersect (PermissionSet other) 
		{
			// no intersection possible
			if ((other == null) || (other.IsEmpty ()) || (this.IsEmpty ()))
				return new PermissionSet (PermissionState.None);
			// intersections with unrestricted
			if (this.IsUnrestricted ())
				return other.Copy ();
			if (other.IsUnrestricted ())
				return this.Copy ();

			PermissionSet interSet = new PermissionSet (PermissionState.None);
			foreach (IPermission p in other.list) {
				// for every type in both list
				IPermission i = interSet.GetPermission (p.GetType ());
				if (i != null) {
					// add intersection for this type
					interSet.AddPermission (p.Intersect (i));
				}
				// or reject!
			}
			return interSet;
		}

		public virtual bool IsEmpty () 
		{
			// note: Unrestricted isn't empty
			return ((state == PermissionState.Unrestricted) ? false : (list.Count == 0));
		}

		public virtual bool IsUnrestricted () 
		{
			return (state == PermissionState.Unrestricted);
		}

		public virtual IPermission RemovePermission (Type permClass) 
		{
			foreach (object o in list) {
				if (o.GetType ().Equals (permClass)) {
					list.Remove (o);
					return (IPermission) o;
				}
			}
			return null;
		}

		public virtual IPermission SetPermission (IPermission perm) 
		{
			if (perm is IUnrestrictedPermission)
				state = PermissionState.None;
			RemovePermission (perm.GetType ());
			list.Add (perm);
			return perm;
		}

		public override string ToString ()
		{
			return ToXml ().ToString ();
		}

		public virtual SecurityElement ToXml ()
		{
			SecurityElement se = new SecurityElement ("PermissionSet");
			se.AddAttribute ("class", GetType ().FullName);
			se.AddAttribute ("version", "1");
			if (state == PermissionState.Unrestricted)
				se.AddAttribute ("Unrestricted", "true");
			else {
				foreach (IPermission p in list)
					se.AddChild (p.ToXml ());
			}
			return se;
		}

		public virtual PermissionSet Union (PermissionSet other)
		{
			if (other == null)
				return this.Copy ();
			if (this.IsUnrestricted () || other.IsUnrestricted ())
				return new PermissionSet (PermissionState.Unrestricted);
			
			PermissionSet copy = this.Copy ();
			foreach (IPermission p in other.list) {
				copy.AddPermission (p);
			}
			return copy;
		}

		public virtual int Count {
			get { return list.Count; }
		}

		public virtual bool IsSynchronized {
			get { return list.IsSynchronized; }
		}

		public virtual bool IsReadOnly {
			get { return false; } // always false
		}

		public virtual object SyncRoot {
			get { return list.SyncRoot; }
		}

		[MonoTODO()]
		void IDeserializationCallback.OnDeserialization (object sender) 
		{
		}
	}
}
