package org.fxmisc.richtext.util;

import org.fxmisc.richtext.GenericStyledArea;
import org.fxmisc.richtext.model.PlainTextChange;
import org.fxmisc.richtext.model.RichTextChange;
import org.fxmisc.richtext.model.TextChange;
import org.fxmisc.undo.UndoManager;
import org.fxmisc.undo.UndoManagerFactory;

import java.util.function.Consumer;

public final class UndoUtils {

    private UndoUtils() {
        throw new IllegalStateException("UndoUtils cannot be instantiated");
    }

    public static <PS, SEG, S> UndoManager defaultUndoManager(GenericStyledArea<PS, SEG, S> area) {
        return area.isPreserveStyle()
                ? richTextUndoManager(area)
                : plainTextUndoManager(area);
    }

    /* ********************************************************************** *
     *                                                                        *
     * UndoManager Factory Methods                                            *
     *                                                                        *
     * Code that constructs different kinds of UndoManagers for an area       *
     *                                                                        *
     * ********************************************************************** */

    public static <PS, SEG, S> UndoManager<RichTextChange<PS, SEG, S>> richTextUndoManager(GenericStyledArea<PS, SEG, S> area) {
        return richTextUndoManager(area, UndoManagerFactory.unlimitedHistoryFactory());
    }

    public static <PS, SEG, S> UndoManager<RichTextChange<PS, SEG, S>> richTextUndoManager(GenericStyledArea<PS, SEG, S> area,
                                                               UndoManagerFactory factory) {
        return factory.create(area.richChanges(), RichTextChange::invert, applyRichTextChange(area), TextChange::mergeWith, TextChange::isIdentity);
    };

    public static <PS, SEG, S> UndoManager<PlainTextChange> plainTextUndoManager(GenericStyledArea<PS, SEG, S> area) {
        return plainTextUndoManager(area, UndoManagerFactory.unlimitedHistoryFactory());
    }

    public static <PS, SEG, S> UndoManager<PlainTextChange> plainTextUndoManager(GenericStyledArea<PS, SEG, S> area,
                                                                UndoManagerFactory factory) {
        return factory.create(area.plainTextChanges(), PlainTextChange::invert, applyPlainTextChange(area), TextChange::mergeWith, TextChange::isIdentity);
    }

    /* ********************************************************************** *
     *                                                                        *
     * Change Appliers                                                        *
     *                                                                        *
     * Code that handles how a change should be applied to the area           *
     *                                                                        *
     * ********************************************************************** */

    public static <PS, SEG, S> Consumer<PlainTextChange> applyPlainTextChange(GenericStyledArea<PS, SEG, S> area) {
        return change -> area.replaceText(change.getPosition(), change.getPosition() + change.getRemoved().length(), change.getInserted());
    }

    public static <PS, SEG, S> Consumer<RichTextChange<PS, SEG, S>> applyRichTextChange(GenericStyledArea<PS, SEG, S> area) {
        return change -> area.replace(change.getPosition(), change.getPosition() + change.getRemoved().length(), change.getInserted());
    }
}

@@ -335,16 +335,144 @@
 sumer;%0A%0A
+/**%0A * A class filled with factory methods to help easily construct an %7B@link UndoManager%7D for a %7B@link GenericStyledArea%7D.%0A */%0A
 public f
@@ -597,32 +597,326 @@
 iated%22);%0A    %7D%0A%0A
+    /**%0A     * Constructs an UndoManager with an unlimited history:%0A     * if %7B@link GenericStyledArea#isPreserveStyle() the area's preserveStyle flag is true%7D, the returned UndoManager%0A     * can undo/redo %7B@link RichTextChange%7Ds; otherwise, it can undo/redo %7B@link PlainTextChange%7Ds.%0A     */%0A
     public stati
@@ -1688,32 +1688,148 @@
 *********** */%0A%0A
+    /**%0A     * Returns an UndoManager with an unlimited history that can undo/redo %7B@link RichTextChange%7Ds.%0A     */%0A
     public stati
@@ -2028,32 +2028,122 @@
 tory());%0A    %7D%0A%0A
+    /**%0A     * Returns an UndoManager that can undo/redo %7B@link RichTextChange%7Ds.%0A     */%0A
     public stati
@@ -2396,20 +2396,16 @@
 nges(), 
-Rich
 TextChan
@@ -2499,16 +2499,133 @@
     %7D;%0A%0A
+    /**%0A     * Returns an UndoManager with an unlimited history that can undo/redo %7B@link PlainTextChange%7Ds.%0A     */%0A
     publ
@@ -2823,32 +2823,123 @@
 tory());%0A    %7D%0A%0A
+    /**%0A     * Returns an UndoManager that can undo/redo %7B@link PlainTextChange%7Ds.%0A     */%0A
     public stati
@@ -3188,21 +3188,16 @@
 nges(), 
-Plain
 TextChan
@@ -3849,24 +3849,262 @@
 ******* */%0A%0A
+    /**%0A     * Applies a %7B@link PlainTextChange%7D to the given area when the %7B@link UndoManager%7D's change stream emits an event%0A     * by %7B@code area.replaceText(change.getPosition(), change.getRemovalEnd(), change.getInserted()%7D.%0A     */%0A
     public s
@@ -4285,47 +4285,251 @@
 .get
-Position() + ch
+RemovalEnd(), change.getInserted());%0A    %7D%0A%0A    /**%0A     * Applies a %7B@link PlainTextChange%7D to the given area when the %7B@link UndoM
 an
+a
 ge
-.getRemoved().length
+r%7D's change stream emits an event%0A     * by %7B@code area.replace(change.getPosition(), change.getRemovalEnd
 (), 
@@ -4544,33 +4544,34 @@
 etInserted()
-);
+%7D.
 %0A    
-%7D%0A
+ */
 %0A    public 
@@ -4759,47 +4759,18 @@
 .get
-Position() + change.getRemoved().length
+RemovalEnd
 (), 

Add javadoc to UndoUtils