using System.Linq;
using System.Windows.Media;

using C1.Silverlight.RichTextBox;
using C1.Silverlight.RichTextBox.Documents;
using System.Text.RegularExpressions;
using System.Windows.Documents;

namespace VSDev9
{
    public class C1RichTextBoxHtml
        : C1RichTextBox
    {
        C1RangeStyleCollection _rangeStyles = new C1RangeStyleCollection();

        // initialize regular expression used to parse HTML
        string tagPattern =
            @"</?(?<tagName>[\.a-zA-Z0-9_:\-]+)" +
            @"(\s+(?<attName>[\.a-zA-Z0-9_:\-]+)(?<attValue>(=""[^""]+"")?))*\s*/?>";

        // initialize styles used to color the document
        C1TextElementStyle brDarkBlue = new C1TextElementStyle 
        { 
            { C1TextElement.ForegroundProperty, new SolidColorBrush(Color.FromArgb(255, 0, 0, 180)) }
        };
        C1TextElementStyle brDarkRed = new C1TextElementStyle 
        { 
            { C1TextElement.ForegroundProperty, new SolidColorBrush(Color.FromArgb(255, 180, 0, 0)) }
        };
        C1TextElementStyle brLightRed = new C1TextElementStyle 
        { 
            { C1TextElement.ForegroundProperty, new SolidColorBrush(Colors.Red) }
        };

        public C1RichTextBoxHtml() 
        {
            StyleOverrides.Add(_rangeStyles);
            ReturnMode = ReturnMode.SoftLineBreak;
            TextWrapping = System.Windows.TextWrapping.NoWrap;

            DocumentChanged += new System.EventHandler(C1RichTextBoxHtml_DocumentChanged);
            TextChanged += new System.EventHandler<C1TextChangedEventArgs>(C1RichTextBoxHtml_TextChanged);
        }

        void C1RichTextBoxHtml_DocumentChanged(object sender, System.EventArgs e)
        {
            UpdateSyntaxColoring(Document.ContentRange);
        }

        void C1RichTextBoxHtml_TextChanged(object sender, C1TextChangedEventArgs e)
        {
            var start = e.Range.Start.Enumerate(LogicalDirection.Backward)
                                     .FirstOrDefault(p => p.Symbol.Equals('\n'));
            if (start != null)
            {
                start = start.GetPositionAtOffset(1);
            }
            var end = e.Range.End.Enumerate().FirstOrDefault(p => p.Symbol.Equals('\n'));
            var doc = e.Range.Start.Element.Root;
            UpdateSyntaxColoring(new C1TextRange(start ?? doc.ContentStart, end ?? doc.ContentEnd));
        }

        // perform syntax coloring
        void UpdateSyntaxColoring(C1TextRange range)
        {
            // remove old coloring
            _rangeStyles.RemoveRange(range);

            var input = range.Text;

            // highlight the matches            
            foreach (Match m in Regex.Matches(input, tagPattern))
            {
                // select whole tag, make it dark blue
                _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, m), brDarkBlue));

                // select tag name, make it dark red
                var tagName = m.Groups["tagName"];
                _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, tagName), brDarkRed));

                // select attribute names, make them light red
                var attGroup = m.Groups["attName"];
                if (attGroup != null)
                {
                    var atts = attGroup.Captures;
                    for (int i = 0; i < atts.Count; i++)
                    {
                        var att = atts[i];
                        _rangeStyles.Add(new C1RangeStyle(GetRangeAtTextOffset(range.Start, att), brLightRed));
                    }
                }
            }
        }

        C1TextRange GetRangeAtTextOffset(C1TextPointer pos, Capture capture)
        {
            var start = pos.GetPositionAtOffset(capture.Index, C1TextRange.TextTagFilter);
            var end = start.GetPositionAtOffset(capture.Length, C1TextRange.TextTagFilter);
            return new C1TextRange(start, end);
        }
    }
}
