package com.tyron.common.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * Adopted from: <a
 * href="https://github.com/JetBrains/intellij-community/blob/master/platform/util/base/src/com/intellij/filename/UniqueNameBuilder.java">UniqueNameBuilder.java</a>
 */
public final class UniqueNameBuilder<T> {
  private static final String VFS_SEPARATOR = "/";
  private final Map<T, String> myPaths = new HashMap<>();
  private final String mySeparator;
  private final String myRoot;

  public UniqueNameBuilder(String root, String separator) {
    myRoot = root;
    mySeparator = separator;
  }

  public boolean contains(T file) {
    return myPaths.containsKey(file);
  }

  public int size() {
    return myPaths.size();
  }

  private static final class Node {
    final String myText;
    final HashMap<String, Node> myChildren;
    final Node myParentNode;
    int myNestedChildrenCount;

    Node(String text, Node parentNode) {
      myText = text;
      myParentNode = parentNode;
      myChildren = new HashMap<>();
    }

    Node findOrAddChild(String word) {
      Node node = myChildren.get(word);
      if (node == null) myChildren.put(word, node = new Node(word, this));
      return node;
    }
  }

  private final Node myRootNode = new Node("", null);

  // Build a trie from path components starting from end
  // E.g. following try will be build from example
  //
  // |<-------[/fabrique]  <-  [/idea]
  // /idea/pycharm/download/index.html                                                 |
  // /idea/fabrique/download/index.html           [RootNode] <- [/index.html] <- [/download] <-
  // [/pycharm]  <- [/idea]
  // /idea/pycharm/documentation/index.html                              |
  //                                                                     |<------[/documentation] <-
  // [/pycharm]  <- [/idea]
  public void addPath(T key, String path) {
    path = trimStart(path, myRoot);
    myPaths.put(key, path);

    Node current = myRootNode;
    Iterator<String> pathComponentsIterator = new PathComponentsIterator(path);

    while (pathComponentsIterator.hasNext()) {
      String word = pathComponentsIterator.next();
      current = current.findOrAddChild(word);
    }
    for (Node c = current; c != null; c = c.myParentNode) ++c.myNestedChildrenCount;
  }

  private static String trimStart(String s, String prefix) {
    if (s.startsWith(prefix)) {
      return s.substring(prefix.length());
    }
    return s;
  }

  public String getShortPath(T key) {
    String path = myPaths.get(key);
    if (path == null) return key.toString();

    Node current = myRootNode;
    Node firstNodeWithBranches = null;
    Node firstNodeBeforeNodeWithBranches = null;
    Node fileNameNode = null;

    Iterator<String> pathComponentsIterator = new PathComponentsIterator(path);

    while (pathComponentsIterator.hasNext()) {
      String pathComponent = pathComponentsIterator.next();
      current = current.findOrAddChild(pathComponent);

      if (fileNameNode == null) fileNameNode = current;
      if (firstNodeBeforeNodeWithBranches == null
          && firstNodeWithBranches != null
          && current.myChildren.size() <= 1) {
        if (current.myParentNode.myNestedChildrenCount - current.myParentNode.myChildren.size()
            < 1) {
          firstNodeBeforeNodeWithBranches = current;
        }
      }

      if (current.myChildren.size() != 1 && firstNodeWithBranches == null) {
        firstNodeWithBranches = current;
      }
    }

    StringBuilder b = new StringBuilder();
    if (firstNodeBeforeNodeWithBranches == null) {
      firstNodeBeforeNodeWithBranches = current;
    }

    boolean skipFirstSeparator = true;
    for (Node c = firstNodeBeforeNodeWithBranches; c != myRootNode; c = c.myParentNode) {
      if (c != fileNameNode
          && c != firstNodeBeforeNodeWithBranches
          && c.myParentNode.myChildren.size() == 1) {
        b.append(mySeparator);
        b.append("\u2026");

        while (c.myParentNode != fileNameNode && c.myParentNode.myChildren.size() == 1)
          c = c.myParentNode;
      } else {
        if (c.myText.startsWith(VFS_SEPARATOR)) {
          if (!skipFirstSeparator) b.append(mySeparator);
          skipFirstSeparator = false;
          b.append(c.myText, VFS_SEPARATOR.length(), c.myText.length());
        } else {
          b.append(c.myText);
        }
      }
    }
    return b.toString();
  }

  public String getSeparator() {
    return mySeparator;
  }

  private static final class PathComponentsIterator implements Iterator<String> {
    private final String myPath;
    private int myLastPos;
    private int mySeparatorPos;

    PathComponentsIterator(String path) {
      myPath = path;
      myLastPos = path.length();
      mySeparatorPos = path.lastIndexOf(VFS_SEPARATOR);
    }

    @Override
    public boolean hasNext() {
      return myLastPos != 0;
    }

    @Override
    public String next() {
      if (myLastPos == 0) throw new NoSuchElementException();
      String pathComponent;

      if (mySeparatorPos != -1) {
        pathComponent = myPath.substring(mySeparatorPos, myLastPos);
        myLastPos = mySeparatorPos;
        mySeparatorPos = myPath.lastIndexOf(VFS_SEPARATOR, myLastPos - 1);
      } else {
        pathComponent = myPath.substring(0, myLastPos);
        if (!pathComponent.startsWith(VFS_SEPARATOR)) pathComponent = VFS_SEPARATOR + pathComponent;
        myLastPos = 0;
      }
      return pathComponent;
    }

    @Override
    public void remove() {
      throw new UnsupportedOperationException("remove");
    }
  }
}
