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

using DiagramUI;
using CategoryTheory;
using RealMatrixProcessor;

namespace DataPerformer
{
    [Serializable()]
    public class IteratorGLM : ISerializable, ICategoryObject, IDataConsumer, IPostSetArrow, IIteratorConsumer
    {

        #region Fields
        protected List<string> sAliases = new List<string>();
        protected List<string> sLeft = new List<string>();
        protected List<string> sRight = new List<string>();
        protected List<List<string>> sR = new List<List<string>>();
        protected double[] dx;
        protected double[,] d;
        protected List<IIterator> iterators = new List<IIterator>();
        protected IAlias[] aliases;
        protected string[] keys;
        protected IMeasure[] left;
        protected IMeasure[] right;
        protected double[,] ht;
        protected double[,] a;
        protected double[,] a1;
        protected double[] z;
        protected double[] z1;
        protected double[,] mr;
        protected double[,] mr1;
        protected IMeasure[,] r;
        protected double[] y;
        protected double[] y1;
        protected double[] yr;
        protected double[,] htr;
        protected double[,] ad;
        protected List<IIterator> ownIteratos = new List<IIterator>();

        static private readonly ConstMeasure zero = new ConstMeasure(0);
        static private readonly ConstMeasure unity = new ConstMeasure(1);

        protected object obj;

        protected IDataConsumer consumer;

        List<IMeasurements> measurements = new List<IMeasurements>();

        #endregion

        #region Constuctors

        public IteratorGLM()
        {
            consumer = this;
        }

        public IteratorGLM(SerializationInfo info, StreamingContext context)
            : this()
        {
            sAliases = info.GetValue("Aliases", typeof(List<string>)) as List<string>;
            sLeft = info.GetValue("Left", typeof(List<string>)) as List<string>;
            sRight = info.GetValue("Right", typeof(List<string>)) as List<string>;
            sR = info.GetValue("R", typeof(List<List<string>>)) as List<List<string>>;
            dx = info.GetValue("Dx", typeof(double[])) as double[];
            d = info.GetValue("D", typeof(double[,])) as double[,];
        }

        #endregion

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Aliases", sAliases, typeof(List<string>));
            info.AddValue("Left", sLeft, typeof(List<string>));
            info.AddValue("Right", sRight, typeof(List<string>));
            info.AddValue("R", sR, typeof(List<List<string>>));
            info.AddValue("Dx", dx, typeof(double[]));
            info.AddValue("D", d, typeof(double[,]));
        }

        #endregion

        #region ICategoryObject Members

        ICategory ICategoryObject.Category
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        ICategoryArrow ICategoryObject.Id
        {
            get { throw new Exception("The method or operation is not implemented."); }
        }

        #endregion

        #region IAssociatedObject Members

        object IAssociatedObject.Object
        {
            get
            {
                return obj;
            }
            set
            {
                obj = value;
            }
        }

        #endregion

        #region IPostSetArrow Members

        void IPostSetArrow.PostSetArrow()
        {
            createArrays();
            setZeros();
            prepare();
        }

        #endregion

        #region IDataConsumer Members

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

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

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

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

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

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

        #endregion

        #region IIteratorConsumer Members

        void IIteratorConsumer.Add(IIterator iterator)
        {
            ownIteratos.Add(iterator);
        }

        void IIteratorConsumer.Remove(IIterator iterator)
        {
            ownIteratos.Remove(iterator);
        }

        #endregion

        #region Specific Members


        public void Set(List<string> aliases, List<string> left,
            List<string> right, List<List<string>> r,
            double[] dx, double[,] d)
        {
            this.sAliases = aliases;
            this.sLeft = left;
            this.sRight = right;
            this.sR = r;
            this.dx = dx;
            this.d = d;
            createArrays();
            setZeros();
            prepare();
        }


        public int DataCount
        {
            get
            {
                return sLeft.Count;
            }
        }

        public int AliasesCount
        {
            get
            {
                return sAliases.Count;
            }
        }


        public List<string> AllAliases
        {
            get
            {
                List<string> l = new List<string>();
                DataConsumer.GetAliases(this, l);
                return l;
            }
        }

        public string GetAliasName(int i)
        {
            return sAliases[i];
        }

        public string GetLeftName(int i)
        {
            return sLeft[i];
        }

        public string GetRightName(int i)
        {
            return sRight[i];
        }

        public List<string> AllMeasurements
        {
            get
            {
                IDataConsumer c = this;
                List<string> list = new List<string>();
                for (int i = 0; i < c.Count; i++)
                {
                    IMeasurements m = c[i];
                    IAssociatedObject ao = m as IAssociatedObject;
                    string on = PureDesktop.GetRelativeName(this, ao) + ".";
                    for (int j = 0; j < m.Count; j++)
                    {
                        string s = on + m[j].Name;
                        list.Add(s);
                    }
                }
                return list;
            }
        }

        public static void GetIterators(IDataConsumer consumer, List<IIterator> iterators)
        {
            getIterators(consumer, iterators);
        }


        public double Iterate()
        {
            double sigma = 0;
            List<IIterator> iterators;
            if (ownIteratos.Count != 0)
            {
                iterators = ownIteratos;
            }
            else
            {
                iterators = this.iterators;
            }
            if (iterators.Count == 0)
            {
                return 1;
            }
            foreach (IIterator it in iterators)
            {
                it.Reset();
            }
            for (int i = 0; i < a.GetLength(0); i++)
            {
                for (int j = 0; j < a.GetLength(1); j++)
                {
                    a[i, j] = d[i, j];
                }
            }
            for (int i = 0; i < z.Length; i++)
            {
                z[i] = 0;
            }
            while (true)
            {
                consumer.Reset();
                try
                {
                    consumer.UpdateChildrenData();
                }
                catch (Exception)
                {
                    goto cycle;
                }
                for (int i = 0; i < y.Length; i++)
                {
                    object o = left[i].Parameter();
                    if (o == null)
                    {
                        goto cycle;
                    }
                    y[i] = (double)o;
                    o = right[i].Parameter();
                    if (o == null | o is DBNull)
                    {
                        goto cycle;
                    }
                    double res = (double)o - y[i];
                    yr[i] = res;
                    sigma += res * res;
                }
                for (int i = 0; i < aliases.Length; i++)
                {
                    IAlias a = aliases[i];
                    string key = keys[i];
                    double delta = dx[i];
                    setDelta(a, key, delta);
                    consumer.Reset();
                    consumer.UpdateChildrenData();
                    for (int j = 0; j < y.Length; j++)
                    {
                        object obj = left[j].Parameter();
                        if (obj == null)
                        {
                            setDelta(a, key, -delta);
                            goto cycle;
                        }
                        ht[i, j] = ((double)obj - y[j]) / delta;
                    }
                    setDelta(a, key, -delta);
                }
                for (int i = 0; i < y.Length; i++)
                {
                    for (int j = 0; j <= i; j++)
                    {
                        mr[i, j] = (double)r[i, j].Parameter();
                        mr[j, i] = mr[i, j];
                    }
                }
                RealMatrix.Invert(mr, mr1);
                RealMatrix.Multiply(ht, mr1, htr);
                for (int i = 0; i < a.GetLength(0); i++)
                {
                    for (int k = 0; k < htr.GetLength(1); k++)
                    {
                        z[i] += htr[i, k] * yr[k];
                        for (int j = 0; j < a.GetLength(1); j++)
                        {
                            a[i, j] += htr[i, k] * ht[j, k];
                        }
                    }
                }
            cycle:
                foreach (IIterator it in iterators)
                {
                    if (!it.Next())
                    {
                        goto m;
                    }
                }
            }
        m:
            RealMatrix.Invert(a, a1);
            RealMatrix.Multiply(z, a1, z1);
            for (int i = 0; i < z1.Length; i++)
            {
                setDelta(aliases[i], keys[i], z1[i]);
            }
            return sigma;
        }


        void setDelta(IAlias a, string s, double delta)
        {
            double x = (double)a[s];
            a[s] = x + delta;
        }



        static void getIterators(IDataConsumer consumer, List<IIterator> list)
        {
            for (int i = 0; i < consumer.Count; i++)
            {
                IMeasurements m = consumer[i];
                if (m is IIterator)
                {
                    IIterator it = m as IIterator;
                    if (!list.Contains(it))
                    {
                        list.Add(it);
                    }
                }
                if (m is IDataConsumer)
                {
                    IDataConsumer c = m as IDataConsumer;
                    getIterators(c, list);
                }
            }
        }



        void setZeros()
        {
            if (left != null)
            {
                for (int i = 0; i < left.Length; i++)
                {
                    left[i] = zero;
                    right[i] = zero;
                }
            }
            if (r != null)
            {
                for (int i = 0; i < r.GetLength(0); i++)
                {
                    for (int j = 0; j < r.GetLength(1); j++)
                    {
                        r[i, j] = (i == j) ? unity : zero;
                    }
                }
            }
            return;
            for (int i = 0; i < sAliases.Count; i++)
            {
                for (int j = 0; j < sAliases.Count; j++)
                {
                    d[i, j] = 0;
                }
            }
            for (int i = 0; i < sAliases.Count; i++)
            {
                dx[i] = 0.000001;
            }
        }

        protected void prepare()
        {
            getIterators(this, iterators);
            setZeros();
            aliases = new IAlias[sAliases.Count];
            keys = new string[sAliases.Count];
            for (int i = 0; i < sAliases.Count; i++)
            {
                object[] o = DataConsumer.FindAlias(this, sAliases[i]);
                aliases[i] = o[0] as IAlias;
                keys[i] = o[1] + "";
            }
            z = new double[sAliases.Count];
            z1 = new double[sAliases.Count];
            a = new double[sAliases.Count, sAliases.Count];
            a1 = new double[sAliases.Count, sAliases.Count];
            for (int i = 0; i < sLeft.Count; i++)
            {
                IMeasure m = find(sLeft[i]);
                left[i] = m;
            }
            for (int i = 0; i < sRight.Count; i++)
            {
                IMeasure m = find(sRight[i]);
                right[i] = m;
            }
            int n = sR.Count;
            int k = sLeft.Count;
            mr = new double[k, k];
            mr1 = new double[k, k];
            y = new double[k];
            y1 = new double[k];
            yr = new double[k];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j <= i; j++)
                {
                    string sr = sR[i][j];
                    IMeasure mea = find(sr);
                    if (mea != null)
                    {
                        r[i, j] = mea;
                    }
                }
            }
        }

        private IMeasure find(string name)
        {
            int n = name.LastIndexOf(".");
            if (n < 0)
            {
                return null;
            }
            string cn = name.Substring(0, n);
            string suff = name.Substring(n + 1);
            for (int i = 0; i < measurements.Count; i++)
            {
                IMeasurements mea = measurements[i];
                IAssociatedObject ao = mea as IAssociatedObject;
                string na = PureDesktop.GetRelativeName(this, ao);
                if (cn.Equals(na))
                {
                    for (int j = 0; j < mea.Count; j++)
                    {
                        IMeasure m = mea[j];
                        if (suff.Equals(m.Name))
                        {
                            return m;
                        }
                    }
                }
            }
            return null;
        }


        private void createArrays()
        {
            if (dx != null)
            {
                if (dx.Length > 0)
                {
                    a = new double[dx.Length, dx.Length];
                    a1 = new double[dx.Length, dx.Length];
                }
            }
            if (sLeft.Count > 0)
            {
                left = new IMeasure[sLeft.Count];
                right = new IMeasure[sLeft.Count];
                r = new IMeasure[left.Length, left.Length];
                if (sAliases.Count > 0)
                {
                    ht = new double[sAliases.Count, sLeft.Count];
                    htr = new double[sAliases.Count, sLeft.Count];
                }
            }
        }



        #endregion

        #region ConstMeasure

        class ConstMeasure : IMeasure, IDerivation
        {
            const Double t = 0;
            private double c;
            const double z = 0;

            MeasureParameter par;
            MeasureParameter der;

            public ConstMeasure(double c)
            {
                this.c = c;
                par = getP;
                der = getD;
            }

            #region IMeasure Members

            MeasureParameter IMeasure.Parameter
            {
                get { return par; }
            }

            string IMeasure.Name
            {
                get { return "const" + c; }
            }

            object IMeasure.Type
            {
                get { return t; }
            }

            #endregion

            object getP()
            {
                return c;
            }
            object getD()
            {
                return z;
            }

            #region IDerivation Members

            double IDerivation.Derivation
            {
                get { return 0; }
            }

            #endregion
        }

        #endregion
    }
}
