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

namespace IMLibrary.Controls.Toolbox
{
    partial class Toolbox
    {
        internal const string Serializer_XmlConfig = "ToolboxConfig";
        internal const string Serializer_XmlHeader = "Toolbox";
        internal const string Serializer_Category = "Category";
        internal const string Serializer_Item = "Item";
        internal const string Serializer_ToolboxItems = "ToolboxItems";
        internal const string Serializer_Type = "ItemType";

        internal const string Serializer_Text = "text";
        internal const string Serializer_Opened = "opened";
        internal const string Serializer_Visible = "visible";
        internal const string Serializer_Enabled = "enabled";
        
        internal const string Serializer_General = "general";
        internal const string Serializer_AllowNested = "allowNested";
        internal const string Serializer_AllowToolboxItems = "allowToolboxItems";
        internal const string Serializer_AutoScroll = "autoScroll";
        internal const string Serializer_DrowTabLevel = "drawLevel";
        internal const string Serializer_ItemHeight = "itemHeight";
        internal const string Serializer_ShowAll = "showAll";
        internal const string Serializer_ShowIcons = "showIcons";
        internal const string Serializer_ShowPointer = "showPointer";
        internal const string Serializer_TabHeight = "tabHeight";

        private const string Serializer_Deletable = "deletable";
        private const string Serializer_Tip = "tip";

        /// <summary>
        /// Saves the state of the <see cref="Toolbox"/> to the file.
        /// </summary>
        /// <param name="filename">The path to the file.</param>
        public virtual void Save(string filename)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement toolbox = doc.CreateElement(Serializer_XmlConfig);
            doc.AppendChild(toolbox);
            SaveXml(toolbox);
            doc.Save(filename);
        }

        /// <summary>
        /// Saves the state of the <see cref="Toolbox"/> to the <see cref="XmlElement"/> object.
        /// </summary>
        /// <param name="xml">An <see cref="XmlElement"/> to serialize to.</param>
        public virtual void SaveXml(XmlElement xml)
        {
            //if (xml == null)
            //    throw new ArgumentNullException("xml");

            //XmlElement toolbox = xml.OwnerDocument.CreateElement(Serializer_XmlHeader);
            //xml.AppendChild(toolbox);

            //toolbox.SetAttribute(Serializer_General, CreateGeneralCategory.ToString());
            //toolbox.SetAttribute(Serializer_ItemHeight, this.ItemHeight.ToString());
            //toolbox.SetAttribute(Serializer_TabHeight, this.TabHeight.ToString());
            //toolbox.SetAttribute(Serializer_AutoScroll, this.AutoScroll.ToString());
            //toolbox.SetAttribute(Serializer_AllowToolboxItems, this.AllowToolboxItems.ToString());
            //toolbox.SetAttribute(Serializer_AllowNested, this.AllowNestedTabs.ToString());
            //toolbox.SetAttribute(Serializer_DrowTabLevel, this.DrawTabLevel.ToString());
            //toolbox.SetAttribute(Serializer_ShowAll, this.ShowAll.ToString());
            //toolbox.SetAttribute(Serializer_ShowIcons, this.ShowIcons.ToString());
            //toolbox.SetAttribute(Serializer_ShowPointer, this.ShowPointer.ToString());

            //foreach (Tab tab in Categories)
            //{   
            //    SerializeCategory(toolbox, tab);
            //}

            //if (this.Items.Count > 0)
            //{
            //    XmlElement toolboxItems = xml.OwnerDocument.CreateElement(Serializer_ToolboxItems);
            //    toolbox.AppendChild(toolboxItems);

            //    foreach (Item item in Items)
            //    {
            //        SerializeItem(toolboxItems, item);
            //    }
            //}
        }

        /// <summary>
        /// Loads the state of the <see cref="Toolbox"/> from the file.
        /// </summary>
        /// <param name="filename">The path to the file.</param>
        public virtual void Load(string filename)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(filename);
            XmlElement toolbox = (XmlElement)doc.SelectSingleNode(Serializer_XmlConfig);
            LoadXml(toolbox);
        }

        /// <summary>
        /// Loads the state of the <see cref="Toolbox"/> from the <see cref="XmlElement"/> object.
        /// </summary>
        /// <param name="xml">An <see cref="XmlElement"/> to deserialize from.</param>
        public virtual void LoadXml(XmlElement xml)
        {
            //if (xml == null)
            //    throw new ArgumentNullException("xml");

            //GeneralCategory = null;
            //Categories.Clear();
            //Items.Clear();

            //XmlElement toolbox = (XmlElement)xml.SelectSingleNode(Serializer_XmlHeader);
            
            //bool createGeneral;
            //if (!GetAttribute(toolbox, Serializer_General, out createGeneral))
            //    createGeneral = true;
            //this.CreateGeneralCategory = createGeneral;
            //if (CreateGeneralCategory)
            //{
            //    GeneralCategory = CreateNewTab(Properties.Resources.ToolboxTabGeneral);
            //    GeneralCategory.AllowDelete = false;
            //}

            //int intValue;
            //bool boolValue;
            //if (GetAttribute(toolbox, Serializer_ItemHeight, out intValue))
            //    this.ItemHeight = intValue;
            //if (GetAttribute(toolbox, Serializer_TabHeight, out intValue))
            //    this.TabHeight = intValue;
            //if (GetAttribute(toolbox, Serializer_AutoScroll, out boolValue))
            //    this.AutoScroll = boolValue;
            //if (GetAttribute(toolbox, Serializer_AllowToolboxItems, out boolValue))
            //    this.AllowToolboxItems = boolValue;
            //if (GetAttribute(toolbox, Serializer_AllowNested, out boolValue))
            //    this.AllowNestedTabs = boolValue;
            //if (GetAttribute(toolbox, Serializer_DrowTabLevel, out boolValue))
            //    this.DrawTabLevel = boolValue;
            //if (GetAttribute(toolbox, Serializer_ShowAll, out boolValue))
            //    this.ShowAll = boolValue;
            //if (GetAttribute(toolbox, Serializer_ShowIcons, out boolValue))
            //    this.ShowIcons = boolValue;
            //if (GetAttribute(toolbox, Serializer_ShowPointer, out boolValue))
            //    this.ShowPointer = boolValue;

            //XmlNodeList categories = toolbox.SelectNodes(Serializer_Category);
            //foreach (XmlElement category in categories)
            //{
            //    DeserializeCategory(category, this);
            //}

            //XmlElement toolboxItems = (XmlElement)toolbox.SelectSingleNode(Serializer_ToolboxItems);
            //if (toolboxItems != null)
            //{
            //    XmlNodeList items = toolboxItems.SelectNodes(Serializer_Item);
            //    foreach (XmlElement itemNode in items)
            //    {
            //        DeserializeItem(itemNode, this);
            //    }
            //}
        }

        #region Static methods
        /// <summary>
        /// Gets a boolean attribute from the xml <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The <see cref="XmlElement"/> containing an <paramref name="attribute"/>.</param>
        /// <param name="attribute">The name of the attribute.</param>
        /// <param name="result">An output value.</param>
        /// <returns><b>true</b> if attribute exists and its value is of boolean type; otherwise <b>false</b>.</returns>
        protected static bool GetAttribute(XmlElement element, string attribute, out bool result)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            result = false;

            string resultString = element.GetAttribute(attribute);
            if (!string.IsNullOrEmpty(resultString))
            {
                try
                {
                    result = bool.Parse(resultString);
                    return true;
                }
                catch (FormatException)
                {
                }
            }
            return false;
        }

        /// <summary>
        /// Gets an interger attribute from the xml <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The <see cref="XmlElement"/> containing an <paramref name="attribute"/>.</param>
        /// <param name="attribute">The name of the attribute.</param>
        /// <param name="result">An output value.</param>
        /// <returns><b>true</b> if attribute exists and its value is of interger type; otherwise <b>false</b>.</returns>
        protected static bool GetAttribute(XmlElement element, string attribute, out int result)
        {
            if (element == null)
                throw new ArgumentNullException("element");
            
            result = 0;

            string resultString = element.GetAttribute(attribute);
            if (!string.IsNullOrEmpty(resultString))
            {
                try
                {
                    result = int.Parse(resultString);
                    return true;
                }
                catch (FormatException)
                {
                }
            }
            return false;
        }

        /// <summary>
        /// Gets a string attribute from the xml <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The <see cref="XmlElement"/> containing an <paramref name="attribute"/>.</param>
        /// <param name="attribute">The name of the attribute.</param>
        /// <param name="result">An output value.</param>
        /// <returns><b>true</b> if attribute exists; otherwise <b>false</b>.</returns>
        protected static bool GetAttribute(XmlElement element, string attribute, out string result)
        {
            result = element.GetAttribute(attribute);
            return !string.IsNullOrEmpty(result);
        }
        #endregion

        /// <summary>
        /// Serializes a category <paramref name="tab"/> to the <see cref="XmlElement"/>.
        /// </summary>
        /// <param name="parent">A <see cref="XmlElement"/> representing the <see cref="Toolbox"/> or the parent <see cref="Tab"/>.</param>
        /// <param name="tab">A <see cref="Tab"/> to serialize.</param>
        protected virtual void SerializeCategory(XmlElement parent, Tab tab)
        {
            XmlElement category = parent.OwnerDocument.CreateElement(Serializer_Category);
            parent.AppendChild(category);

            category.SetAttribute(Serializer_Text, tab.Text);
            SerializeCategoryProperties(category, tab);
            
            if (this.AllowNestedTabs)
            {
                foreach (Tab child in tab.Categories)
                {
                    SerializeCategory(category, child);
                }
            }

            foreach (Item item in tab.Items)
            {
                SerializeItem(category, item);
            }
        }

        /// <summary>
        /// Serializes a <paramref name="tab"/> properties into the <paramref name="category"/> element.
        /// </summary>
        /// <param name="category">A <see cref="XmlElement"/> representing the serialized <see cref="Tab"/> object.</param>
        /// <param name="tab">A <see cref="Tab"/> to serialize.</param>
        protected virtual void SerializeCategoryProperties(XmlElement category, Tab tab)
        {
            category.SetAttribute(Serializer_Opened, tab.Opened.ToString());
            if (!tab.Visible)
                category.SetAttribute(Serializer_Visible, tab.Visible.ToString());
            if (!tab.AllowDelete)
                category.SetAttribute(Serializer_Deletable, tab.AllowDelete.ToString());
            if (tab == GeneralCategory)
                category.SetAttribute(Serializer_General, bool.TrueString);
        }

        /// <summary>
        /// Serializes an <paramref name="item"/> to the <see cref="XmlElement"/>.
        /// </summary>
        /// <param name="category">A <see cref="XmlElement"/> representing the category.</param>
        /// <param name="item">An <see cref="Item"/> to serialize.</param>
        [Obsolete("Use Toolbox.Item.SerializeToXml instead of using this method", false)]
        protected virtual void SerializeItem(XmlElement category, Item item)
        {
            XmlElement tabItem = item.SerializeToXml(category.OwnerDocument);
            Type itemType = item.GetType();
            if (itemType != Toolbox._itemType)
            {
                tabItem.SetAttribute(Serializer_Type, string.Format("{0}, {1}", itemType.FullName, itemType.Assembly.GetName().Name));
            }
            category.AppendChild(tabItem);
        }

        /// <summary>
        /// Deserializes a <paramref name="category"/> from the xml and adds it to the <see cref="Toolbox"/>.
        /// </summary>
        /// <param name="category">A <see cref="XmlElement"/> object that represents a <see cref="Tab"/>.</param>
        [Obsolete("This method is no longer invoked. Please, use 'DeserializeCategory(XmlElement, ITab)' instead", true)]
        protected virtual void DeserializeCategory(XmlElement category)
        {
        }

        /// <summary>
        /// Deserializes a <paramref name="category"/> from the xml and adds it to the <paramref name="parent"/> collection.
        /// </summary>
        /// <param name="category">A <see cref="XmlElement"/> object that represents a <see cref="Tab"/>.</param>
        /// <param name="parent">A parent <see cref="ITab"/> that owns the deserialized <see cref="Tab"/>.</param>
        protected virtual void DeserializeCategory(XmlElement category, ITab parent)
        {
            string stringValue;
            bool boolValue;
            
            if (GetAttribute(category, Serializer_Text, out stringValue))
            {
                Tab tab = null;

                if (GetAttribute(category, Serializer_General, out boolValue) && boolValue)
                    tab = GeneralCategory;
                else
                {
                    tab = CreateNewTab(stringValue); 
                }

                if (tab != null)
                {
                    DeserializeCategoryProperties(category, tab);

                    parent.Categories.Add(tab);

                    if (this.AllowNestedTabs)
                    {
                        XmlNodeList children = category.SelectNodes(Serializer_Category);
                        foreach (XmlElement tabNode in children)
                        {
                            DeserializeCategory(tabNode, tab);
                        }
                    }

                    XmlNodeList items = category.SelectNodes(Serializer_Item);
                    foreach (XmlElement itemNode in items)
                    {
                        DeserializeItem(itemNode, tab);
                    }
                }
            }
        }

        /// <summary>
        /// Deserializes a <paramref name="category"/> XML element to <paramref name="tab"/> value.
        /// </summary>
        /// <param name="category">A <see cref="XmlElement"/> representation of serialized <see cref="Tab"/> object.</param>
        /// <param name="tab">A <see cref="Tab"/> to deserialize.</param>
        protected virtual void DeserializeCategoryProperties(XmlElement category, Tab tab)
        {
            bool boolValue;
            if (GetAttribute(category, Serializer_Deletable, out boolValue))
                tab.AllowDelete = boolValue;
            if (GetAttribute(category, Serializer_Visible, out boolValue))
                tab.Visible = boolValue;
            if (GetAttribute(category, Serializer_Opened, out boolValue))
                tab.Opened = boolValue;
        }

        /// <summary>
        /// Deserializes an <paramref name="xmlItem"/> from the xml and adds it to the category <paramref name="tab"/>.
        /// </summary>
        /// <param name="xmlItem">A <see cref="XmlElement"/> object that represents an <see cref="Item"/>.</param>
        /// <param name="tab">A category <see cref="Tab"/> or the <see cref="Toolbox"/> to which an item has to be added.</param>
        [Obsolete("Use Toolbox.Item.DeserializeXml instead of using this method", false)]
        protected virtual void DeserializeItem(XmlElement xmlItem, ITab tab)
        {
            Item item = null;
            XmlAttribute attribute = xmlItem.Attributes[Serializer_Type];
            if (attribute != null)
            {
                Type itemType = Type.GetType(attribute.Value);
                if (itemType != null)
                {
                    try
                    {
                        item = (Item)Activator.CreateInstance(itemType);
                    }
                    catch { }
                }
            }
            if (item == null)
                item = new Item();
            item.DeserializeXml(xmlItem);
            tab.Items.Add(item);
        }
    }
}
