package edu.stanford.nlp.parser.lexparser;

import edu.stanford.nlp.ling.CategoryWordTag;
import edu.stanford.nlp.ling.Label;
import edu.stanford.nlp.trees.Tree;
import edu.stanford.nlp.trees.TreeTransformer;

import java.util.*;

/**
 */
public class CNFTransformers {

  private CNFTransformers() {}


  static class ToCNFTransformer implements TreeTransformer {
    public Tree transformTree(Tree t) {
      if (t.isLeaf()) {
        return t.treeFactory().newLeaf(t.label());
      }
      Tree[] children = t.children();
      if (children.length > 1 || t.isPreTerminal() || t.label().value().startsWith("ROOT")) {
        Label label = t.label();
        Tree[] transformedChildren = new Tree[children.length];
        for (int childIndex = 0; childIndex < children.length; childIndex++) {
          Tree child = children[childIndex];
          transformedChildren[childIndex] = transformTree(child);
        }
        return t.treeFactory().newTreeNode(label, Arrays.asList(transformedChildren));
      }
      Tree tree = t;
      List<String> conjoinedList = new ArrayList<>();
      while (tree.children().length == 1 && !tree.isPrePreTerminal()) {
        String nodeString = tree.label().value();
        if (!nodeString.startsWith("@")) {
          conjoinedList.add(nodeString);
        }
        tree = tree.children()[0];
      }
      String nodeString = tree.label().value();
      if (!nodeString.startsWith("@")) {
        conjoinedList.add(nodeString);
      }
      String conjoinedLabels;
      if (conjoinedList.size() > 1) {
        StringBuilder conjoinedLabelsBuilder = new StringBuilder();
        for (String s : conjoinedList) {
          conjoinedLabelsBuilder.append("&");
          conjoinedLabelsBuilder.append(s);
        }
        conjoinedLabels = conjoinedLabelsBuilder.toString();
      } else if (conjoinedList.size() == 1) {
        conjoinedLabels = conjoinedList.iterator().next();
      } else {
        return transformTree(t.children()[0]);
      }
      children = tree.children();
      Label label = t.label().labelFactory().newLabel(conjoinedLabels);
      Tree[] transformedChildren = new Tree[children.length];
      for (int childIndex = 0; childIndex < children.length; childIndex++) {
        Tree child = children[childIndex];
        transformedChildren[childIndex] = transformTree(child);
      }
      return t.treeFactory().newTreeNode(label, Arrays.asList(transformedChildren));
    }
  }

  static class FromCNFTransformer implements TreeTransformer {
    public Tree transformTree(Tree t) {
      if (t.isLeaf()) {
        return t.treeFactory().newLeaf(t.label());
      }
      Tree[] children = t.children();
      Tree[] transformedChildren = new Tree[children.length];
      for (int childIndex = 0; childIndex < children.length; childIndex++) {
        Tree child = children[childIndex];
        transformedChildren[childIndex] = transformTree(child);
      }
      Label label = t.label();
      if (!label.value().startsWith("&")) {
        return t.treeFactory().newTreeNode(label, Arrays.asList(transformedChildren));
      }
      String[] nodeStrings = label.value().split("&");
      int i = nodeStrings.length - 1;
      label = t.label().labelFactory().newLabel(nodeStrings[i]);
      Tree result = t.treeFactory().newTreeNode(label, Arrays.asList(transformedChildren));
      while (i > 1) {
        i--;
        label = t.label().labelFactory().newLabel(nodeStrings[i]);
        result = t.treeFactory().newTreeNode(label, Collections.singletonList(result));
      }
      return result;
    }
  }

  public static void main(String[] args) {
    CategoryWordTag.printWordTag = false;
    String path = args[0];
    List<Tree> trees = TreebankAnnotator.getTrees(path, 200, 219, 0, 10);
    List<Tree> annotatedTrees = new TreebankAnnotator(new Options(), path).annotateTrees(trees);
    for (Tree tree : annotatedTrees) {
      System.out.println("ORIGINAL:\n");
      tree.pennPrint();
      System.out.println("CNFed:\n");
      Tree cnfTree = new ToCNFTransformer().transformTree(tree);
      cnfTree.pennPrint();
      System.out.println("UnCNFed:\n");
      Tree unCNFTree = new FromCNFTransformer().transformTree(cnfTree);
      unCNFTree.pennPrint();
      System.out.println("\n\n");
    }
  }


}


