using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Text.RegularExpressions;
using C1.Silverlight;
using C1.Silverlight.RichTextBox;
using C1.Silverlight.RichTextBox.Documents;

namespace SyntaxHighlight
{
    public partial class Page : UserControl
    {
        C1RichTextBox _rtb;
        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 Page()
        {
            InitializeComponent();

            _rtb = new C1RichTextBox
            {
                ReturnMode = ReturnMode.SoftLineBreak,
                TextWrapping = TextWrapping.NoWrap,
                Document = new C1Document
                {
                    FontFamily = new FontFamily("Courier New"),
                    FontSize = 16,
                    Blocks = 
                    {
                        new C1Paragraph 
                        {
                            Children =
                            {
                                new C1Run 
                                { 
                                    Text = GetStringResource("w3c.htm")
                                },
                            },
                        }
                    }
                },
                StyleOverrides = { _rangeStyles }
            };
            LayoutRoot.Children.Add(_rtb);

            _rtb.TextChanged += tb_TextChanged;
            UpdateSyntaxColoring(_rtb.Document.ContentRange);
        }

        void tb_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);
        }

        // utility
        static string GetStringResource(string resourceName)
        {
            string text = string.Empty;
            var asm = System.Reflection.Assembly.GetExecutingAssembly();
            var appName = asm.FullName.Split(',')[0];
            var uri = new Uri(string.Format("{0};component/{1}", appName, resourceName), UriKind.Relative);
            var sri = Application.GetResourceStream(uri);
            using (var sr = new StreamReader(sri.Stream))
            {
                return sr.ReadToEnd();
            }
        }
    }
}
