using System;
using System.Text;
using System.IO;
using System.Xml;
using System.Collections;
using System.Text.RegularExpressions;
#if AdvTree
namespace DevComponents.Tree.TextMarkup
#elif DOTNETBAR
namespace DevComponents.DotNetBar.TextMarkup
#elif SUPERGRID
namespace DevComponents.SuperGrid.TextMarkup
#elif LAYOUT
namespace DevComponents.DotNetBar.Layout.TextMarkup
#endif
{
    internal class MarkupParser
    {
        #region Private Variables
        private static string TextElementName = "text";
        private static string BodyTag = "body";
        #endregion
        #region Internal Implementation
        public static BodyElement Parse(string text)
        {
            StringBuilder plainText = new StringBuilder(text.Length);
            BodyElement root = new BodyElement();
            root.HasExpandElement = false;
            MarkupElement currentParent = root;
            Stack openTags = new Stack();
            openTags.Push(root);
            // Input text is not wrapped into the container tag so we wrap it here
            text = text.Replace(" ", "{ent_nbsp}");
            text = text.Replace("&zwsp;", "{ent_zwsp}");
            text = text.Replace("<", "{ent_lt}");
            text = text.Replace(">", "{ent_gt}");
            text = text.Replace("&", "{ent_amp}");
            text = text.Replace("|", "{ent_l}");
            text = text.Replace("&", "|");
            text = text.Replace("{ent_nbsp}", " ");
            text = text.Replace("{ent_zwsp}", "&zwsp;"); 
            text = text.Replace("{ent_lt}", "<");
            text = text.Replace("{ent_gt}", ">");
            StringReader sr = new StringReader("<"+BodyTag+">" + text + ""+BodyTag+">");
#if !DEBUG
            try
#endif
            {
                XmlTextReader reader = new XmlTextReader(sr);
                //reader.EntityHandling = EntityHandling.ExpandCharEntities;
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        if (reader.Name == BodyTag)
                            continue;
                        MarkupElement el = CreateMarkupElement(reader.Name);
                        if (el == null)
                        {
                            reader.Skip();
                            continue;
                        }
                        else if (el is ExpandElement)
                            root.HasExpandElement = true;
                        if (el is IActiveMarkupElement)
                            root.ActiveElements.Add(el);
                        // Parse any attributes here
                        if (reader.AttributeCount > 0)
                        {
                            el.ReadAttributes(reader);
                            reader.MoveToElement();
                        }
                        currentParent.Elements.Add(el);
                        if (el is ContainerElement)
                            currentParent = el;
                        if (!reader.IsEmptyElement)
                            openTags.Push(el);
                    }
                    else if (reader.NodeType == XmlNodeType.Text)
                    {
                        if (reader.Value.Length == 1)
                        {
                            TextElement el = CreateMarkupElement(TextElementName) as TextElement;
                            if (reader.Value == " ")
                            {
                                el.TrailingSpace = true;
                                plainText.Append(' ');
                            }
                            else
                            {
                                el.Text = reader.Value;
                                el.Text = el.Text.Replace("|", "&");
                                el.Text = el.Text.Replace("{ent_l}", "|");
                                el.Text = el.Text.Replace("{ent_amp}", "&&");
                                plainText.Append(el.Text+" ");
                            }
                            currentParent.Elements.Add(el);
                        }
                        else
                        {
                            string s = reader.Value;
                            if (s.StartsWith("\r\n"))
                                s = s.TrimStart(new char[] { '\r', '\n' });
                            s = s.Replace("\r\n", " ");
                            string[] words = s.Split(' ');
                            bool space = false;
                            if (currentParent.Elements.Count > 0 && currentParent.Elements[currentParent.Elements.Count - 1] is NewLine)
                                space = true;
                            for (int i = 0; i < words.Length; i++)
                            {
                                if (words[i].Length == 0)
                                {
                                    if (space)
                                        continue;
                                    space = true;
                                }
                                else
                                    space = false;
                                TextElement el = CreateMarkupElement(TextElementName) as TextElement;
                                el.Text = words[i].Replace("|","&");
                                el.Text = el.Text.Replace("{ent_l}", "|");
                                el.Text = el.Text.Replace("{ent_amp}", "&&");
                                plainText.Append(el.Text + " ");
                                if (i < words.Length - 1)
                                {
                                    el.TrailingSpace = true;
                                    space = true;
                                }
                                currentParent.Elements.Add(el);
                            }
                        }
                    }
                    else if (reader.NodeType == XmlNodeType.Whitespace)
                    {
                        if (reader.Value.IndexOf(' ') >= 0)
                        {
                            TextElement el = CreateMarkupElement(TextElementName) as TextElement;
                            el.TrailingSpace = true;
                            currentParent.Elements.Add(el);
                        }
                    }
                    else if (reader.NodeType == XmlNodeType.EntityReference)
                    {
                        TextElement el = CreateMarkupElement(TextElementName) as TextElement;
                        if (reader.Name == "nbsp")
                        {
                            el.TrailingSpace = true;
                        }
                        else if (reader.Name == "zwsp")
                        {
                            el.TrailingSpace = false;
                        }
                        else
                            el.Text = reader.Name;
                        el.EnablePrefixHandling = false;
                        currentParent.Elements.Add(el);
                    }
                    else if (reader.NodeType == XmlNodeType.EndElement)
                    {
                        MarkupElement el = openTags.Pop() as MarkupElement;
                        if (el != currentParent)
                        {
                            currentParent.Elements.Add(new EndMarkupElement(el));
                        }
                        else
                        {
                            if (currentParent != root)
                                currentParent = currentParent.Parent;
                        }
                    }
                }
            }
#if !DEBUG
            catch
            {
                return null;
            }
#endif
            root.PlainText = plainText.ToString();
            return root;
        }
        public static MarkupElement CreateMarkupElement(string elementName)
        {
            if (elementName == "b" || elementName == "strong")
                return new Strong();
            else if (elementName == "i" || elementName == "em")
                return new Italic();
            else if (elementName == "u")
                return new Underline();
            else if (elementName == "br")
                return new NewLine();
            else if (elementName == "expand")
                return new ExpandElement();
            else if (elementName == "a")
                return new HyperLink();
            else if (elementName == "p")
                return new Paragraph();
            else if (elementName == "div")
                return new Div();
            else if (elementName == "span")
                return new Span();
            else if (elementName == "img")
                return new ImageElement();
            else if (elementName == "h1")
                return new Heading();
            else if (elementName == "h2")
                return new Heading(2);
            else if (elementName == "h3")
                return new Heading(3);
            else if (elementName == "h4")
                return new Heading(4);
            else if (elementName == "h5")
                return new Heading(5);
            else if (elementName == "h6")
                return new Heading(6);
            else if (elementName == "font")
                return new FontElement();
            else if (elementName == "s" || elementName == "strike")
                return new Strike();
            else if (elementName == TextElementName)
                return new TextElement();
            else if (elementName == "symbol")
                return new SymbolElement();
            return null;
        }
        /// 
        /// Tests whether input text could be markup text.
        /// 
        /// Text to test.
        /// true if text could be markup, otherwise false
        public static bool IsMarkup(ref string text)
        {
            if (text == null || text.IndexOf("") < 0 && text.IndexOf("/>") < 0)
                return false;
            return true;
        }
        internal static string RemoveExpand(string text)
        {
            return Regex.Replace(text, "", "", RegexOptions.IgnoreCase);
        }
        #endregion
    }
}