// Author: Dwivedi, Ajay kumar
//            Adwiv@Yahoo.com
using System;
using System.Collections;
using System.Xml.Serialization;

namespace System.Xml.Schema
{
	/// <summary>
	/// Summary description for XmlSchemaParticle.
	/// </summary>
	public abstract class XmlSchemaParticle : XmlSchemaAnnotated
	{
		internal static XmlSchemaParticle Empty {
			get {
				if (empty == null) {
					empty = new EmptyParticle ();
				}
				return empty;
			}
		}

		decimal minOccurs, maxOccurs;
		string  minstr, maxstr;
		static XmlSchemaParticle empty;
		decimal validatedMinOccurs = 1, validatedMaxOccurs = 1;
		internal int recursionDepth = -1;
		private decimal minEffectiveTotalRange = -1;
		internal bool parentIsGroupDefinition;

		protected XmlSchemaParticle()
		{
			minOccurs = decimal.One;
			maxOccurs = decimal.One;
		}

		#region Attributes

		[System.Xml.Serialization.XmlAttribute("minOccurs")]
		public string MinOccursString
		{
			get{ return minstr; }
			set
			{
				if (value == null) {
					minOccurs = decimal.One;
					minstr = value;
					return;
				}

				decimal val = decimal.Parse(value);
				if(val >= 0 && (val == Decimal.Truncate(val)))
				{
					minOccurs = val;
					minstr	 = val.ToString();
				}
				else
				{
					throw new XmlSchemaException
						("MinOccursString must be a non-negative number",null); 					
				}
			}
		}

		[System.Xml.Serialization.XmlAttribute("maxOccurs")]
		public string MaxOccursString
		{
			get{ return maxstr; }
			set
			{
				if(value == "unbounded")
				{
					maxstr = value;
					maxOccurs = decimal.MaxValue;
				}
				else
				{
					decimal val = decimal.Parse(value);
					if(val >= 0 && (val == Decimal.Truncate(val)))
					{
						maxOccurs = val;
						maxstr = val.ToString();
					}
					else
					{
						throw new XmlSchemaException
							("MaxOccurs must be a non-negative integer",null);
					}
					if (val == 0 && minstr == null)
						minOccurs = 0;
				}
			}
		}

		#endregion

		#region XmlIgnore

		[XmlIgnore]
		public decimal MinOccurs
		{
			get{ return  minOccurs; }
			set
			{
				MinOccursString = value.ToString ();
			}
		}

		[XmlIgnore]
		public decimal MaxOccurs 
		{
			get{ return  maxOccurs; } 
			set
			{
				MaxOccursString = value.ToString ();
			}
		}

		internal decimal ValidatedMinOccurs
		{
			get { return validatedMinOccurs; }
		}

		internal decimal ValidatedMaxOccurs
		{
			get { return validatedMaxOccurs; }
		}
		#endregion

		internal XmlSchemaParticle OptimizedParticle;

		internal abstract XmlSchemaParticle GetOptimizedParticle (bool isTop);

		internal XmlSchemaParticle GetShallowClone ()
		{
			return (XmlSchemaParticle) MemberwiseClone ();
		}

		internal void CompileOccurence (ValidationEventHandler h, XmlSchema schema)
		{
			if (MinOccurs > MaxOccurs && !(MaxOccurs == 0 && MinOccursString == null))
				error(h,"minOccurs must be less than or equal to maxOccurs");
			else {
				if (MaxOccursString == "unbounded")
					this.validatedMaxOccurs = decimal.MaxValue;
				else
					this.validatedMaxOccurs = maxOccurs;
				if (this.validatedMaxOccurs == 0)
					this.validatedMinOccurs = 0;
				else
					this.validatedMinOccurs = minOccurs;
			}
		}

		internal override void CopyInfo (XmlSchemaParticle obj)
		{
			base.CopyInfo (obj);
			if (MaxOccursString == "unbounded")
				obj.MaxOccurs = obj.validatedMaxOccurs = decimal.MaxValue;
			else 
				obj.MaxOccurs = obj.validatedMaxOccurs = this.ValidatedMaxOccurs;
			if (MaxOccurs == 0)
				obj.MinOccurs = obj.validatedMinOccurs = 0;
			else
				obj.MinOccurs = obj.validatedMinOccurs = this.ValidatedMinOccurs;
		}

		internal virtual bool ValidateOccurenceRangeOK (XmlSchemaParticle other,
			ValidationEventHandler h, XmlSchema schema, bool raiseError)
		{
			if ((this.ValidatedMinOccurs < other.ValidatedMinOccurs) ||
				(other.ValidatedMaxOccurs != decimal.MaxValue &&
				this.ValidatedMaxOccurs > other.ValidatedMaxOccurs)) {
				if (raiseError)
					error (h, "Invalid derivation occurence range was found.");
				return false;
			}
			return true;
		}

		internal virtual decimal GetMinEffectiveTotalRange ()
		{
			return ValidatedMinOccurs;
		}

		internal decimal GetMinEffectiveTotalRangeAllAndSequence ()
		{
			if (minEffectiveTotalRange >= 0)
				return minEffectiveTotalRange;

			decimal product = 0; //this.ValidatedMinOccurs;
			XmlSchemaObjectCollection col = null;
			if (this is XmlSchemaAll)
				col = ((XmlSchemaAll) this).Items;
			else
				col = ((XmlSchemaSequence) this).Items;
			foreach (XmlSchemaParticle p in col)
				product += p.GetMinEffectiveTotalRange ();

			minEffectiveTotalRange = product;
			return product;
		}

		// 3.9.6 Particle Emptiable
		internal virtual bool ValidateIsEmptiable ()
		{
			return this.validatedMinOccurs == 0 || this.GetMinEffectiveTotalRange () == 0;
		}

		internal abstract bool ValidateDerivationByRestriction (XmlSchemaParticle baseParticle,
			ValidationEventHandler h, XmlSchema schema, bool raiseError);

		internal abstract void ValidateUniqueParticleAttribution (
			XmlSchemaObjectTable qnames, ArrayList nsNames,
			ValidationEventHandler h, XmlSchema schema);

		internal abstract void ValidateUniqueTypeAttribution (XmlSchemaObjectTable labels,
			ValidationEventHandler h, XmlSchema schema);

		// See http://www.thaiopensource.com/relaxng/simplify.html
		internal abstract void CheckRecursion (int depth, ValidationEventHandler h, XmlSchema schema);

		internal abstract bool ParticleEquals (XmlSchemaParticle other);

		#region Internal Class
		public class EmptyParticle : XmlSchemaParticle
		{
			internal EmptyParticle ()
			{
			}

			internal override XmlSchemaParticle GetOptimizedParticle (bool isTop)
			{
				return this;
			}

			internal override bool ParticleEquals (XmlSchemaParticle other)
			{
				return other == this || other == XmlSchemaParticle.Empty;
			}

			internal override bool ValidateDerivationByRestriction (XmlSchemaParticle baseParticle,
				ValidationEventHandler h, XmlSchema schema, bool raiseError)
			{
				return true;
			}

			internal override void CheckRecursion (int depth, 
				ValidationEventHandler h, XmlSchema schema)
			{
				// do nothing
			}

			internal override void ValidateUniqueParticleAttribution (XmlSchemaObjectTable qnames,
				ArrayList nsNames, ValidationEventHandler h, XmlSchema schema)
			{
				// do nothing
			}

			internal override void ValidateUniqueTypeAttribution (XmlSchemaObjectTable labels,
				ValidationEventHandler h, XmlSchema schema)
			{
				// do nothing
			}

		}
		#endregion
	}
}