using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;


using CategoryTheory;
using DiagramUI;
using DataPerformer;
using GeneralLinearMethod;

namespace Regression
{
	/// <summary>
	/// Regresson based on the aliases 
	/// </summary>
	[Serializable()]
	public class AliasRegression : IDataConsumer, ICategoryObject, IStructuredSelectionConsumer, 
		ISerializable, IStructuredCalculation, IPostSetArrow	
	{
		
		#region Fields

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

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

		/// <summary>
		/// Selections
		/// </summary>
        private List<IStructuredSelectionCollection> selections = new List<IStructuredSelectionCollection>();

		/// <summary>
		/// Selected selections
		/// </summary>
		private Dictionary<int, IStructuredSelection> selectedSelections = new Dictionary<int, IStructuredSelection>();

		/// <summary>
		/// Names of aliases
		/// </summary>
		private ArrayList aliasNames = new ArrayList();

		/// <summary>
		/// Names of measures
		/// </summary>
		private Hashtable measuresNames = new Hashtable();

		/// <summary>
		/// Selecion names
		/// </summary>
		private Hashtable selectionNames = new Hashtable();

		/// <summary>
		/// Measuements
		/// </summary>
		private Hashtable measures = new Hashtable();

		/// <summary>
		/// Aliases
		/// </summary>
		private object[,] aliases;

		/// <summary>
		/// Dispersions
		/// </summary>
		private double[] dispersions;

		/// <summary>
		/// Delta
		/// </summary>
		private double[] delta;

		/// <summary>
		/// Input data
		/// </summary>
		private double[] x;

		/// <summary>
		/// Output data
		/// </summary>
		private double?[] y;

		/// <summary>
		/// Auxiliary output data
		/// </summary>
		private double?[] y1;

		/// <summary>
		/// Auxiliary matrix
		/// </summary>
		private double?[,] h;

		/// <summary>
		/// Associated object
		/// </summary>
		private object obj;

		/// <summary>
		/// Aggregate selection
		/// </summary>
		private AggregateSelection selection = new AggregateSelection();

		/// <summary>
		/// General linear method
		/// </summary>
		private StructuredGLM method;
 
		#endregion
	
		#region Constructors
		public AliasRegression()
		{
		}

		public AliasRegression(SerializationInfo info, StreamingContext context)
		{
			aliasNames = (ArrayList) info.GetValue("AliasNames", typeof(ArrayList));
			measuresNames = (Hashtable) info.GetValue("MeasurementsNames", typeof(Hashtable));
			selectionNames = (Hashtable) info.GetValue("SelectionNames", typeof(Hashtable));
			dispersions = (double[]) info.GetValue("Dispersions", typeof(double[]));
			delta = (double[]) info.GetValue("Delta", typeof(double[]));
			comments = (ArrayList) info.GetValue("Comments", typeof(ArrayList));
		}

		#endregion

		#region IDataConsumer Members

		void IDataConsumer.Add(IMeasurements arrow)
		{
			measurements.Add(arrow);
		}

        void IDataConsumer.Remove(IMeasurements arrow)
		{
			measurements.Remove(arrow);
		}

		public void UpdateChildrenData()
		{
			try
			{
                foreach (IMeasurements m in measurements)
				{
					m.UpdateMeasurements();
				}
			}
			catch (Exception e)
			{
				PureDesktop.ThrowException(this, e);
			}
		}

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

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

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

		#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 IStructuredSelectionConsumer Members

		public void Add(IStructuredSelectionCollection selection)
		{
			selections.Add(selection);
		}

		public void Remove(IStructuredSelectionCollection selection)
		{
			selections.Remove(selection);
		}

		#endregion

		#region ISerializable Members

		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("AliasNames", aliasNames);
			info.AddValue("MeasurementsNames", measuresNames);
			info.AddValue("SelectionNames", selectionNames);
			info.AddValue("Dispersions", dispersions);
			info.AddValue("Delta", delta);
			info.AddValue("Comments", comments);
		}

		#endregion

		#region IStructuredCalculation Members

		public void Calculate(double[] x, IStructuredSelection selection, double?[] y)
		{
			Double a = 0;
			for (int i = 0; i < x.Length; i++)
			{
				IAlias al = aliases[i, 0] as IAlias;
				string s = aliases[i, 1] as string;
				al[s] = x[i];
			}
			UpdateChildrenData();
			if (DataPerformerStrategy.Object != null)
			{
				DataPerformerStrategy.Object.UpdateAll(DataConsumer.Desktop);
			}
			DataConsumer.Reset(this);
			UpdateChildrenData();
			int n = 0;
			for (int i = 0; i < measures.Count; i++)
			{
				IMeasure m = measures[i] as IMeasure;
				object t = m.Type;
				if (t.Equals(a))
				{
					y[n] = (double) m.Parameter();
					++n;
					continue;
				}
				else
				{
					object[] ar = m.Parameter() as object[];
					for (int j = 0; j < ar.Length; j++)
					{
						y[n] = (double) ar[j];
						++n;
					}
				}
			}
		}

		public int Dimension
		{
			get
			{
				return aliases.GetLength(0);
			}
		}

		#endregion

		#region IPostSetArrow Members

		public void PostSetArrow()
		{
			aliases = new object[aliasNames.Count, 2];
			int i = 0;
			foreach (string s in aliasNames)
			{
				object[] o = DataConsumer.FindAlias(this, s);
				aliases[i, 0] = o[0];
				aliases[i, 1] = o[1];
				++i;
			}
			int nmea = 0;
			for (i = 0; i < measuresNames.Count; i++)
			{
				string name = measuresNames[i] as string;
				int n = name.LastIndexOf(".");
				string m = name.Substring(0, n);
                foreach (IMeasurements meas in measurements)
				{
					IAssociatedObject ao = meas as IAssociatedObject;
					//INamedComponent nc = ao.Object as INamedComponent;
					if (!m.Equals(PureDesktop.GetRelativeName(this, ao)))
					{
						continue;
					}
					string suff = name.Substring(n + 1);
					for (int j = 0; j < meas.Count; j++)
					{
						IMeasure mea = meas[j];
						if (!mea.Name.Equals(suff))
						{
							continue;
						}
						measures[nmea] = mea;
						++nmea;
					}
				}
			}
			for (i = 0; i < selectionNames.Count; i++)
			{
				string sn = selectionNames[i] as string;
				foreach (IStructuredSelectionCollection s in selections)
				{
					IAssociatedObject ao = s as IAssociatedObject;
					//INamedComponent nc = ao.Object as INamedComponent;
					string name = PureDesktop.GetRelativeName(this, ao);//nc.Name;
					for (int j = 0; j < s.Count; j++)
					{
						
						if (!sn.Equals(name + "." + s[j].Name))
						{
							continue;
						}
						selectedSelections[i] = s[j];
					}
				}
			}
			Init();
		}


		#endregion

		#region Specific members

		/// <summary>
		/// Aliases
		/// </summary>
		public Hashtable Aliases
		{
			set
			{
				for (int i = 0; i < value.Count; i++)
				{
					if (!value.ContainsKey(i))
					{
						throw new Exception("Abscent alias");
					}
				}
				aliases = new object[value.Count, 2];
				dispersions = new double[value.Count];
				delta = new double[value.Count];
				aliasNames.Clear();
				for (int i = 0; i < value.Count; i++)
				{
					object[] o = value[i] as object[];
					string name = o[0] as string;
					object[] ob = DataConsumer.FindAlias(this, name);
					aliases[i, 0] = ob[0];
					aliases[i, 1] = ob[1];
					dispersions[i] = (double) o[1];
					delta[i] = (double) o[2];
					aliasNames.Add(name);
				}
			}
			get
			{
				Hashtable t = new Hashtable();
				for (int i = 0; i < aliasNames.Count; i++)
				{
					string name = aliasNames[i] as string;
					t[i] = new object[]{name, dispersions[i], delta[i]};
				}
				return t;
			}
		}


		/// <summary>
		/// Names of selections
		/// </summary>
		public Hashtable SelectionsNames
		{
			get
			{
				return selectionNames;
			}
			set
			{
				for (int i = 0; i < value.Count; i++)
				{
					if (!value.ContainsKey(i))
					{
						throw new Exception("Shortage of selections");
					}
				}
				selectionNames = value;
				selectedSelections.Clear();
				foreach (IStructuredSelectionCollection s in selections)
				{
					IAssociatedObject ao = s as IAssociatedObject;
					//INamedComponent nc = ao.Object as INamedComponent;
					string name = PureDesktop.GetRelativeName(this, ao);
					foreach (int i in selectionNames.Keys)
					{
						for (int j = 0; j < s.Count; j++)
						{
							string sn = name + "." + s[j].Name;
							if (sn.Equals(selectionNames[i]))
							{
								selectedSelections[i] = s[j];
								break;
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// Selections
		/// </summary>
		public List<IStructuredSelectionCollection> Selections
		{
			get
			{
				return selections;
			}
		}

		/// <summary>
		/// Names of neasurements
		/// </summary>
		public Hashtable MeasuresNames
		{
			get
			{
				return measuresNames;
			}
			set
			{
				for (int i = 0; i < value.Count; i++)
				{
					if (!value.ContainsKey(i))
					{
						throw new Exception("Shortage of measurements");
					}
				}
				measuresNames = value;
				measures.Clear();
				for (int i = 0; i < measuresNames.Count; i++)
				{
					string s = measuresNames[i] as string;
					int n = s.LastIndexOf(".");
					string name = s.Substring(0, n);
					foreach (IAssociatedObject ao in measurements)
					{
						IAssociatedObject ob = null;
						if (ao is DataLink)
						{
							IArrowLabel ar = ao.Object as IArrowLabel;
							IObjectLabel l = ar.Target;
							ob = l.Object as IAssociatedObject;
						}
						else
						{
							ob = ao;
						}
						INamedComponent nc = ob.Object as INamedComponent;
						if (!name.Equals(PureDesktop.GetRelativeName(this, ao)))
						{
							continue;
						}
						string suffix = s.Substring(n + 1);
						IMeasurements meas = ob as IMeasurements;
						for (int j = 0; j < meas.Count; j++)
						{
							IMeasure m = meas[j];
							if (m.Name.Equals(suffix))
							{
								measures[i] = m;
								goto fin;
							}
						}
					fin:
						int k = 0;
						++k;
					}
				}
			}
		}
		
		/// <summary>
		/// Initialaztion
		/// </summary>
		public void Init()
		{
			selection.Selections = selectedSelections;
			method = new StructuredGLM(selection, this);
		}

		/// <summary>
		/// Updates selections
		/// </summary>
		public void UpdateSelections()
		{
			foreach (object o in selectedSelections.Values)
			{
				if (o is IUpdatableSelection)
				{
					IUpdatableSelection s = o as IUpdatableSelection;
					s.UpdateSelection();
				}
			}
		}

		/// <summary>
		/// Performs iteration step
		/// </summary>
		/// <returns>Sogma0</returns>
		public double Iterate()
		{
			MeasuresNames = measuresNames;
			SelectionsNames = selectionNames;
			Init();
			int n = Dimension;
			int l = selection.DataDimension;
			if (x == null)
			{
				x = new double[n];
			}
			else if (x.Length != n)
			{
				x = new double[n];
			}
			if (y == null)
			{
				y = new double?[l];
				y1 = new double?[l];
			}
			else if (y.Length != l)
			{
				y = new double?[l];
				y1 = new double?[l];
			}
			if (h == null)
			{
				h = new double?[n, l];
			}
			else if ((h.GetLength(0) != n) | (h.GetLength(1) != l))
			{
				h = new double?[n, l];
			}
			for (int i = 0; i < x.Length; i++)
			{
				IAlias al = aliases[i, 0] as IAlias;
				string s = aliases[i, 1] as string;
				x[i] = (double) al[s];
			}
			double sigma = method.Iterate(x, delta, dispersions, y, y1, h);
			for (int i = 0; i < x.Length; i++)
			{
				IAlias al = aliases[i, 0] as IAlias;
				string s = aliases[i, 1] as string;
				al[s] = x[i];
			}
			return sigma;
		}
		
		/// <summary>
		/// Gets sigma
		/// </summary>
		public double SquareResidual
		{
			get
			{
				int n = Dimension;
				int l = selection.DataDimension;
				if (x == null)
				{
					x = new double[n];
				}
				else if (x.Length != n)
				{
					x = new double[n];
				}
				if (y == null)
				{
					y = new double?[l];
				}
				else if (y.Length != l)
				{
					y = new double?[l];
				}
				return (double) method.GetSquareResidual(x, y);
			}
		}

		/// <summary>
		/// Dimension of data
		/// </summary>
		public int DataDimension
		{
			get
			{
				return selection.DataDimension;
			}
		}

		/// <summary>
		/// Sets aliases
		/// </summary>
		public void SetAliases()
		{
			for (int i = 0; i < x.Length; i++)
			{
				IAlias al = aliases[i, 0] as IAlias;
				string s = aliases[i, 1] as string;
				al[s] = x[i];
			}
		}

		/// <summary>
		/// Residuals
		/// </summary>
		public double?[] Residuals
		{
			get
			{
				return method.Residuals;
			}
		}

		
		/// <summary>
		/// Data
		/// </summary>
		public double?[] Data
		{
			get
			{
				return method.Data;
			}
		}

		/// <summary>
		/// Data processing method
		/// </summary>
		public StructuredGLM Method
		{
			get
			{
				return method;
			}
		}

		#endregion
	}

	public class AggregateSelection : IStructuredSelection
	{ 
		#region Fields
		private Dictionary<int, IStructuredSelection> selections = new Dictionary<int,IStructuredSelection>();
		private string name = "";
		#endregion

		#region IStructuredSelection Members

		public int DataDimension
		{
			get
			{
				int dim = 0;
				foreach (IStructuredSelection s in selections.Values)
				{
					dim += s.DataDimension;
				}
				return dim;
			}
		}

		public double? this[int n]
		{
			get
			{
				int m;
				IStructuredSelection s = chooseSelection(n, out m);
				return s[m];
			}
		}

		public double GetWeight(int n)
		{
			int m;
			IStructuredSelection s = chooseSelection(n, out m);
			return s.GetWeight(m);
		}

		public double GetApriorWeight(int n)
		{
			int m;
			IStructuredSelection s = chooseSelection(n, out m);
			return s.GetApriorWeight(m);
		}

		public int GetTolerance(int n)
		{
			int m;
			IStructuredSelection s = chooseSelection(n, out m);
			return s.GetTolerance(m);
		}

		public void SetTolerance(int n, int tolerance)
		{
			int m;
			IStructuredSelection s = chooseSelection(n, out m);
			s.SetTolerance(m, tolerance);
		}

		/// <summary>
		/// The "is fixed amount" sign
		/// </summary>
		public bool HasFixedAmount
		{
			get
			{
				foreach (IStructuredSelection s in selections.Values)
				{
					if (!s.HasFixedAmount)
					{
						return false;
					}
				}
				return true;
			}
		}

		/// <summary>
		/// Selection name
		/// </summary>
		public string Name
		{
			get
			{
				return name;
			}
		}

		#endregion

		#region Specific members

		public void Clear()
		{
			selections.Clear();
		}

		public Dictionary<int, IStructuredSelection> Selections
		{
			set
			{
				for (int i = 0; i < value.Count; i++)
				{
					if (!value.ContainsKey(i))
					{
						throw new Exception("Illegal selection number");
					}
					if (!(value[i] is IStructuredSelection))
					{
						throw new Exception("Component is not a selection");
					}
				}
				selections = value;
			}
		}
		
		private IStructuredSelection chooseSelection(int n, out int m)
		{
			int k = 0;
			for (int i = 0; i < selections.Count; i++)
			{
				IStructuredSelection s = selections[i];
				int d = s.DataDimension;
				if (n < k + d)
				{
					m = n - k;
					return s;
				}
				k += d;
			}
			m = -1;
			return null;
		}
		#endregion
	}


	/// <summary>
	/// Consumer of structured selection
	/// </summary>
	public interface IStructuredSelectionConsumer
	{
		/// <summary>
		/// Adds selection collection
		/// </summary>
		/// <param name="selection">Selection to add</param>
		void Add(IStructuredSelectionCollection selection);

		/// <summary>
		/// Removes selection collecion
		/// </summary>
		/// <param name="selection">Selection to remove</param>
		void Remove(IStructuredSelectionCollection selection);
	}

	[Serializable()]
	public class SelectionLink : ICategoryArrow, IRemovableObject, ISerializable
	{
		#region Fields
		private object obj;
		private int a = 0;

		private IStructuredSelectionConsumer source;

		private IStructuredSelectionCollection target;

		#endregion

		#region Constructors
		public SelectionLink()
		{
		}

		public SelectionLink(SerializationInfo info, StreamingContext context)
		{
			info.GetValue("A", typeof(int));
		}

		#endregion

		#region ICategoryArrow Members

		public ICategoryObject Source
		{
			get
			{
				return source as ICategoryObject;
			}
			set
			{
				if (!(value is IStructuredSelectionConsumer))
				{
					CategoryException.ThrowIllegalSourceException();
				}
				source = value as IStructuredSelectionConsumer;
			}
		}

		public ICategoryObject Target
		{
			get
			{
				return target as ICategoryObject;
			}
			set
			{
				if (!(value is IStructuredSelectionCollection))
				{
					CategoryException.ThrowIllegalTargetException();
				}
				ICategoryObject s = source as ICategoryObject;
				INamedComponent ns = s.Object as INamedComponent;
				INamedComponent nt = value.Object as INamedComponent;
				if (nt.Root.Ord >= ns.Root.Ord)
				{
					throw new Exception(DataLink.SetProviderBefore);
				}
				target = value as IStructuredSelectionCollection;
				source.Add(target);
			}
		}

		public bool IsMonomorphism
		{
			get
			{
				return false;
			}
		}

		public bool IsEpimorphism
		{
			get
			{
				return false;
			}
		}

		public bool IsIsomorphism
		{
			get
			{
				return false;
			}
		}

		public ICategoryArrow Compose(ICategory category, ICategoryArrow next)
		{
			return null;
		}

		#endregion

		#region IAssociatedObject Members

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

		#endregion

		#region IRemovableObject Members

		public void RemoveObject()
		{
			source.Remove(target);
		}

		#endregion

		#region ISerializable Members

		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("A", a);
		}

		#endregion
	}

	/// <summary>
	/// Updatable selection
	/// </summary>
	public interface IUpdatableSelection
	{
		/// <summary>
		/// Updates selection
		/// </summary>
		void UpdateSelection();
	}

}
