Code/Dependencies/Pixie/Pixie/Markup/DecorationSpan.cs
using System;

namespace WasmBox.Pixie.Markup {
    /// <summary>
    /// Describes an enumeration of text decorations.
    /// </summary>
    [Flags]
    public enum TextDecoration {
        /// <summary>
        /// Text is left undecorated.
        /// </summary>
        None = 0,

        /// <summary>
        /// Text is rendered in bold.
        /// </summary>
        Bold = 1,

        /// <summary>
        /// Text is rendered in italics.
        /// </summary>
        Italic = 2,

        /// <summary>
        /// Text is underlined.
        /// </summary>
        Underline = 4,

        /// <summary>
        /// Text is struck through.
        /// </summary>
        Strikethrough = 8
    }

    /// <summary>
    /// A markup node that toggles text decorations for its contents.
    /// </summary>
    public sealed class DecorationSpan : ContainerNode {
        /// <summary>
        /// Creates a decoration span from the given contents and decoration.
        /// </summary>
        /// <param name="contents">The decoration span's contents.</param>
        /// <param name="decoration">The decoration to apply to the contents.</param>
        public DecorationSpan(
            MarkupNode contents,
            TextDecoration decoration)
            : this(contents, decoration, UnifyDecorations) { }

        /// <summary>
        /// Creates a decoration span from the given contents and decoration.
        /// </summary>
        /// <param name="contents">The decoration span's contents.</param>
        /// <param name="decoration">The decoration to apply to the contents.</param>
        /// <param name="updateDecoration">An operator that updates the decorations.</param>
        public DecorationSpan(
            MarkupNode contents,
            TextDecoration decoration,
            Func<TextDecoration, TextDecoration, TextDecoration> updateDecoration)
            : base(contents) {
            this.Decoration = decoration;
            this.UpdateDecoration = updateDecoration;
        }

        /// <summary>
        /// Gets the decoration to apply. Text decorations are encoded
        /// as a bit field, so this field may specify zero or more
        /// decorations.
        /// </summary>
        /// <returns>The decoration to apply.</returns>
        public TextDecoration Decoration { get; private set; }

        /// <summary>
        /// Gets the binary operator that is used to update the text
        /// decoration of this span's contents. The first argument to
        /// the operator represents the previous text decoration,
        /// the second argument represents the text decoration
        /// supplied by a span and the return value are the final
        /// text decorations for this span's contents.
        /// </summary>
        /// <returns>A binary operator.</returns>
        public Func<TextDecoration, TextDecoration, TextDecoration> UpdateDecoration { get; private set; }

        /// <inheritdoc/>
        public override ContainerNode WithContents(MarkupNode newContents) {
            return new DecorationSpan(newContents, Decoration, UpdateDecoration);
        }

        /// <inheritdoc/>
        public override MarkupNode Fallback => Contents;

        /// <summary>
        /// Renders a node in bold.
        /// </summary>
        /// <param name="node">The node to render in bold.</param>
        /// <returns>A decoration span that renders the node in bold.</returns>
        public static DecorationSpan MakeBold(MarkupNode node) {
            return new DecorationSpan(node, TextDecoration.Bold);
        }

        /// <summary>
        /// Renders a node in italics.
        /// </summary>
        /// <param name="node">The node to render in italics.</param>
        /// <returns>A decoration span that renders the node in italics.</returns>
        public static DecorationSpan MakeItalicized(MarkupNode node) {
            return new DecorationSpan(node, TextDecoration.Italic);
        }

        /// <summary>
        /// Renders a node in strikethrough mode.
        /// </summary>
        /// <param name="node">The node to render in strikethrough mode.</param>
        /// <returns>A decoration span that renders the node in strikethrough mode.</returns>
        public static DecorationSpan MakeStruckthrough(MarkupNode node) {
            return new DecorationSpan(node, TextDecoration.Strikethrough);
        }

        /// <summary>
        /// Renders a node in underline mode.
        /// </summary>
        /// <param name="node">The node to render in underline mode.</param>
        /// <returns>A decoration span that renders the node in underline mode.</returns>
        public static DecorationSpan MakeUnderlined(MarkupNode node) {
            return new DecorationSpan(node, TextDecoration.Underline);
        }

        /// <summary>
        /// Returns the second text decoration.
        /// </summary>
        /// <param name="first">The first text decoration.</param>
        /// <param name="second">The second text decoration.</param>
        /// <returns>The second text decoration.</returns>
        public static TextDecoration ReplaceDecorations(
            TextDecoration first,
            TextDecoration second) {
            return second;
        }

        /// <summary>
        /// Computes the union of two text decorations.
        /// </summary>
        /// <param name="first">The first text decoration.</param>
        /// <param name="second">The second text decoration.</param>
        /// <returns>The union of the text decorations.</returns>
        public static TextDecoration UnifyDecorations(
            TextDecoration first,
            TextDecoration second) {
            return first | second;
        }

        /// <summary>
        /// Computes the intersection of two text decorations.
        /// </summary>
        /// <param name="first">The first text decoration.</param>
        /// <param name="second">The second text decoration.</param>
        /// <returns>The intersection of the text decorations.</returns>
        public static TextDecoration IntersectDecorations(
            TextDecoration first,
            TextDecoration second) {
            return first & second;
        }

        /// <summary>
        /// Computes the exclusive or of two text decorations.
        /// </summary>
        /// <param name="first">The first text decoration.</param>
        /// <param name="second">The second text decoration.</param>
        /// <returns>The exclusive or of the text decorations.</returns>
        public static TextDecoration ToggleDecorations(
            TextDecoration first,
            TextDecoration second) {
            return first ^ second;
        }
    }
}