using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using C1.Silverlight.RichTextBox;
using C1.Silverlight.RichTextBox.Documents;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media.Effects;

namespace Annotations
{
    /// <summary>
    /// Provides annotation support in a C1RichTextBox.
    /// </summary>
    public class AnnotationService
    {
        // Text style applied to highlights.
        C1TextElementStyle _highlightStyle = new C1TextElementStyle
        {
            { C1TextElement.BackgroundProperty, new SolidColorBrush(Color.FromArgb(255, 255, 255, 204)) }
        };

        // Text style applied to the text range of a note.
        C1TextElementStyle _noteRangeStyle = new C1TextElementStyle
        {
            { C1TextElement.BackgroundProperty, new SolidColorBrush(Color.FromArgb(255, 230, 255, 230)) },
            { C1TextElement.BorderBrushProperty, new SolidColorBrush(Color.FromArgb(255, 0, 128, 0)) },
            { C1TextElement.BorderThicknessProperty, new Thickness(1.5, 0, 1.5, 0) },
            { C1TextElement.CornerRadiusProperty, new CornerRadius(3) },
        };

        C1RichTextBox _richTextBox;
        C1RangeStyleCollection _highlights = new C1RangeStyleCollection();
        C1RangeStyleCollection _notes = new C1RangeStyleCollection();
        List<StickyNote> _stickies = new List<StickyNote>();

        /// <summary>
        /// Creates a new AnnotationService attached to a C1RichTextBox.
        /// </summary>
        /// <param name="richTextBox">The C1RichTextBox to attach to.</param>
        public AnnotationService(C1RichTextBox richTextBox)
        {
            _richTextBox = richTextBox;

            // Add style overrides to set the text style of highlight and notes text ranges.
            _richTextBox.StyleOverrides.Add(_highlights);
            _richTextBox.StyleOverrides.Add(_notes);

            // Add a painter to display sticky notes.
            _richTextBox.Painters.Add(new StickyPainter(_stickies));

            _richTextBox.TextChanged += OnTextChanged;
        }

        /// <summary>
        /// Return all highlights.
        /// </summary>
        public IEnumerable<C1TextRange> Highlights
        {
            get { return _highlights.Select(rs => rs.Range); }
        }

        /// <summary>
        /// Return all notes.
        /// </summary>
        public IEnumerable<StickyNote> Notes
        {
            get { return _stickies; }
        }

        /// <summary>
        /// Adds a new highlight.
        /// </summary>
        /// <param name="range"></param>
        public void AddHighlight(C1TextRange range)
        {
            _highlights.Add(new C1RangeStyle(range, _highlightStyle));
        }

        /// <summary>
        /// Removes highlights from a range.
        /// </summary>
        /// <param name="range"></param>
        public void RemoveHighlights(C1TextRange range)
        {
            foreach (var oldRange in _highlights.ToList())
            {
                // When a highlight intersects the removed range, remove it but leave the parts
                // that weren't inside the removed range.
                if (oldRange.Range.Intersects(range))
                {
                    _highlights.Remove(oldRange);
                    if (oldRange.Range.Start < range.Start)
                    {
                        _highlights.Add(new C1RangeStyle(
                            new C1TextRange(oldRange.Range.Start, range.Start), 
                            oldRange.Style));
                    }
                    if (oldRange.Range.End > range.End)
                    {
                        _highlights.Add(new C1RangeStyle(
                            new C1TextRange(range.End, oldRange.Range.End),
                            oldRange.Style));
                    }
                }
            }
        }

        /// <summary>
        /// Add a new text note.
        /// </summary>
        /// <param name="range"></param>
        public StickyNote AddTextNote(C1TextRange range)
        {
            var rangeStyle = new C1RangeStyle(range, _noteRangeStyle);
            _notes.Add(rangeStyle);
            var note = new StickyNote { Range = range };
            _stickies.Add(note);
            return note;
        }

        /// <summary>
        /// Remove all notes that touch a given range.
        /// </summary>
        /// <param name="range"></param>
        public void RemoveNotes(C1TextRange range)
        {
            foreach (var sticky in _stickies.Where(s => s.Range.Intersects(range)).ToList())
            {
                _stickies.Remove(sticky);
            }
            _notes.RemoveRange(range);
        }

        void OnTextChanged(object sender, C1TextChangedEventArgs e)
        {
            // On text changes remove all highlights and notes whose ranges are empty.
            // That happens when the text inside the range was deleted.

            foreach (var sticky in _stickies.Where(s => s.Range.IsEmpty).ToList())
            {
                _stickies.Remove(sticky);
            }
            foreach (var note in _notes.Where(rs => rs.Range.IsEmpty).ToList())
            {
                _notes.Remove(note);
            }
            foreach (var highlight in _highlights.Where(rs => rs.Range.IsEmpty).ToList())
            {
                _highlights.Remove(highlight);
            }
        }

    }
}
