using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Collections.Generic;
using System.Xml;


using CategoryTheory;
using DiagramUI;
using FormulaEditor;
using GeneralLinearMethod;
using AnalyticPolynom;


namespace DataPerformer
{
	/// <summary>
	/// <summary>
	/// Recurrent object
	/// </summary>
	[Serializable()]
	public class Recursive : ICategoryObject, ISerializable, IDataConsumer, IMeasurements, IStarted, IAlias,
		IPostSetArrow, ICheckCorrectness, IStep	
	{
		
		#region Fields

		
		/// <summary>
		/// Associated object
		/// </summary>
		private object obj;
		
		/// <summary>
		/// Internal variables
		/// </summary>
		private Hashtable variables = new Hashtable();

		/// <summary>
		/// External parameters
		/// </summary>
		private Hashtable parameters = new Hashtable();

		/// <summary>
		/// Aliases
		/// </summary>
		private Hashtable aliases = new Hashtable();

		/// <summary>
		/// String representation of internal variables
		/// </summary>
		private Hashtable vars = new Hashtable();

		/// <summary>
		/// String representation of external parameters
		/// </summary>
		private Hashtable pars = new Hashtable();

		/// <summary>
		/// String represntation of external aliases
		/// </summary>
		private Hashtable externalAls = new Hashtable();

		/// <summary>
		/// External aliases
		/// </summary>
		private Hashtable externalAliases = new Hashtable();

		/// <summary>
		/// Output measurements
		/// </summary>
		private ArrayList output = new ArrayList();

		/// <summary>
		/// Data links
		/// </summary>
		private List<IMeasurements> measurements = new List<IMeasurements>();

		/// <summary>
		/// Comments
		/// </summary>
		private ArrayList comments = new ArrayList();

		/// <summary>
		/// The "is updated" sign
		/// </summary>
		private bool isUpdated = false;


		/// <summary>
		/// Number of step
		/// </summary>
		private int step;


		/// <summary>
		/// Number of old step
		/// </summary>
		private int oldStep;


		/// <summary>
		/// Argument of formula
		/// </summary>
		private ElementaryObjectArgument arg;


		#endregion

		#region Constructors

		/// <summary>
		/// Default constructor
		/// </summary>
		public Recursive()
		{
		}

		/// <summary>
		/// Serialization constructor
		/// </summary>
		/// <param name="info">Serialization info</param>
		/// <param name="context">Streaming context</param>
		public Recursive(SerializationInfo info, StreamingContext context)
		{
			try
			{
				vars = (Hashtable)info.GetValue("Variables", typeof(Hashtable));	
				pars = (Hashtable)info.GetValue("Parameters", typeof(Hashtable));	
				aliases = (Hashtable)info.GetValue("Aliases", typeof(Hashtable));	
				externalAls = (Hashtable)info.GetValue("ExternalAliases", typeof(Hashtable));
				comments = (ArrayList)info.GetValue("Comments", typeof(ArrayList));
			}
			catch (Exception)
			{
			}
		}

		#endregion

		#region ISerializable Members

		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("Variables", vars);	
			info.AddValue("Parameters", pars);	
			info.AddValue("Aliases", aliases);
			info.AddValue("ExternalAliases", externalAls);
			info.AddValue("Comments", comments);
		}

		#endregion

		#region ICategoryObject Members

		public ICategory Category
		{
			get
			{
				return null;
			}
		}

		public ICategoryArrow Id
		{
			get
			{
				return null;
			}
		}

		#endregion

		#region IAssociatedObject Members

		public object Object
		{
			get
			{
				return obj;
			}
			set
			{
				obj = value;
			}
		}

		#endregion

		#region IDataConsumer Members

		public void Add(IMeasurements m)
		{
            measurements.Add(m);
		}

        public void Remove(IMeasurements m)
		{
            measurements.Remove(m);
		}

		public void UpdateChildrenData()
		{
            foreach (IMeasurements m in measurements)
			{
				m.UpdateMeasurements();
			}
		}

		int IDataConsumer.Count
		{
			get
			{
				return measurements.Count;
			}
		}

		public IMeasurements this[int n]
		{
			get
			{
                return measurements[n];
			}
		}

		public void Reset()
		{
			DataConsumer.Reset(this);
		}

		#endregion

		#region IMeasurements Members

		IMeasure DataPerformer.IMeasurements.this[int n]
		{
			get
			{
				return output[n] as IMeasure;
			}
		}

		public void UpdateMeasurements()
		{
			if (IsUpdated)
			{
				return;
			}
			if (step == oldStep)
			{
				return;
			}
			step = oldStep;
			try
			{
				foreach (char c in externalAliases.Keys)
				{
					object[] o = externalAliases[c] as object[];
					IAlias al = o[0] as IAlias;
					string key = o[1] as string;
					object[] ob = variables[c] as object[];
					al[key] = ob[0];
				}
				UpdateChildrenData();
				foreach (char c in variables.Keys)
				{
					object[] o = variables[c] as object[];
					arg[c] = o[0];
				}
				foreach (char c in parameters.Keys)
				{
					IMeasure m = parameters[c] as IMeasure;
					arg[c] = m.Parameter();
				}
				foreach (char c in aliases.Keys)
				{
					object o = aliases[c];
					arg[c] = o;
				}
				foreach (char c in variables.Keys)
				{
					object[] o = variables[c] as object[];
					ObjectFormulaTree tree = o[1] as ObjectFormulaTree;
					o[0] = tree.Result;
					//ICalculator calc = o[2] as ICalculator;
					//o[0] = calc.Result;
				}
				isUpdated = true;
			}
			catch (Exception e)
			{
				PureDesktop.ThrowException(this, e);
			}
		}


		public string SourceName
		{
			get
			{
				IObjectLabel lab = obj as IObjectLabel;
				return lab.Name;
			}
		}

		public bool IsUpdated
		{
			get
			{
				return isUpdated;
			}
			set
			{
				isUpdated = value;
			}
		}

		int IMeasurements.Count
		{
			get
			{
				return output.Count;
			}
		}

		#endregion

		#region IStarted Members

		public void Start()
		{
			foreach (char c in vars.Keys)
			{
				object[] o0 = vars[c] as object[];
				object[] o = variables[c] as object[];
				o[0] = o0[0];
			}
			oldStep = step;
		}

		#endregion

		#region IAlias Members

		public List<string> AliasNames
		{
			get
			{
				List<string> list = new List<string>();
				foreach (char c in aliases.Keys)
				{
					list.Add(c + "");
				}
				foreach (char c in vars.Keys)
				{
					list.Add(c + "");
				}
				return list;
			}
		}

		object DiagramUI.IAlias.this[string alias]
		{
			get
			{
				if (aliases.ContainsKey(alias[0]))
				{
					return aliases[alias[0]];
				}
				object[] o = vars[alias[0]] as object[];
				return o[2];
			}
			set
			{
				if (aliases.ContainsKey(alias[0]))
				{
					aliases[alias[0]] = value;
					return;
				}
				object[] o = vars[alias[0]] as object[];
				o[2] = value;
			}
		}

        public object GetType(int n)
        {
            Double a = 0;
            return a;
        }


		#endregion

		#region IPostSetArrow Members

		/// <summary>
		/// The operation that performs after arrows setting
		/// </summary>
		public void PostSetArrow()
		{
			acceptParameters();
			acceptFormulas();
			ExternalAliases = externalAls;
		}
		#endregion

		#region ICheckCorrectness Members

		/// <summary>
		/// Checks its correctenss
		/// </summary>
		public void CheckCorrectness()
		{
			try
			{
				PostSetArrow();
			}
			catch (Exception e)
			{
				PureDesktop.ThrowException(this, e);
			}
		}

		#endregion

		#region IStep Members

		/// <summary>
		/// Step
		/// </summary>
		public int Step
		{
			get
			{
				return step;
			}
			set
			{
				step = value;
			}
		}

		#endregion

		#region Specific Members

		/// <summary>
		/// String representation of internal variables
		/// </summary>
		public Hashtable Variables
		{
			get
			{
				return vars;
			}
			set
			{
				vars = value;
			}
		}

		/// <summary>
		/// Recursive formulas
		/// </summary>
		public Hashtable Formulas
		{
			set
			{
				foreach (char c in value.Keys)
				{
					if (!vars.ContainsKey(c))
					{
						throw new Exception("Shortage of variables");
					}
				}
				foreach (char c in vars.Keys)
				{
					if (!value.ContainsKey(c))
					{
						throw new Exception("Shortage of formulas");
					}
				}
				foreach (char c in value.Keys)
				{
					object[] o = vars[c] as object[];
					o[1] = value[c];
				}
			}
		}

		/// <summary>
		/// All variabes those are not internal
		/// </summary>
		public string AllExternalVariables
		{
			get
			{
				string s = "";
				foreach (object[] o in vars.Values)
				{
					string f = o[1] as string;
					if (f == null)
					{
						continue;
					}
					MathFormula form = MathFormula.FromString(MathSymbolFactory.Sizes, f);
					string v = ElementaryObjectDetector.GetVariables(form);
					foreach (char c in v)
					{
						if (s.IndexOf(c) < 0)
						{
							s += c;
						}
					}
				}
				return s;
			}
		}

		/// <summary>
		/// String of alias parameters
		/// </summary>
		public string AliasesString
		{
			set
			{
				aliases.Clear();
				foreach (char c in value)
				{
					aliases[c] = new object[3];
				}
				string str = AllExternalVariables;
				pars.Clear();
				parameters.Clear();
				foreach (char c in str)
				{
					if (vars.ContainsKey(c))
					{
						continue;
					}
					if (aliases.ContainsKey(c))
					{
						continue;
					}
					object[] o = new object[3];
					pars[c] = o;
				}
			}
			get
			{
				string s = "";
				foreach (char c in aliases.Keys)
				{
					s += c;
				}
				return s;
			}
		}

		/// <summary>
		/// Dynalical parameter
		/// </summary>
		public DynamicalParameter Parameter
		{
			set
			{
				string s = value.Variables;
				parameters.Clear();
				foreach (char c in s)
				{
					parameters[c] = value[c];
				}
			}
		}

		/// <summary>
		/// External arguments table
		/// </summary>
		public Hashtable Arguments
		{
			get
			{
				return pars;
			}
			set
			{
				string str = AllExternalVariables;
				string s = "";
				foreach (char c in str)
				{
					if (vars.ContainsKey(c))
					{
						continue;
					}
					if (aliases.ContainsKey(c))
					{
						continue;
					}
					s += c;
				}
				foreach (char c in s)
				{
					if (!value.ContainsKey(c))
					{
						throw new Exception(DataConsumer.VariablesShortage);
					}
				}
				pars = value;
				acceptParameters();
				acceptFormulas();
			}
		}

		/// <summary>
		/// External arguments table
		/// </summary>
		public ArrayList ExternalArguments
		{
			get
			{
				ArrayList list = new ArrayList();
				foreach (char c in pars.Keys)
				{
					string s = pars[c] as string;
					list.Add(c + " = " + s);
				}
				return list;
			}
		}

		/// <summary>
		/// External aliases table
		/// </summary>
		public Hashtable ExternalAliases
		{
			get
			{
				return externalAls;
			}
			set
			{
				externalAls = value;
				externalAliases.Clear();
				foreach (char c in externalAls.Keys)
				{
					string s = externalAls[c] as string;
					object[] o = DataConsumer.FindAlias(this, s);
					externalAliases[c] = o;
				}
			}
		}

		/// <summary>
		/// Comments
		/// </summary>
		public ArrayList Comments
		{
			get
			{
				return comments;
			}
			set
			{
				comments = value;
			}
		}

		/// <summary>
		/// Accepts formulas
		/// </summary>
		private void acceptFormulas()
		{
			Hashtable table = new Hashtable();
			foreach (char c in parameters.Keys)
			{
				IMeasure m = parameters[c] as IMeasure;
				table[c] =	m.Type; 
			}
			foreach (char c in vars.Keys)
			{
				object[] o = vars[c] as object[];
				table[c] = o[2];
			}
			foreach (char c in aliases.Keys)
			{
				object[] o = aliases[c] as object[];
				table[c] = ElementaryBinaryOperation.DoubleType;
			}
			IFormulaObjectCreator creator = new FormulaArrayObjectCreator(
				new ElementaryObjectsCreator(table));
			arg = new ElementaryObjectArgument();
			variables.Clear();
			foreach (char c in vars.Keys)
			{
				variables[c] = new object[3];
			}
			foreach (char c in vars.Keys)
			{
				object[] os = vars[c] as object[];
				object[] ol = variables[c] as object[];
				string f = os[1] as string;
				MathFormula form = MathFormula.FromString(MathSymbolFactory.Sizes, f);
				ObjectFormulaTree tree = new ObjectFormulaTree(form.FullTransform, creator);
				if (!os[2].Equals(tree.ReturnType))
				{
					throw new Exception("Illegal return type");
				}
				ol[1] = tree;
				//ol[2] = calcC.Create(tree);
				arg.Add(tree);
			}
			output.Clear();
			foreach (char c in variables.Keys)
			{
				output.Add(Variable.GetMeasure(c, this));
			}
		}

		/// <summary>
		/// Accepts external parameters
		/// </summary>
		private void acceptParameters()
		{
			parameters.Clear();
			foreach (char c in pars.Keys)
			{
				string s = pars[c] as string;
                foreach (IMeasurements m in measurements)
				{
					/*IAssociatedObject obj = m as IAssociatedObject;
					INamedComponent nc = obj.Object as INamedComponent;*/
					string name = DataConsumer.GetName(this, m);//nc.Name;
					for (int i = 0; i < m.Count; i++)
					{
						IMeasure mea = m[i];
						string str = name + "." + mea.Name;
						if (s.Equals(str))
						{
							parameters[c] = mea;
							goto next;
						}
					}
				}
			next:
				continue;
			}
		}

		#endregion

		#region Variable

		/// <summary>
		/// Auxiliary class for measurement providinf
		/// </summary>
		class Variable
		{

			/// <summary>
			/// Measure key
			/// </summary>
			private char key;

			/// <summary>
			/// Parent
			/// </summary>
			private Recursive r;

			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="key">Measure key</param>
			/// <param name="r">Parent</param>
			private Variable(char key, Recursive r)
			{
				this.key = key;
				this.r = r;
			}

			/// <summary>
			/// Variable measurements
			/// </summary>
			/// <param name="key">Measure key</param>
			/// <param name="r">Parent</param>
			/// <returns>The measure</returns>
			static public IMeasure GetMeasure(char key, Recursive r)
			{
				Variable v = new Variable(key, r);
				return v.measure;
			}
			
			/// <summary>
			/// Gets value of measurement
			/// </summary>
			/// <returns></returns>
			private object getValue()
			{
				object[] o = r.variables[key] as object[];
				return o[0];
			}

			/// <summary>
			/// Gets derivation
			/// </summary>
			/// <returns></returns>
			private object getDerivation()
			{
				double a = 0;
				return a;
			}

			/// <summary>
			/// Measure
			/// </summary>
			private IMeasure measure
			{
				get
				{
					object[] o = r.vars[key] as object[];
					return new MeasureDerivation(o[2], new MeasureParameter(getValue), 
						new MeasureParameter(getDerivation), key + "");
				}
			}
		}
		#endregion

	}

}
