using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using CommonLang;
using CommonUI.Cell.Game;
using CommonUI.Data;
using CommonLang.Log;
using CommonLang.Xml;

namespace CommonUI.Display.Text
{
    /// <summary>
    /// 文字字符上的属性
    /// </summary>
    public class TextAttribute : ICloneable
    {
        /// <summary>
        ///  字符颜色 0 表示无特性 (RGBA)
        /// </summary>
        public uint fontColor = 0xffffffff;
        /// <summary>
        /// 子尺寸 0 表示无特性
        /// </summary>
        public float fontSize;
        /// <summary>
        /// 字体名字,空表示无特性
        /// </summary>
        public string fontName;
        /// <summary>
        /// 字体
        /// </summary>
        public FontStyle fontStyle = FontStyle.STYLE_PLAIN;
        public bool underline
        {
            get
            {
                switch (fontStyle)
                {
                    case FontStyle.STYLE_BOLD_ITALIC_UNDERLINED:
                    case FontStyle.STYLE_ITALIC_UNDERLINED:
                    case FontStyle.STYLE_BOLD_UNDERLINED:
                    case FontStyle.STYLE_UNDERLINED:
                        return true;
                }
                return false;
            }
            set
            {
                if (!value)
                {
                    switch (fontStyle)
                    {
                        case FontStyle.STYLE_BOLD_ITALIC_UNDERLINED:
                            fontStyle = FontStyle.STYLE_BOLD_ITALIC;
                            break;
                        case FontStyle.STYLE_ITALIC_UNDERLINED:
                            fontStyle = FontStyle.STYLE_ITALIC;
                            break;
                        case FontStyle.STYLE_BOLD_UNDERLINED:
                            fontStyle = FontStyle.STYLE_BOLD;
                            break;
                        case FontStyle.STYLE_UNDERLINED:
                            fontStyle = FontStyle.STYLE_PLAIN;
                            break;
                    }
                }
                else
                {
                    switch (fontStyle)
                    {
                        case FontStyle.STYLE_BOLD_ITALIC:
                            fontStyle = FontStyle.STYLE_BOLD_ITALIC_UNDERLINED;
                            break;
                        case FontStyle.STYLE_ITALIC:
                            fontStyle = FontStyle.STYLE_ITALIC_UNDERLINED;
                            break;
                        case FontStyle.STYLE_BOLD:
                            fontStyle = FontStyle.STYLE_BOLD_UNDERLINED;
                            break;
                        case FontStyle.STYLE_PLAIN:
                            fontStyle = FontStyle.STYLE_UNDERLINED;
                            break;
                    }
                }
            }
        }
        /// <summary>
        /// 描边
        /// </summary>
        public TextBorderCount borderCount = TextBorderCount.Null;
        /// <summary>
        /// 描边颜色 (RGBA)
        /// </summary>
        public uint borderColor;
        /// <summary>
        /// 此字符替换成图片,空表示无特性
        /// </summary>
        public string resImage;
        /// <summary>
        /// 图片渲染方式
        /// </summary>
        public ImageZoom resImageZoom;
        /// <summary>
        /// 此字符替换成动画,空表示无特性
        /// </summary>
        public string resSprite;
        /// <summary>
        /// 标记此处可以被点击触发事件,空表示无特性
        /// </summary>
        public string link;
        /// <summary>
        /// 行对齐方式
        /// </summary>
        public RichTextAlignment anchor = RichTextAlignment.taNA;
        /// <summary>
        /// 扩展的自定义渲染部分
        /// </summary>
        public TextDrawable drawable;

        public TextAttribute(
            uint fColor = 0,
            float fSize = 0,
            string fName = null,
            FontStyle fStyle = FontStyle.STYLE_PLAIN,
            RichTextAlignment ta = RichTextAlignment.taNA,
            TextBorderCount bCount = TextBorderCount.Null,
            uint bColor = 0,
            string rImage = null,
            string rSprite = null,
            string pLink = null,
            ImageZoom rImageZoom = null,
            TextDrawable drawable = null)
        {
            this.fontColor = fColor;
            this.fontSize = fSize;
            this.fontName = fName;
            this.fontStyle = fStyle;
            this.anchor = ta;

            this.resImage = rImage;
            this.resImageZoom = rImageZoom;
            this.resSprite = rSprite;

            this.borderCount = bCount;
            this.borderColor = bColor;

            this.link = pLink;

            this.drawable = drawable;
        }
        public TextAttribute(TextAttribute other)
        {
            this.fontColor = other.fontColor;
            this.fontSize = other.fontSize;
            this.fontName = other.fontName;
            this.fontStyle = other.fontStyle;
            this.anchor = other.anchor;

            this.borderCount = other.borderCount;
            this.borderColor = other.borderColor;

            this.resImage = other.resImage;
            this.resImageZoom = CUtils.TryClone(other.resImageZoom);
            this.resSprite = other.resSprite;

            this.link = other.link;
            this.drawable = other.drawable;
        }


        public object Clone()
        {
            return new TextAttribute(this);
        }

        public override bool Equals(object obj)
        {
            if (obj is TextAttribute)
            {
                return this.Equals(obj as TextAttribute);
            }
            return false;
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public bool Equals(TextAttribute other)
        {
            if (other == this)
                return true;
            if (other == null)
                return false;

            if (this.fontColor != other.fontColor)
                return false;
            if (this.fontSize != other.fontSize)
                return false;
            if (!string.Equals(this.fontName, other.fontName))
                return false;
            if (this.fontStyle != other.fontStyle)
                return false;
            if (this.anchor != other.anchor)
                return false;

            if (this.borderCount != other.borderCount)
                return false;
            if (this.borderColor != other.borderColor)
                return false;

            if (!string.Equals(this.resImage, other.resImage))
                return false;
            if (!string.Equals(this.resSprite, other.resSprite))
                return false;

            if (!ImageZoom.Equals(this.resImageZoom, other.resImageZoom))
                return false;

            if (!string.Equals(this.link, other.link))
                return false;

            if (drawable != null && !drawable.Equals(other.drawable))
                return false;

            if (drawable == null && other.drawable != null)
            {
                return false;
            }
            return true;
        }

        public bool IsValid()
        {
            if (fontColor != 0)
                return true;
            if (fontSize != 0)
                return true;
            if (!string.IsNullOrEmpty(fontName))
                return true;
            if (anchor != RichTextAlignment.taNA)
                return true;

            if (borderCount > 0)
                return true;
            if (borderColor != 0)
                return true;

            if (!string.IsNullOrEmpty(resImage))
                return true;
            if (!string.IsNullOrEmpty(resSprite))
                return true;

            if (resImageZoom != null)
                return true;

            if (!string.IsNullOrEmpty(link))
                return true;

            if (drawable != null)
                return true;

            return false;
        }

        public void Combine(TextAttribute other, bool isCover = true)
        {
            if (other.fontColor != 0)
            {
                this.fontColor = other.fontColor;
            }
            if (other.fontSize != 0)
            {
                this.fontSize = other.fontSize;
            }
            if (!string.IsNullOrEmpty(other.fontName))
            {
                this.fontName = other.fontName;
            }
            if (other.fontStyle != 0)
            {
                this.fontStyle = other.fontStyle;
            }
            if (other.anchor != RichTextAlignment.taNA)
            {
                this.anchor = other.anchor;
            }

            if (other.borderCount > 0)
            {
                this.borderCount = other.borderCount;
            }
            if (other.borderColor != 0)
            {
                this.borderColor = other.borderColor;
            }

            if (!string.IsNullOrEmpty(other.resImage))
            {
                this.resImage = other.resImage;
            }
            if (!string.IsNullOrEmpty(other.resSprite))
            {
                this.resSprite = other.resSprite;
            }

            if (other.resImageZoom != null)
            {
                this.resImageZoom = CUtils.TryClone(other.resImageZoom);
            }

            if (other.drawable != null)
            {
                this.drawable = CUtils.TryClone(other.drawable);
            }

            if (isCover && !string.IsNullOrEmpty(other.link))
            {
                this.link = other.link;
            }
            else if (!isCover)
            {
                this.link += other.link;
            }
        }

        public static TextAttribute Combine(TextAttribute src, TextAttribute dst)
        {
            TextAttribute ret = new TextAttribute(src);
            ret.Combine(dst);
            return ret;
        }

    }

    //------------------------------------------------------------------------------------------------------------------------


    //------------------------------------------------------------------------------------------------------------------------


    public class ImageZoomParser : CommonLang.Parser.ParserAdapter
    {
        public ImageZoomParser() : base(typeof(ImageZoom)) { }
        public override object StringToObject(string text)
        {
            return ImageZoom.FromString(text);
        }
        public override string ObjectToString(object obj)
        {
            return ImageZoom.ToString(obj as ImageZoom);
        }
    }
    public class ImageZoom : ICloneable
    {
        static ImageZoom()
        {
            Parser.RegistParser(new ImageZoomParser());
        }

        public enum ImageFill
        {
            Clamp,
            Repeat,
        }
        public ImageFill Filling = ImageFill.Clamp;
        public float Width;
        public float Height;
        public object Clone()
        {
            ImageZoom ret = new ImageZoom();
            ret.Filling = this.Filling;
            ret.Width = this.Width;
            ret.Height = this.Height;
            return ret;
        }
        public static bool Equals(ImageZoom a, ImageZoom b)
        {
            if (a != null && b != null)
            {
                if (a.Filling != b.Filling)
                    return false;
                if (a.Width != b.Width)
                    return false;
                if (a.Height != b.Height)
                    return false;
                return true;
            }
            return a == b;
        }
        public override string ToString()
        {
            return ToString(this);
        }

        public static ImageZoom FromString(string text)
        {
            string[] kvs = text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (kvs.Length >= 2)
            {
                ImageZoom ret = new ImageZoom();
                ret.Width = float.Parse(kvs[0]);
                ret.Height = float.Parse(kvs[1]);
                if (kvs.Length >= 3)
                {
                    ret.Filling = (ImageFill)Enum.Parse(typeof(ImageFill), kvs[2], true);
                }
                return ret;
            }
            return null;
        }
        public static string ToString(ImageZoom obj)
        {
            if (obj != null)
            {
                return string.Format("{0},{1},{2}", obj.Width, obj.Height, obj.Filling);
            }
            return null;
        }
    }


    //------------------------------------------------------------------------------------------------------------------------
    /////////////////////////////////////////////////////////////////////////////////////////////
    // 
    /////////////////////////////////////////////////////////////////////////////////////////////

    /// <summary>
    /// 含有属性的文字
    /// </summary>
    public class AttributedString : ICloneable
    {
        private string text = "";

        private List<TextAttribute> attributes = new List<TextAttribute>();

        public AttributedString()
        {
        }
        public AttributedString(string other, TextAttribute ta)
        {
            Append(other, ta);
        }

        public object Clone()
        {
            AttributedString ret = new AttributedString();
            ret.text = this.text;
            ret.attributes = CUtils.CloneList<TextAttribute>(this.attributes);
            return ret;
        }

        public override bool Equals(object obj)
        {
            if (obj is AttributedString)
            {
                AttributedString other = obj as AttributedString;
                if (other.text.Equals(this.text))
                {
                    for (int i = text.Length - 1; i >= 0; --i)
                    {
                        if (!other.attributes[i].Equals(this.attributes[i]))
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }
            return false;
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public bool SetAttribute(int index, int len, TextAttribute ta)
        {
            if (index + len <= attributes.Count)
            {
                int end = index + len;
                for (int i = index; i < end; ++i)
                {
                    attributes[i] = ta;
                }
                return true;
            }
            return false;
        }

        public AttributedString AddAttribute(TextAttribute attribute, bool isCover = true)
        {
            return AddAttribute(attribute, 0, text.Length, isCover);
        }

        public AttributedString AddAttribute(TextAttribute attribute, int beginIndex, int count, bool isCover = true)
        {
            int endIndex = beginIndex + count;
            for (int i = beginIndex; i < endIndex; ++i)
            {
                attributes[i].Combine(attribute, isCover);
            }
            return this;
        }

        public AttributedString Append(AttributedString other)
        {
            if (other != null && !string.IsNullOrEmpty(other.text))
            {
                text += other.text;
                for (int i = 0; i < other.text.Length; ++i)
                {
                    attributes.Add(other.attributes[i]);
                }
            }
            return this;
        }

        public AttributedString Append(string other)
        {
            if (attributes.Count > 0)
            {
                TextAttribute lastAttr = attributes[attributes.Count - 1];
                Append(other, lastAttr);
            }
            else
            {
                Append(other, new TextAttribute(Color.COLOR_WHITE, 12));
            }
            return this;
        }

        public AttributedString Append(string other, TextAttribute ta)
        {
            if (!string.IsNullOrEmpty(other))
            {
                text += other;
                for (int i = 0; i < other.Length; ++i)
                {
                    attributes.Add(ta);
                }
            }
            return this;
        }

        public AttributedString DeleteString(int beginIndex, int count)
        {
            text = text.Remove(beginIndex, count);
            attributes.RemoveRange(beginIndex, count);
            return this;
        }

        public AttributedString ClearString()
        {
            text = "";
            attributes.Clear();
            return this;
        }

        public int Length
        {
            get
            {
                return text.Length;
            }
        }

        override public string ToString()
        {
            return text;
        }

        public char GetChar(int index)
        {
            if (index < text.Length)
            {
                return text[index];
            }
            return default(char);
        }

        public TextAttribute GetAttribute(int index)
        {
            if (index < attributes.Count)
            {
                return attributes[index];
            }
            return null;
        }

    }
    //------------------------------------------------------------------------------------------------------------------------

    //------------------------------------------------------------------------------------------------------------------------
    public class AttributedStringDecoder
    {
        protected Logger log = LoggerFactory.GetLogger("AttributedStringDecoder");

        public const string TEXT_XML_KEY_COLOR = "color";
        public const string TEXT_XML_KEY_SIZE = "size";
        public const string TEXT_XML_KEY_FACE = "face";
        public const string TEXT_XML_KEY_STYLE = "style";

        public const string TEXT_XML_KEY_B_COUNT = "border";
        public const string TEXT_XML_KEY_B_COLOR = "bcolor";

        public const string TEXT_XML_KEY_RES_IMG = "img";
        /// <summary>
        /// @"宽,高"
        /// img_zoom = "width,height"
        /// </summary>
        public const string TEXT_XML_KEY_RES_IMAGE_ZOOM = "img_zoom";
        /// <summary>
        /// @"资源名,精灵名,动画ID"
        /// spr = "res/sprite.xml,sprite_name,anim"
        /// </summary>
        public const string TEXT_XML_KEY_RES_SPR = "spr";


        public const string TEXT_XML_KEY_LINK = "link";
        public const string TEXT_XML_KEY_LINE_ANCHOR = "anchor";

        public const string TEXT_XML_NODE_BREAK = "br";
        public const string TEXT_XML_NODE_SPACE = "p";

        public virtual AttributedString CreateFromXML(XmlDocument xml, TextAttribute defaultTA = null)
        {
            AttributedString attr = new AttributedString();
            TextAttribute curAttr = (defaultTA != null) ? defaultTA : new TextAttribute(Color.COLOR_WHITE, 16);
            try
            {
                internalBuildXML(attr, xml.DocumentElement, curAttr);
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
            return attr;
        }
        public virtual AttributedString CreateFromXML(string text, TextAttribute defaultTA = null)
        {
            XmlDocument xml = XmlUtil.FromString(text);
            return CreateFromXML(xml, defaultTA);
        }

        protected virtual void DecodeAttribute(XmlElement node, XmlAttribute x_attr, TextAttribute attr)
        {
            switch (x_attr.Name)
            {
                case TEXT_XML_KEY_COLOR:
                    {
                        string kv = node.GetAttribute(TEXT_XML_KEY_COLOR);
                        uint argb = uint.Parse(kv, System.Globalization.NumberStyles.HexNumber);
                        attr.fontColor = Color.toRGBA(argb);
                    }
                    break;
                case TEXT_XML_KEY_SIZE:
                    {
                        attr.fontSize = int.Parse(node.GetAttribute(TEXT_XML_KEY_SIZE));
                    }
                    break;
                case TEXT_XML_KEY_STYLE:
                    {
                        string style = node.GetAttribute(TEXT_XML_KEY_STYLE);
                        int value;
                        if (int.TryParse(style, out value))
                        {
                            attr.fontStyle = (FontStyle)value;
                        }
                        else
                        {
                            attr.fontStyle = (FontStyle) Enum.Parse(typeof(FontStyle), style);
                        }
                    }
                    break;
                case TEXT_XML_KEY_FACE:
                    {
                        attr.fontName = node.GetAttribute(TEXT_XML_KEY_FACE);
                    }
                    break;
                case TEXT_XML_KEY_B_COUNT:
                    {
                        attr.borderCount = (Data.TextBorderCount)int.Parse(node.GetAttribute(TEXT_XML_KEY_B_COUNT));
                    }
                    break;
                case TEXT_XML_KEY_B_COLOR:
                    {
                        string kv = node.GetAttribute(TEXT_XML_KEY_B_COLOR);
                        uint argb = uint.Parse(kv, System.Globalization.NumberStyles.HexNumber);
                        attr.borderColor = Color.toRGBA(argb);
                    }
                    break;
                case TEXT_XML_KEY_RES_IMG:
                    {
                        attr.resImage = node.GetAttribute(TEXT_XML_KEY_RES_IMG);
                    }
                    break;
                case TEXT_XML_KEY_RES_SPR:
                    {
                        attr.resSprite = node.GetAttribute(TEXT_XML_KEY_RES_SPR);
                    }
                    break;
                case TEXT_XML_KEY_RES_IMAGE_ZOOM:
                    {
                        attr.resImageZoom = ImageZoom.FromString(node.GetAttribute(TEXT_XML_KEY_RES_IMAGE_ZOOM));
                    }
                    break;
                case TEXT_XML_KEY_LINK:
                    {
                        attr.link = node.GetAttribute(TEXT_XML_KEY_LINK);
                    }
                    break;
                case TEXT_XML_KEY_LINE_ANCHOR:
                    {
                        string value = node.GetAttribute(TEXT_XML_KEY_LINE_ANCHOR);
                        try
                        {
                            attr.anchor = (RichTextAlignment)Enum.Parse(typeof(RichTextAlignment), value, true);
                        }
                        catch (Exception err)
                        {
                            log.Error(err.Message, err);
                        }
                    }
                    break;
            }
            TextDrawable drawable = ITextDrawableFactory.Instance.CreateTextDrawable(x_attr.Name, x_attr.Value);
            if (drawable != null)
            {
                attr.drawable = drawable;
            }
        }

        private void internalBuildXML(AttributedString atext, XmlElement node, TextAttribute parentAttr)
        {
            string nname = node.Name;

            TextAttribute attr = new TextAttribute();
            foreach (XmlAttribute x_attr in node.Attributes)
            {
                DecodeAttribute(node, x_attr, attr);
            }
            attr = TextAttribute.Combine(parentAttr, attr);
            if (!attr.IsValid())
            {
                attr = parentAttr;
            }
            if (node.ChildNodes.Count > 0)
            {
                foreach (XmlNode e in node.ChildNodes)
                {
                    if (e is XmlText || e is XmlCDataSection)
                    {
                        atext.Append(e.Value, attr);
                    }
                    else if (e is XmlElement)
                    {
                        internalBuildXML(atext, e as XmlElement, attr);
                    }
                }
            }
            if (string.Equals(nname, TEXT_XML_NODE_BREAK))
            {
                atext.Append("\n", attr);
            }
            else if (string.Equals(nname, TEXT_XML_NODE_SPACE))
            {
                atext.Append(" ", attr);
            }
        }

    }

}