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


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

namespace DataPerformer
{
	/// <summary>
	/// Function of double array.
	/// </summary>
	[Serializable()]
	public class DoubleArrayFunction : ICategoryObject, ISerializable, IObjectOperation, IOperationAcceptor, IPostSetArrow
	{
		
		#region Fields
		
		private static readonly Double a = 0;
		private object obj;
		private object returnType;
		private object returnValue;
		private Hashtable table = new Hashtable();
		private ArrayList arg = new ArrayList();
		private int[][] ranks;
		private int[] rank;
		private object[][] yy;
		private double step = 0;
		private double begin;
        private object[] y;

		
		#endregion

		#region Constructors

		public DoubleArrayFunction(object type)
		{
			if (type == null)
			{
				return;
			}
			returnType = type;
			init();
		}

		public DoubleArrayFunction(SerializationInfo info, StreamingContext context)
		{
			returnType = info.GetValue("Type", typeof(object));
			table = info.GetValue("Table", typeof(Hashtable)) as Hashtable;
			init();
		}

		#endregion

		#region ISerializable Members

		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("Type", returnType);
			info.AddValue("Table", table);
		}

		#endregion

		#region ICategoryObject Members

		public ICategoryArrow Id
		{
			get
			{
				return null;
			}
		}

		public ICategory Category
		{
			get
			{
				return null;
			}
		}

		#endregion

		#region IAssociatedObject Members

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

		#endregion

		#region IObjectOperation Members

		public object this[object[] x]
		{
			get
			{
				double a = (double) x[0];
				return this[a];
			}
		}

		public object ReturnType
		{
			get
			{
				return returnType;
			}
		}

		public int Arity
		{
			get
			{
				return 1;
			}
		}

		public bool IsPowered
		{
			get
			{
				return true;
			}
		}

		#endregion

		#region IOperationAcceptor Members

		public IObjectOperation Accept(object type)
		{
			if (type.Equals(a))
			{
				return this;
			}
			return null;
		}

		#endregion

		#region IPostSetArrow Members

		public void PostSetArrow()
		{
			arg = new ArrayList(table.Keys);
			arg.Sort();
			begin = (double) arg[0];
			double b = (double) arg[1];
			step = b - begin;
			for (int i = 2; i < arg.Count; i++)
			{
				double c = (double) arg[i];
				if (Math.Abs((c - b) - step) > (0.0000000000001 * step))
				{
					step = 0;
					return;
				}
				b = c;
			}
		}

		#endregion

		#region Specific Members
		
		public object this[double x]
		{
			get
			{
				double a = begin;
				if (x < a)
				{
					return table[a];
				}
				if (step != 0)
				{
					int n = (int) ((x - begin) / step);
					if (n >= arg.Count)
					{
						return table[arg[arg.Count - 1]];
					}
					double c = (double) arg[n];
					return table[c];
				}
				foreach (double b in arg)
				{
					if (b >= x)
					{
						return table[b];
					}
				}
				return table[arg[arg.Count - 1]];
			}
			set
			{
				object[] o = newArray;
				setObject(value, o);
				table[x] = o;
			}
		}

		private object[] newArray
		{
			get
			{
				return createArray(returnType as object[]);
			}
		}

		private object[] createArray(object[] o)
		{
			object[] ob = new object[o.Length];
			if (o[0] is object[])
			{
				for (int i = 0; i < o.Length; i++)
				{
					ob[i] = createArray(o[i] as object[]);
				}
				return ob;
			}
			double x = 0;
			for (int i = 0; i < o.Length; i++)
			{
				ob[i] = x;
			}
			return ob;
		}

		private void setObject(object o1, object o2)
		{
			object[] ob1 = o1 as object[];
			object[] ob2 = o2 as object[];
			if (!(ob1[0] is object[]))
			{
				for (int i = 0; i < ob1.Length; i++)
				{
					ob2[i] = ob1[i];
				}
				return;
			}
			for (int i = 0; i < ob1.Length; i++)
			{
				setObject(ob1[i], ob2[i]);
			}
		}

		private void init()
		{
			ArrayOperation.CreateAllArrays(this, new object[]{a}, out y, out yy, out rank, out ranks);
            returnType = new ArrayReturnType(a, rank);
		}

		#endregion

	}
}
