package com.google.javascript.gents.util;

import com.google.javascript.jscomp.AbstractCompiler;
import com.google.javascript.jscomp.NodeUtil;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Set;
import javax.annotation.Nullable;

/** Utility methods for variable naming. */
public class NameUtil {
  private AbstractCompiler compiler;

  public NameUtil(AbstractCompiler compiler) {
    this.compiler = compiler;
  }

  /** Returns the last identifier of a qualified name string. */
  public String lastStepOfName(String name) {
    return lastStepOfName(NodeUtil.newQName(compiler, name));
  }

  /** Returns the last identifier of a name node. */
  public String lastStepOfName(Node n) {
    return n.isGetProp() ? n.getLastChild().getString() : n.getQualifiedName();
  }

  /**
   * Gets the longest namespace that is a prefix of the name string. Returns null if no namespaces
   * are valid prefixes.
   */
  @Nullable
  public String findLongestNamePrefix(String name, Set<String> namespaces) {
    return findLongestNamePrefix(NodeUtil.newQName(compiler, name), namespaces);
  }

  /**
   * Gets the longest namespace that is a prefix of the name node. Returns null if no namespaces are
   * valid prefixes.
   */
  @Nullable
  public String findLongestNamePrefix(Node name, Set<String> namespaces) {
    if (namespaces.contains(name.getQualifiedName())) {
      return name.getQualifiedName();
    } else if (name.isGetProp()) {
      return findLongestNamePrefix(name.getFirstChild(), namespaces);
    }
    return null;
  }

  /**
   * Returns the new name string with a prefix replaced with the new prefix. Returns input name if
   * prefix does not exist.
   */
  public String replacePrefixInName(String name, String prefix, String newPrefix) {
    Node nameNode = NodeUtil.newQName(compiler, name);
    // Placeholder node to ensure name has a parent
    Node placeholder = new Node(Token.EMPTY, nameNode);
    replacePrefixInName(nameNode, prefix, newPrefix);
    return placeholder.getFirstChild().getQualifiedName();
  }

  /**
   * In-place replaces a prefix with a new prefix in a name node. Does nothing if prefix does not
   * exist.
   */
  public void replacePrefixInName(Node name, String prefix, String newPrefix) {
    if (name.matchesQualifiedName(prefix)) {
      Node newName = NodeUtil.newQName(compiler, newPrefix);
      JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(name);
      newName.setJSDocInfo(jsdoc);
      name.replaceWith(newName);
    } else {
      if (name.isGetProp()) {
        replacePrefixInName(name.getFirstChild(), prefix, newPrefix);
      }
    }
  }
}