using System;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;

using GLib;
using Gtk;
using Tomboy;

public class WikiMarkupPlugin : NotePlugin 
{
    private readonly Stack markupMarks = new Stack();
    private readonly Hashtable markupTags = new Hashtable();
    private Regex markupExpression;
    private char[] markupChars;

    protected override void Initialize()
    {
        if (null == markupExpression) {
            markupTags["_"] = Note.TagTable.Lookup("underline");
            markupTags["#"] = Note.TagTable.Lookup("highlight");
            markupTags["*"] = Note.TagTable.Lookup("bold");
            markupTags["/"] = Note.TagTable.Lookup("italic");
            markupTags["|"] = Note.TagTable.Lookup("monospace");
            markupTags["'"] = Note.TagTable.Lookup("size:small");
            markupTags["=="] = Note.TagTable.Lookup("size:large");
            markupTags["==="] = Note.TagTable.Lookup("size:huge");

            StringBuilder expression = new StringBuilder();
            Hashtable charTable = new Hashtable();

            foreach(string marker in markupTags.Keys) {
                if (expression.Length > 0)
                    expression.Append("|");

                expression.Append(Regex.Escape(marker));
                charTable[marker[0]] = true;
            }

            ArrayList chars = new ArrayList(charTable.Keys);
            markupChars = (char[])chars.ToArray(typeof(char));
            markupExpression = new Regex(@"(?:^|\s)(" + expression + @")(.+?)\1");
        }
    }

    protected override void Shutdown()
    {
    }

    protected override void OnNoteOpened()
    {
        Buffer.InsertText += buffer_InsertText;
    }

    private void buffer_InsertText (object sender, InsertTextArgs args)
    {
        if (args.Text.IndexOfAny(markupChars) >= 0) {
            TextIter start = args.Pos;
            start.BackwardChars(start.LineOffset);

            TextIter end = start;
            end.ForwardChars(end.CharsInLine);

            ApplyMarkup(start, end);
        }
    }

    private void ApplyMarkup(TextIter start, TextIter end)
    {
        string slice = start.GetSlice(end);

        foreach(Match match in markupExpression.Matches(slice)) {
            Group marker = match.Groups[1];
            Group content = match.Groups[2];
            NoteTag tag = null;

            if (content.Value[0] != marker.Value[0])
                tag = (NoteTag)markupTags[marker.Value];

            if (null != tag) {
                TextIter first = start;
                first.ForwardChars(marker.Index);

                TextIter last = first;
                last.ForwardChars(marker.Length);

                markupMarks.Push(Buffer.CreateMark(null, first, true));
                markupMarks.Push(Buffer.CreateMark(null, last, false));

                first.ForwardChars(marker.Length);
                last.ForwardChars(content.Length);
                Buffer.ApplyTag(tag, first, last);

                first.ForwardChars(content.Length);
                last.ForwardChars(marker.Length);

                markupMarks.Push(Buffer.CreateMark(null, first, true));
                markupMarks.Push(Buffer.CreateMark(null, last, false));
            }
        }

        if (markupMarks.Count > 0)
            Idle.Add(RemoveMarkerTags);
    }

    private bool RemoveMarkerTags()
    {
        while(markupMarks.Count > 0) {
            TextIter last = Buffer.GetIterAtMark((TextMark)markupMarks.Pop());
            TextIter first = Buffer.GetIterAtMark((TextMark)markupMarks.Pop());
            Buffer.Delete(ref first, ref last);
        }

        return false;
    }
}

// vim: sw=4 sta et
