package org.pageseeder.diffx.core;

import org.junit.Assert;
import org.pageseeder.diffx.DiffXException;
import org.pageseeder.diffx.action.Action;
import org.pageseeder.diffx.action.Actions;
import org.pageseeder.diffx.action.Operator;
import org.pageseeder.diffx.config.TextGranularity;
import org.pageseeder.diffx.event.DiffXEvent;
import org.pageseeder.diffx.format.SmartXMLFormatter;
import org.pageseeder.diffx.format.XMLDiffXFormatter;
import org.pageseeder.diffx.handler.ActionHandler;
import org.pageseeder.diffx.test.Events;
import org.pageseeder.diffx.test.TestHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;

public abstract class BaseProcessorTest {

  /**
   * @return The processor instance to use for texting.
   */
  public abstract DiffProcessor getDiffProcessor();

  public final void assertDiffIsApplicable(List<? extends DiffXEvent> seq1, List<? extends DiffXEvent> seq2, List<Action> actions) throws IOException {
    // Ensure that the diff is applicable
    Assert.assertTrue("The resulting diff is not applicable", Actions.isApplicable(seq1, seq2, actions));
  }

  public final void assertDiffIsCorrect(List<? extends DiffXEvent> seq1, List<? extends DiffXEvent> seq2, List<Action> actions) throws IOException {
    // Apply to second sequence to ensure we get the first
    String got1 = Events.toXML(Actions.generate(actions, true));
    String exp1 = Events.toXML(seq1);
    Assert.assertEquals("Applying diff to #2 did not produce #1 ", exp1, got1);

    // Apply to first sequence to ensure we get the second
    String got2 = Events.toXML(Actions.generate(actions, false));
    String exp2 = Events.toXML(seq2);
    Assert.assertEquals("Applying diff to #1 did not produce #2 ", exp2, got2);
  }

  public List<Action> diffToActions(List<? extends DiffXEvent> seq1, List<? extends DiffXEvent> seq2) throws IOException {
    DiffProcessor processor = getDiffProcessor();
    ActionHandler handler = new ActionHandler();
    processor.process(seq1, seq2, handler);
    return handler.getActions();
  }

  public static String toXML(List<Action> actions) throws IOException {
    StringWriter xml = new StringWriter();
    XMLDiffXFormatter formatter = new SmartXMLFormatter(xml);
    Actions.format(actions, formatter);
    return xml.toString();
  }

  public static String toTestOutput(List<Action> actions) throws IOException {
    TestHandler handler = new TestHandler();
    Actions.format(actions, handler);
    return handler.getOutput();
  }


  public static void assertDiffIsWellFormedXML(List<Action> actions) throws IOException {
    assertIsWellFormedXML(toXML(actions));
  }

  public static void assertMatchTestOutput(List<Action> actions, String[] exp) throws IOException {
    // check the possible values
    String output = toTestOutput(actions);
    for (String s : exp) {
      if (s.equals(output)) return;
    }
    Assert.assertEquals(exp[0], output);
  }


  public static void assertIsWellFormedXML(String xml) throws IOException {
    try {
      InputSource source = new InputSource(new StringReader(xml));
      SAXParserFactory factory = SAXParserFactory.newInstance();
      factory.newSAXParser().parse(source, new DefaultHandler());
    } catch (SAXException | ParserConfigurationException ex) {
      throw new AssertionError("XML is not well-formed");
    }
  }

  /**
   * Print the error details.
   */
  protected void printXMLErrorDetails(String xml1, String xml2, String[] exp, String got, List<Action> actions) {
    System.err.println("+------------------------------------------------");
    System.err.println("| Input A: \"" + xml1 + "\"");
    System.err.println("| Input B: \"" + xml2 + "\"");
    System.err.println("| Output:  \"" + got + "\"");
    System.err.print("| Expect:  ");
    for (String s : exp) System.err.print("\"" + s + "\" ");
    System.err.println();
    System.err.print("| Actions: ");
    for (Action action : actions) {
      System.err.print(action.type() == Operator.DEL ? '-' : action.type() == Operator.INS ? '+' : '=');
      System.err.print(action.events());
    }
    System.err.println();
  }

}

@@ -316,26 +316,254 @@
 x.event.
-DiffXEvent
+CloseElementEvent;%0Aimport org.pageseeder.diffx.event.DiffXEvent;%0Aimport org.pageseeder.diffx.event.OpenElementEvent;%0Aimport org.pageseeder.diffx.event.impl.CloseElementEventImpl;%0Aimport org.pageseeder.diffx.event.impl.OpenElementEventImpl
 ;%0Aimport
@@ -1091,24 +1091,82 @@
 ringWriter;%0A
+import java.util.ArrayList;%0Aimport java.util.Collections;%0A
 import java.
@@ -3030,33 +3030,32 @@
 tOutput();%0A  %7D%0A%0A
-%0A
   public static 
@@ -3124,24 +3124,415 @@
 Exception %7B%0A
+    List%3CAction%3E wrapped = new ArrayList%3C%3E();%0A    // We wrap the actions in case we have a completely different output%0A    OpenElementEvent root = new OpenElementEventImpl(%22root%22);%0A    wrapped.add(new Action(Operator.KEEP, Collections.singletonList(root)));%0A    wrapped.addAll(actions);%0A    wrapped.add(new Action(Operator.KEEP, Collections.singletonList(new CloseElementEventImpl(root))));%0A
     assertIs
@@ -3551,23 +3551,23 @@
 L(toXML(
-actions
+wrapped
 ));%0A  %7D%0A

Update well-formedness test