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>
	/// Vector formula data transformer
	/// </summary>
	[Serializable()]
	public class VectorFormulaConsumer : DataConsumer, IMeasurements, ISerializable,
		IPostSetArrow, IAlias, ICheckCorrectness	
	{
		
		/// <summary>
		/// Resource string
		/// </summary>
		public static readonly string ExternalParameter_ = "External parameter ";

		/// <summary>
		/// Resource string
		/// </summary>
		public static readonly string _IsNotDefined = " is not defined";

		/// <summary>
		/// Output measurements
		/// </summary>
		private IMeasure[] measurements;

		/// <summary>
		/// Results of calculation
		/// </summary>
		private object[,] result;

		/// <summary>
		/// String representation of formulas
		/// </summary>
		private string[] formulaString;

		/// <summary>
		/// Formulas
		/// </summary>
		private MathFormula[] formula;

		/// <summary>
		/// Trees
		/// </summary>
		private ObjectFormulaTree[] trees;


		/// <summary>
		/// Formula arguments
		/// </summary>
		private ElementaryObjectArgument arg;

		/// <summary>
		/// Input parameter
		/// </summary>
		private DynamicalParameter par;

		/// <summary>
		/// Input arguments
		/// </summary>
		private ArrayList arguments = new ArrayList();

		/// <summary>
		/// All variables
		/// </summary>
		private string allVariables;

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

		/// <summary>
		/// Derivation trees
		/// </summary>
		private Dictionary<int, Dictionary<char, ObjectFormulaTree>> derivations;

		/// <summary>
		/// Derivation trees
		/// </summary>
		//private Hashtable deriCalc;

		/// <summary>
		/// External unaries
		/// </summary>
		private ArrayList operations = new ArrayList();


		/// <summary>
		/// Names of unaries
		/// </summary>
		private Hashtable operationNames = new Hashtable();

		/// <summary>
		/// The "is serialized" sign
		/// </summary>
		private bool isSerialized = false;

		/// <summary>
		/// Operation table
		/// </summary>
		private Hashtable opTable;

		/// <summary>
		/// The "Calculate derivation" sign
		/// </summary>
		private bool calculateDerivation = true;


		/// <summary>
		/// Constructor
		/// </summary>
		public VectorFormulaConsumer() : base(39)
		{
			init();
			arguments = new ArrayList();
		}
		

		/// <summary>
		/// Constructor
		/// </summary>
		/// </summary>
		/// <param name="info">Serialization info</param>
		/// <param name="context">Streaming context</param>
		public VectorFormulaConsumer(SerializationInfo info, StreamingContext context) : 
			base(39)
		{
			try
			{
				formulaString = (string[])info.GetValue("Formulas", typeof(string[]));
				isSerialized = true;
				arguments = (ArrayList) info.GetValue("Arguments", typeof(ArrayList));
				parameters = (Hashtable) info.GetValue("Parameters", typeof(Hashtable));
				operationNames = (Hashtable) info.GetValue("Unaries", typeof(Hashtable));
				comments = (ArrayList) info.GetValue("Comments", typeof(ArrayList));
				calculateDerivation = (bool) info.GetValue("CalculateDerivation", typeof(bool));
			}
			catch (Exception)
			{
			}
			init();
		}

		/// <summary>
		/// ISerializable interface implementation
		/// </summary>
		/// <param name="info">Serialization info</param>
		/// <param name="context">Streaming context</param>
		new public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("Formulas", formulaString);
			info.AddValue("Arguments", arguments);
			info.AddValue("Parameters", parameters);
			info.AddValue("Unaries", operationNames);
			info.AddValue("Comments", comments);
			info.AddValue("CalculateDerivation", calculateDerivation);
		}

		/// <summary>
		/// Creates correspond xml
		/// </summary>
		/// <param name="doc">document to create element</param>
		/// <returns>The created element</returns>
		/*new public XmlElement CreateXml(XmlDocument doc)
		{
			
			XmlElement el = doc.CreateElement("VectorFormulaConsumer");
			for (int i = 0; i < formulaString.Length; i++)
			{
				XmlElement ef = doc.CreateElement("VectorFormula");
				XmlAttribute attr = doc.CreateAttribute("Formula");
				attr.Value = formulaString[i];
				ef.Attributes.Append(attr);
				XmlAttribute id = doc.CreateAttribute("FormulaId");
				id.Value = i + "";
				ef.Attributes.Append(id);
				el.AppendChild(ef);
			}
			return el;
		}*/



		/// <summary>
		/// The count of measurements
		/// </summary>
		int IMeasurements.Count
		{
			get
			{
				return Dimension;
			}
		}

		/// <summary>
		/// Access to n - th measurement
		/// </summary>
    	IMeasure IMeasurements.this[int n]
		{
			get
			{
				if (measurements == null)
				{
					PureDesktop.ThrowException(this, new Exception("Undefined measurements"));
				}
				IMeasure m = measurements[n];
				if (m == null)
				{
					PureDesktop.ThrowException(this, new Exception("Undefined measure"));
				}
				return m;
			}
		}

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

		/// <summary>
		/// Adds operation
		/// </summary>
		/// <param name="u">The source to add</param>
		public void AddOperation(IObjectOperation op)
		{
			operations.Add(op);
		}

		/// <summary>
		/// Count of external operations
		/// </summary>
		public int OperationsCount
		{
			get
			{
				return operations.Count;
			}
		}

		/// <summary>
		/// Gets i - th external opreation
		/// </summary>
		/// <param name="i">The index of operation</param>
		/// <returns>The i - th unary</returns>
		public IObjectOperation GetOperation(int i)
		{
			return operations[i] as IObjectOperation;
		}

		/// <summary>
		/// Table of unaries
		/// </summary>
		/*	public Hashtable UnaryTable
			{
				set
				{
					setUnaryNames(value);
					setUnaryTrees(value);
				}
				get
				{
					if (trees[0] != null)
					{
						return ElementaryUnaryOperation.GetUnaryTable(trees);
					}
					return new Hashtable();
				}
			}*/

		public ArrayList OperationNumbers
		{
			get
			{
				return SeriesSymbol.GetOperationIndexes(formula);
			}
		}

		/// <summary>
		/// Names of external unatries
		/// </summary>
		public Hashtable OperationNames
		{
			get
			{
				return operationNames;
			}
		}

		

		/// <summary>
		/// Removes operation source
		/// </summary>
		/// <param name="op">The source to add</param>
		public void RemoveOperation(IObjectOperation op)
		{
			operations.Remove(op);
		}


		/// <summary>
		/// The "Calculate derivation" sign
		/// </summary>
		public bool CalculateDerivation
		{
			get
			{
				return calculateDerivation;
			}
			set
			{
				calculateDerivation = value;
			}
		}


		/// <summary>
		/// Input parameters
		/// </summary>
		public string InputParameters
		{
			get
			{
				string var = Variables;
				string s = "";
				foreach (char c in var)
				{
					if (parameters.ContainsKey("" + c))
					{
						continue;
					}
					s += c;
				}
				return s;
			}
		}


		/// <summary>
		/// Updates measurements data
		/// </summary>
		public void UpdateMeasurements()
		{
			if (IsUpdated)
			{
				return;
			}
			try
			{
				if (par == null)
				{
					throw new Exception(DynamicalParameter.UndefinedParameters);
				}
				if (trees == null)
				{
					throw new Exception("Formulas are not accepted");
				}
				UpdateChildrenData();
				par.Set(arg);
				int n = Dimension;
				Double a = 0;
				for (int i = 0; i < n; i++)
				{
					result[i, 0] = trees[i].Result;
					double derivation = 0;
					if (!calculateDerivation)
					{
						continue;
					}
					string str = arg.Variables;
					foreach (char c in str)
					{
						string s = c + "";
						if (parameters.ContainsKey(s))
						{
							continue;
						}
						IMeasure m = par[c];
						if (m.Type.Equals(a))
						{
                            if (m is IDerivation)
                            {
                                IDerivation p = m as IDerivation;
                                if (p == null)
                                {
                                    derivation = 0;
                                    break;
                                }
                                if (derivations.ContainsKey(i))
                                {
                                    Dictionary<char, ObjectFormulaTree> der = derivations[i];
                                  //  Hashtable dc = deriCalc[i] as Hashtable;
                                    if (der.ContainsKey(c))
                                    {
                                        //ICalculator calc = dc[c] as ICalculator;
                                        //derivation += (double)calc.Result * (double)p();
                                        ObjectFormulaTree t = der[c] as ObjectFormulaTree;
                                        derivation += (double)t.Result * p.Derivation;
                                    }
                                }
                            }
						}
					}
					result[i, 1] = derivation;
				}
				isUpdated = true;
			}
			catch (Exception e)
			{
				string s = e.StackTrace;
				PureDesktop.ThrowException(this, e);
			}
		}


		/// <summary>
		/// Accept parameters
		/// </summary>
		/// <param name="s">String that contains parameters characters</param>
		public void AcceptParameters(string s)
		{
			parameters.Clear();
			string str = "";
			str = ElementaryObjectDetector.GetVariables(formula);
			foreach (char c in s)
			{
				if (str.IndexOf(c) < 0)
				{
					throw new Exception("Illegal formula parameter");
				}
			}
			foreach (char c in s)
			{
				double a = 0;
				parameters["" + c] = a;
				arg[c] = a;
			}
		}
	

		/// <summary>
		/// The operation that performs after arrows setting
		/// </summary>
		public void PostSetArrow()
		{
			try
			{
				DynamicalParameter parameter = new DynamicalParameter();
                foreach (IMeasurements measurements in measurementsData)
				{
					/*IAssociatedObject ass = measurements as IAssociatedObject;
					INamedComponent nc = ass.Object as INamedComponent;*/
					string name = DataConsumer.GetName(this, measurements);//nc.Name;
					for (int i = 0; i < measurements.Count; i++)
					{
						IMeasure measure = measurements[i];
						string p = name + "." + measure.Name;
						foreach (string s in arguments)
						{
							if (s.Substring(4).Equals(p))
							{
								char c = s[0];
								parameter.Add(c, measure);
							}
						}
					}
				}
				foreach (string s in arguments)
				{
					if (s.Substring(4).Equals("Time"))
					{
						parameter.Add(s[0], DataConsumer.TimeMeasure);
					}
				}
				Parameter = parameter;
				string argStr = AllVariables; 
				foreach (string key in parameters.Keys)
				{
					arg[key[0]] = parameters[key];
				}
				postSetUnary();
			}
			catch (Exception ex)
			{
				PureDesktop.ThrowException(this, ex);
			}
		}


		/// <summary>
		/// The name of measurements source
		/// </summary>
		public string SourceName
		{
			get
			{
				INamedComponent comp = obj as INamedComponent;
				return comp.Name;
			}
		}
		
		/// <summary>
		/// Arguments of this object
		/// </summary>
		public ArrayList Arguments
		{
			get
			{
				return arguments;
			}
			set
			{
				string str = Variables;
				bool b = false;
				foreach (char c in str)
				{
					foreach (string s in value)
					{
						if (s[0] == c)
						{
							goto m;
						}
					}
					throw new Exception(VariablesShortage);
				m:
					b = b;
				}
				arguments = value;
			}
		}

		/// <summary>
		/// The input dynamical parameter
		/// </summary>
		public DynamicalParameter Parameter
		{
			set
			{
				par = value;

				int n = Dimension;
				measurements = new IMeasure[n];
				result = new object[n, 2];
				trees = new ObjectFormulaTree[n];
				if (!isSerialized)
				{
					arguments.Clear();
				}
				Hashtable ops = operationTable;
				if (!isSerialized)
				{
					ops = opTable;
				}
				isSerialized = false;
				Hashtable table = new Hashtable();
				string var = par.Variables;
				Double a = 0;
				if (allVariables == null)
				{
					allVariables = AllVariables;
				}
				foreach (char c in allVariables)
				{
					table[c] = a;
				}
				foreach (char c in var)
				{
					IMeasure m = par[c];
					table[c] = m.Type;
				}
				ElementaryObjectsCreator prot = new ElementaryObjectsCreator(table);
				prot.Add(ElementaryRealDetector.Object);
				IFormulaObjectCreator creator = new FormulaArrayObjectCreator(prot);
				for (int i = 0; i < n; i++)
				{
					MathFormula f = formula[i].FullTransform;
					SeriesSymbol.SetOperations(f, ops);
					trees[i] = new ObjectFormulaTree(f, creator);
				}
                derivations = new Dictionary<int, Dictionary<char, ObjectFormulaTree>>();
				//deriCalc = new Hashtable();
				arg = new ElementaryObjectArgument();
				arg.Add(trees);
				string str = par.Variables;
				Hashtable unaryTable = ElementaryUnaryOperation.GetUnaryTable(trees);
				for (int i = 0; i < n; i++)
				{
					if (a.Equals(trees[i].ReturnType))
					{
						string vars = ElementaryObjectDetector.GetVariables(formula[i]);
						string rvar = "";
						foreach (char c in vars)
						{
							if (str.IndexOf(c) >= 0)
							{
								IMeasure m = par[c];
								if (a.Equals(m.Type))
								{
                                    if (!(m is IDerivation))
									{
										goto createMeasure;
									}
								}
								rvar += c;
							}
						}
						derivations[i] = new Dictionary<char,ObjectFormulaTree>();
						//deriCalc[i] = new Hashtable();
						foreach (char c in rvar)
						{
							if (a.Equals(table[c]))
							{
								ObjectFormulaTree der = DerivationPerformer.Derivation(trees[i], "" + c);
                                //der = ElementaryFormulaSimplification.Object.Simplify(der);
								if (der != null)
								{
									arg.Add(der);
									Dictionary<char, ObjectFormulaTree> deriv = derivations[i];
									deriv[c] = der;
									//Hashtable ca = deriCalc[i] as Hashtable;
									//ca[c] = calc;
								}
							}
						}
					}
				createMeasure:
					measurements[i] = MeasureFormula.GetMeasure(this, i);
				}
			}
		}

		/// <summary>
		/// All formulas variables
		/// </summary>
		public string AllVariables
		{
			get
			{
				try
				{
					if (allVariables == null)
					{
						formula = new MathFormula[formulaString.Length];
						for (int i = 0; i < formula.Length; i++)
						{
							formula[i] = MathFormula.FromString(MathSymbolFactory.Sizes, formulaString[i]);
						}
						allVariables = ElementaryObjectDetector.GetVariables(formula);
					}
					return allVariables;
				}
				catch (Exception)
				{
				}
				return "";
			}
		}
		
		/// <summary>
		/// Variables
		/// </summary>
		public string Variables
		{
			get
			{
				try
				{
					string s = "";
					string str = ElementaryObjectDetector.GetVariables(formula);
					foreach (char c in str)
					{
						if (!parameters.ContainsKey("" + c)  & s.IndexOf(c) < 0)
						{
							s += c;
						}
					}					
					return s;
				}
				catch (Exception)
				{
				}
				return "";
			}
		}
		
		/// <summary>
		/// Create aliases
		/// </summary>
		/// <param name="str">String of aliases</param>
		public void CreateAliases(string str)
		{
			parameters.Clear();
			foreach (char c in str)
			{
				double a = 0;
				parameters["" + c] = a;
			}
		}


		/// <summary>
		/// Names of aliases
		/// </summary>
		public List<string> AliasNames
		{
			get
			{
				List<string> s = new List<string>();
				foreach (string str in parameters.Keys)
				{
					s.Add(str);
				}
				return s;
			}
		}

		/// <summary>
		/// Access to alias object
		/// </summary>
		public object this[string alias]
		{
			get
			{
				return parameters[alias];
			}
			set
			{
				char c = alias[0];
				arg[c] = value;
				parameters[alias] = value;
			}
		}

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


		/// <summary>
		/// Dimension of output vector
		/// </summary>
		public int Dimension
		{
			get
			{
				return formulaString.Length;
			}
			set
			{
				formulaString = new string[value];
				result = null;
				formula = null;
				trees = null;
			}
		}
		
		/// <summary>
		/// Set formula to i - th component
		/// </summary>
		/// <param name="s">The string representation of formula</param>
		/// <param name="i">The component number</param>
		public void SetFormula(string s, int i)
		{
			formulaString[i] = s;
		}

		/// <summary>
		/// Gets i - th component formula
		/// </summary>
		/// <param name="i">The component number</param>
		/// <returns>The string representation of formula</returns>
		public string GetFormula(int i)
		{
			return formulaString[i];
		}

		/// <summary>
		/// Accepts formulas
		/// </summary>
		public void AcceptFormulas()
		{
			try
			{
				int n = Dimension;
				measurements = new IMeasure[n];
				formula = new MathFormula[n];
				result = new object[n, 2];
				parameters.Clear();
				arguments.Clear();
				par = null;
				for (int i = 0; i < n; i++)
				{
					MathFormula form = MathFormula.FromString(MathSymbolFactory.Sizes, formulaString[i]);
					formula[i] = form;
				}
				allVariables = ElementaryObjectDetector.GetVariables(formula);
			}
			catch (Exception e)
			{
				PureDesktop.ThrowException(this, e);
			}
		}

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


		/// <summary>
		/// Accepts formulas
		/// </summary>
		private void acceptFormulas()
		{
			try
			{
				int n = Dimension;
				measurements = new IMeasure[n];
				result = new object[n, 2];
				trees = new ObjectFormulaTree[n];
                derivations = new Dictionary<int, Dictionary<char, ObjectFormulaTree>>();
				//deriCalc = new Hashtable();
				for (int i = 0; i < n; i++)
				{
					MathFormula formula = MathFormula.FromString(MathSymbolFactory.Sizes, formulaString[i]);
					MathFormula f = formula.FullTransform;
					trees[i] = new ObjectFormulaTree(f, ElementaryFunctionsCreator.Object);
                    derivations[i] = new Dictionary<char, ObjectFormulaTree>();
					measurements[i] = MeasureFormula.GetMeasure(this, i);
				}
				arg = new ElementaryObjectArgument();
				arg.Add(trees);
			}
			catch (Exception e)
			{
				PureDesktop.ThrowException(this, e);
			}
	
		}

		/// <summary>
		/// The correctess sign
		/// </summary>
		public bool IsCorrect
		{
			get
			{
				int n = Dimension;
				for (int i = 0; i < n; i++)
				{
					if (trees[i] == null)
					{
						return false;
					}
					Dictionary<char, ObjectFormulaTree> der = derivations[i];
					foreach (char c in der.Keys)
					{
						if (der[c] == null)
						{
							return false;
						}
					}
				}
				string str = arg.Variables;
				foreach (char c in str)
				{
					string s = c + "";
					if (parameters.ContainsKey(s))
					{
						continue;
					}
					IMeasure m = par[c];
					if (m == null)
					{
						return false;
					}
					
				}
				return true;
			}
		}

		public ArrayList Operations
		{
			get
			{
				return operations;
			}
		}

		public Hashtable OperationTable
		{
			set
			{
				SeriesSymbol.SetOperations(formula, value);
				opTable = value;
				setOperationNames(value);
			}
		}
		
		private void setOperationNames(Hashtable table)
		{
			operationNames.Clear();
			foreach (int i in table.Keys)
			{
				ICategoryObject o = table[i] as ICategoryObject;
				IObjectLabel l = o.Object as IObjectLabel;
				operationNames[i] = PureDesktop.GetRelativeName(this, o);
			}
		}

		private void setUnaryTrees(Hashtable table)
		{
			string str = Variables;
			for (int i = 0; i < trees.Length; i++)
			{
				ObjectFormulaTree tree = trees[i];
				if (tree != null)
				{
					ElementaryUnaryOperation.SetUnary(tree, table);
					if (derivations.ContainsKey(i))
					{
						ElementaryUnaryOperation.SetUnary(trees[i], table);
						foreach (char c in str)
						{
							Dictionary<char, ObjectFormulaTree> der = derivations[i];
							if (der.ContainsKey(c))
							{
								ObjectFormulaTree t = der[c] as ObjectFormulaTree;
								ElementaryUnaryOperation.SetUnary(t, table);
							}
						}
					}
				}

			}
		}

		private void postSetUnary()
		{
			Hashtable t = operationTable;
			setUnaryTrees(t);
		}


		private Hashtable operationTable
		{
			get
			{
				Hashtable t = new Hashtable();
				foreach (ICategoryObject o in operations)
				{
					IObjectLabel l = o.Object as IObjectLabel;
					string name = PureDesktop.GetRelativeName(this, o);//l.Name;
					if (!operationNames.ContainsValue(name))
					{
						continue;
					}
					foreach (int i in operationNames.Keys)
					{
						if (name.Equals(operationNames[i]))
						{
							t[i] = o;
							break;
						}
					}
				}
				return t;
			}
		}

		
		/// <summary>
		/// Initialization
		/// </summary>
		private void init()
		{
			
		}
					
	
		
		/// <summary>
		/// Auxiliary class for measurements creation
		/// </summary>
		class MeasureFormula
		{
			/// <summary>
			/// Prefix
			/// </summary>
			const string str = "Formula_";

			/// <summary>
			/// The consumer
			/// </summary>
			private VectorFormulaConsumer consumer;
			
			/// <summary>
			/// The component number
			/// </summary>
			private int i;

			/// <summary>
			/// Gets i - th measurement of consumer 
			/// </summary>
			/// <param name="consumer">The consumer</param>
			/// <param name="i">The measurement number</param>
			/// <returns>The i - th measurement of consumer</returns>
			public static IMeasure GetMeasure(VectorFormulaConsumer consumer, int i)
			{
				MeasureFormula m = new MeasureFormula(consumer, i);
				return m.Measure;
			}

			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="i">The measurement number</param>
			/// <returns>The i - th measurement of consumer</returns>
			MeasureFormula(VectorFormulaConsumer consumer, int i)
			{
				this.consumer = consumer;
				this.i = i;
			}

			/// <summary>
			/// Measurement
			/// </summary>
			IMeasure Measure
			{
				get
				{
					MeasureParameter der = null;
					Double a = 0;
					object type = consumer.trees[i].ReturnType;
					if (a.Equals(type))
					{
						if (consumer.derivations.ContainsKey(i))
						{
							der = new MeasureParameter(derivation);
						}
					}
                    if (der != null)
                    {
                        return new MeasureDerivation(consumer.trees[i].ReturnType, new MeasureParameter(calculate), der, str + (i + 1));
                    }
                    return new Measure(consumer.trees[i].ReturnType, calculate, str + (i + 1));
				}
			}

			/// <summary>
			/// Calculates parameter value
			/// </summary>
			/// <returns>Parameter value</returns>
			private object calculate()
			{
				return consumer.result[i, 0];
			}
			
			/// <summary>
			/// Calculates derivation
			/// </summary>
			/// <returns>Derivation value</returns>
			private object derivation()
			{
				return consumer.result[i, 1];
			}

		}

	}

}
