using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Data;

using CategoryTheory;
using MathGraph;


namespace DiagramUI
{
    /// <summary>
    /// Base class of desktop
    /// </summary>
    public class PureDesktop : IDesktop
    {
        #region Fields

        protected ArrayList objects = new ArrayList();
        protected ArrayList arrows = new ArrayList();
        protected ArrayList components = new ArrayList();
        protected Hashtable table = new Hashtable();
        static private Hashtable resources;


        #endregion

        #region Ctor
        public PureDesktop()
        {
        }

        #endregion

        #region IDesktop Members

        public ArrayList Components
        {
            get
            {
                return components;
            }
        }

        /// <summary>
        /// All components
        /// </summary>
        public virtual ICollection AllComponents
        {
            get
            {
                return components;
            }
        }



        public ArrayList Objects
        {
            get
            {
                return objects;
            }
        }

        public ArrayList Arrows
        {
            get
            {
                return arrows;
            }
        }

        public virtual void Copy(ArrayList objects, ArrayList arrows, bool associated)
        {
            PureObjectLabel.Wrappers = true;
            foreach (IObjectLabel l in objects)
            {
                IObjectLabel lab = new PureObjectLabel(l.Name, l.Kind, l.Type, l.X, l.Y);
                lab.Object = l.Object;
                lab.Desktop = this;
                this.objects.Add(lab);
                components.Add(lab);
                table[l.Name] = lab;
            }
            PureObjectLabel.Wrappers = false;
            foreach (IArrowLabel l in arrows)
            {
                IArrowLabel lab = new PureArrowLabel(l.Name, l.Kind, l.Type, l.X, l.Y);
                lab.Arrow = l.Arrow;
                lab.Desktop = this;
                this.arrows.Add(lab);
                components.Add(lab);
                table[l.Name] = lab;
                int s = objects.IndexOf(l.Source);
                int t = objects.IndexOf(l.Target);
                IObjectLabel source = this.objects[s] as IObjectLabel;
                IObjectLabel target = this.objects[t] as IObjectLabel;
                lab.Source = source;
                lab.Target = target;
            }
            if (!associated)
            {
                PureObjectLabel.Wrappers = false;
                return;
            }
            PureObjectLabel.Wrappers = true;
            PureObjectLabel.SetLabels(objects);
            PureArrowLabel.SetLabels(arrows);
            PureObjectLabel.Wrappers = false;
        }

        /// <summary>
        /// Access to component
        /// </summary>
        /// <param name="name">Component name</param>
        /// <returns>The component</returns>
        public virtual INamedComponent this[string name]
        {
            get
            {
                if (!table.ContainsKey(name))
                {
                    return null;
                }
                return table[name] as INamedComponent;
            }
        }


        /// <summary>
        /// Access to object
        /// </summary>
        /// <param name="name">Object name</param>
        /// <returns>The object</returns>
        public object GetObject(string name)
        {
            return GetAssociatedObject(this, name);
        }


        /// <summary>
        /// Root desktop
        /// </summary>
        public virtual IDesktop Root
        {
            get
            {
                return this;
            }
        }



        #endregion

        #region Specific members


        /// <summary>
        /// Gets texts of elements
        /// </summary>
        /// <param name="element">Parent none</param>
        /// <param name="tag">Tag name</param>
        /// <returns>List of texts</returns>
        public static List<string> GetTexts(XmlElement element, string tag)
        {
            List<string> l = new List<string>();
            XmlNodeList list = element.GetElementsByTagName(tag);
            foreach (XmlNode node in list)
            {
                l.Add(node.InnerText);
            }
            return l;
        }


        /// <summary>
        /// Sets texts to childen elements
        /// </summary>
        /// <param name="doc">Parent document</param>
        /// <param name="element">Parent element</param>
        /// <param name="tag">Tag name</param>
        /// <param name="list">List</param>
        public static void SetTexts(XmlDocument doc, XmlElement element, string tag, List<string> list)
        {
            foreach (string s in list)
            {
                XmlElement e = doc.CreateElement(tag);
                e.InnerText = s;
                element.AppendChild(e);
            }
        }


        /// <summary>
        /// Creates table from Xml element
        /// </summary>
        /// <param name="element">The element</param>
        /// <param name="tag">Tag</param>
        /// <param name="attributes">Attributes</param>
        /// <returns>The table</returns>
        public static DataTable CreateTable(XmlElement element, string tag, string[] attributes)
        {
            DataTable table = new DataTable(tag);
            foreach (string attr in attributes)
            {
                table.Columns.Add(attr, "".GetType());
            }
            XmlNodeList list = element.GetElementsByTagName(tag);
            foreach (XmlElement e in list)
            {
                object[] o = new object[attributes.Length];
                for (int i = 0; i < attributes.Length; i++)
                {
                    o[i] = e.Attributes[attributes[i]].Value;
                }
                table.Rows.Add(o);
            }
            return table;
        }


        public static DataTable CreateTable(XmlElement element, string tag, string[] attributes, Type[] types)
        {
            DataTable table = new DataTable(tag);
            for (int i = 0; i < attributes.Length; i++)
            {
                table.Columns.Add(attributes[i], types[i]);
            }
            XmlNodeList list = element.GetElementsByTagName(tag);
            foreach (XmlElement e in list)
            {
                object[] o = new object[attributes.Length];
                for (int i = 0; i < attributes.Length; i++)
                {
                    string s = e.Attributes[attributes[i]].Value;
                    o[i] = Create(s, types[i]);
                }
                table.Rows.Add(o);
            }
            return table;
        }


        protected static object Create(string s, Type type)
        {
            if (type.Equals(typeof(string)))
            {
                return s;
            }
            if (type.Equals(typeof(int)))
            {
                return Int32.Parse(s);
            }
            return null;
        }


        /// <summary>
        /// Create nodes from Xml element
        /// </summary>
        /// <param name="doc">Parent document</param>
        /// <param name="element">Element</param>
        /// <param name="tag">Tag</param>
        /// <param name="table">Tablr</param>
        public static void CreateNodes(XmlDocument doc, XmlElement element, string tag, DataTable table)
        {
            foreach (DataRow row in table.Rows)
            {
                XmlElement e = doc.CreateElement(tag);
                element.AppendChild(e);
                foreach (DataColumn c in table.Columns)
                {
                    string n = c.ColumnName;
                    XmlAttribute attr = doc.CreateAttribute(n);
                    attr.Value = row[n] + "";
                    e.Attributes.Append(attr);
                }
            }
        }

        /// <summary>
        /// Gets object name
        /// </summary>
        /// <param name="ao">Object</param>
        /// <returns>The name</returns>
        public static string GetObjectName(IAssociatedObject ao)
        {
            object o = ao.Object;
            if (!(o is INamedComponent))
            {
                throw new Exception();
            }
            INamedComponent nc = o as INamedComponent;
            return nc.Name + "";
        }


        /// <summary>
        /// Gets relative name
        /// </summary>
        /// <param name="source">Source</param>
        /// <param name="target">Target</param>
        /// <returns>The relative name</returns>
        static public string GetRelativeName(IAssociatedObject source, IAssociatedObject target)
        {
            INamedComponent cs = source.Object as INamedComponent;
            INamedComponent ct = target.Object as INamedComponent;
            return ct.GetName(ct.Desktop);
        }

        /// <summary>
        /// Sets value of alias variable
        /// </summary>
        /// <param name="desktop">Desktop</param>
        /// <param name="alias">The alias name</param>
        /// <param name="val">The alias value</param>
        static public void SetAliasValue(IDesktop desktop, string alias, object val)
        {
            int n = alias.LastIndexOf('.');
            string cName = alias.Substring(0, n);
            INamedComponent c = desktop[cName];
            string alName = alias.Substring(n + 1);
            IAlias al = null;
            if (c is IObjectLabel)
            {
                IObjectLabel ol = c as IObjectLabel;
                al = ol.Object as IAlias;
            }
            if (c is IArrowLabel)
            {
                IArrowLabel ar = c as IArrowLabel;
                al = ar.Arrow as IAlias;
            }
            al[alName] = val;
        }


        /// <summary>
        /// Gets alias value
        /// </summary>
        /// <param name="desktop">Desktop</param>
        /// <param name="alias">The alias name</param>
        /// <returns>The alias value</returns>
        static public object GetAliasValue(IDesktop desktop, string alias)
        {
            int n = alias.IndexOf('.');
            string cName = alias.Substring(0, n);
            INamedComponent c = desktop[cName];
            string alName = alias.Substring(n + 1);
            IAlias al = null;
            if (c is IObjectLabel)
            {
                IObjectLabel ol = c as IObjectLabel;
                al = ol.Object as IAlias;
            }
            if (c is IArrowLabel)
            {
                IArrowLabel ar = c as IArrowLabel;
                al = ar.Arrow as IAlias;
            }
            return al[alName];
        }

        static public IArrowLabel GetArrowLabel(IDesktop desktop, string name)
        {
            object o = desktop[name];
            if (o == null)
            {
                return null;
            }
            if (!(o is IArrowLabel))
            {
                return null;
            }
            return o as IArrowLabel;
        }

        /// <summary>
        /// Gets associated object of desktop component
        /// </summary>
        /// <param name="desktop">Desktop</param>
        /// <param name="name">Component name</param>
        /// <returns>Associated object</returns>
        public static object GetAssociatedObject(IDesktop desktop, string name)
        {
            INamedComponent comp = desktop[name];
            if (comp is IObjectLabel)
            {
                IObjectLabel lab = comp as IObjectLabel;
                return lab.Object;
            }
            if (comp is IArrowLabel)
            {
                IArrowLabel lab = comp as IArrowLabel;
                return lab.Arrow;
            }
            return null;
        }

        /// <summary>
        /// Throws exception linked with object
        /// </summary>
        /// <param name="o">The linked object</param>
        /// <param name="e">The parent exception</param>
        public static void ThrowException(object o, Exception element)
        {
            if (element is DiagramException)
            {
                throw element;
            }
            if (o == null)
            {
                throw element;
            }
            if (o is INamedComponent)
            {
                INamedComponent n = o as INamedComponent;
                throw new DiagramException(element, n);
            }
            if (o is IAssociatedObject)
            {
                IAssociatedObject ass = o as IAssociatedObject;
                object ob = ass.Object;
                if (ob is INamedComponent)
                {
                    INamedComponent nc = ob as INamedComponent;
                    throw new DiagramException(element, nc);
                }
            }
            throw element;
        }

        static public Hashtable Resources
        {
            set
            {
                foreach (object o in value.Keys)
                {
                    if (!(o is string))
                    {
                        throw new Exception();
                    }
                    if (!(value[o] is String))
                    {
                        throw new Exception();
                    }
                }
                resources = value;
            }
        }

        /// <summary>
        /// Creates digraph from objects and arrows
        /// </summary>
        /// <param name="objects">Objects</param>
        /// <param name="arrows">Arrows</param>
        /// <returns>The digraph</returns>
        static public Digraph CreateDigraph(ArrayList objects, ArrayList arrows)
        {
            Digraph graph = new Digraph();
            foreach (IArrowLabel label in arrows)
            {
                label.SourceNumber = objects.IndexOf(label.Source);
                label.TargetNumber = objects.IndexOf(label.Target);
            }
            ArrayList vertices = new ArrayList();
            foreach (IObjectLabel label in objects)
            {
                DigraphVertex v = new DigraphVertex(graph);
                vertices.Add(v);
                v.Object = label;
            }
            foreach (IArrowLabel label in arrows)
            {
                DigraphEdge edge = new DigraphEdge();
                edge.Object = label;
                if (label.SourceNumber is Int32 && label.TargetNumber is Int32)
                {
                    int sourceNum = (int)label.SourceNumber;
                    DigraphVertex vs = vertices[sourceNum] as DigraphVertex;
                    edge.Source = vs;
                    int targetNum = (int)label.TargetNumber;
                    DigraphVertex vt = vertices[targetNum] as DigraphVertex;
                    edge.Target = vt;
                }
            }
            return graph;
        }


        /// <summary>
        /// Composition of path arrows
        /// </summary>
        /// <param name="category">Category of arrows</param>
        /// <param name="path">The path</param>
        /// <returns>The composition</returns>
        static public ICategoryArrow Composition(ICategory category, DigraphPath path)
        {
            IArrowLabel label = path[0].Object as IArrowLabel;
            ICategoryArrow arrow = label.Arrow;
            for (int i = 1; i < path.Count; i++)
            {
                label = path[i].Object as IArrowLabel;
                arrow = label.Arrow.Compose(category, arrow);
            }
            return arrow;
        }




        /// <summary>
        /// Gets resources string
        /// </summary>
        /// <param name="str">String name</param>
        /// <returns>The resources string</returns>
        static public string GetResourceString(string str)
        {
            if (resources != null)
            {
                if (resources.ContainsKey(str))
                {
                    return resources[str] as string;
                }
            }
            return str;
        }

        /// <summary>
        /// Disposes desktop
        /// </summary>
        /// <param name="desktop">Desktop to dispose</param>
        public static void Dispose(IDesktop desktop)
        {
            ICollection c = desktop.AllComponents;
            foreach (object o in c)
            {
                disposeAssociatedObject(o);
            }
        }

        /// <summary>
        /// Disposes associated object
        /// </summary>
        /// <param name="obj">Object to dispose</param>
        static private void disposeAssociatedObject(object obj)
        {
            if (obj is IAssociatedObject)
            {
                IAssociatedObject ao = obj as IAssociatedObject;
                object ob = ao.Object;
                if (ob is IDisposable)
                {
                    IDisposable d = ob as IDisposable;
                    d.Dispose();
                }
            }
        }

        #endregion

    }
}
