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


using CategoryTheory;
using DiagramUI;

namespace DataPerformer
{
    [Serializable()]
    public class ObjectTransformer : ISerializable, ICategoryObject,
        IDataConsumer, IMeasurements, IPostSetArrow, IObjectTransformerConsumer
    {

        #region Fields

        protected IObjectTransformer transformer;

        protected object[] input;

        protected IMeasure[] outMea;

        protected IMeasure[] inMea;

        protected object[] inO;

        protected object[] outO;


        protected bool isUpdated = false;

        private IDataConsumer cons;



        protected List<IMeasurements> mea = new List<IMeasurements>();

        protected Dictionary<string, string> links = new Dictionary<string, string>();

        protected List<IMeasurements> providers;

        protected object obj;

        #endregion

        #region Constructros

        public ObjectTransformer()
        {
            cons = this;
        }

        public ObjectTransformer(SerializationInfo info, StreamingContext context)
            : this()
        {
            links = info.GetValue("Links", typeof(Dictionary<string, string>))
                as Dictionary<string, string>;
        }

        #endregion

        #region ISerializable Members

        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Links", links, typeof(Dictionary<string, string>));
        }

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

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

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

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

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

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

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

        #endregion

        #region IMeasurements Members

        int IMeasurements.Count
        {
            get { return outMea.Length; }
        }

        IMeasure IMeasurements.this[int n]
        {
            get { return outMea[n]; }
        }

        void IMeasurements.UpdateMeasurements()
        {
            if (isUpdated)
            {
                return;
            }
            cons.UpdateChildrenData();
            for (int i = 0; i < inO.Length; i++)
            {
                inO[i] = inMea[i].Parameter();
            }
            transformer.Calculate(inO, outO);
        }

        bool IMeasurements.IsUpdated
        {
            get
            {
                return isUpdated;
            }
            set
            {
                isUpdated = value;
            }
        }

        #endregion

        #region IObjectTransformerConsumer Members

        void IObjectTransformerConsumer.Add(IObjectTransformer transformer)
        {
            if (this.transformer != null)
            {
                throw new Exception();
            }
            this.transformer = transformer;
            initTransformer();

        }

        void IObjectTransformerConsumer.Remove(IObjectTransformer transformer)
        {
            this.transformer = null;
        }

        #endregion

        #region IPostSetArrow Members

        public void PostSetArrow()
        {
            initTransformer();
            Links = links;
        }

        #endregion

        #region Specific Members

        public IObjectTransformer Transformer
        {
            get
            {
                return transformer;
            }
        }
        /*            set
                    {
                        if ((value != null) & (transformer != null))
                        {
                            PureDesktop.ThrowException(this, new Exception("Transformer already exists"));
                        }
                        transformer = value;
                        if (value == null)
                        {
                            return;
                        }
                        initTransformer();
                    }
                }
        */

        public Dictionary<string, string> Links
        {
            get
            {
                return links;
            }
            set
            {
                if (inO.Length != value.Count)
                {
                    PureDesktop.ThrowException(this, new Exception("Undefined input"));
                }
                setLinks(value);
                links = value;
            }
        }

        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;
            }
        }

        protected void setLinks(Dictionary<string, string> links)
        {
            Dictionary<string, int> d = new Dictionary<string, int>();
            string[] inst = transformer.Input;
            for (int i = 0; i < inst.Length; i++)
            {
                d[inst[i]] = i;
            }
            foreach (string key in links.Keys)
            {
                int n = d[key];
                object type = transformer.GetInputType(n);
                string val = links[key];
                IMeasure m = find(val);
                if (!type.Equals(m.Type))
                {
                    PureDesktop.ThrowException(this, new Exception("Illegal type"));
                }
                inMea[n] = m;
            }

        }

        protected 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 < mea.Count; i++)
            {
                IMeasurements m = mea[i];
                IAssociatedObject ao = m as IAssociatedObject;
                string na = PureDesktop.GetRelativeName(this, ao);
                if (cn.Equals(na))
                {
                    for (int j = 0; j < m.Count; j++)
                    {
                        IMeasure ms = m[j];
                        if (suff.Equals(ms.Name))
                        {
                            return ms;
                        }
                    }
                }
            }
            return null;
        }


        protected void initTransformer()
        {
            string[] outS = transformer.Output;
            outO = new object[outS.Length];
            outMea = new IMeasure[outO.Length];
            inMea = new IMeasure[transformer.Input.Length];
            inO = new object[inMea.Length];
            for (int i = 0; i < outS.Length; i++)
            {
                string name = outS[i];
                object type = transformer.GetOutputType(i);
                outMea[i] = new TransMeasure(i, outO, name, type);
            }
        }





        #endregion


        class TransMeasure : IMeasure
        {
            int n;
            object[] outO;
            string name;
            object type;

            MeasureParameter par;

            internal TransMeasure(int n, object[] outO, string name, object type)
            {
                this.n = n;
                this.outO = outO;
                this.name = name;
                this.type = type;
                par = get;
            }

            private object get()
            {
                return outO[n];
            }


            #region IMeasure Members

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

            string IMeasure.Name
            {
                get { return name; }
            }

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

            #endregion
        }


    }
}
