package org.checkerframework.taglet;

import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.TextTree;
import com.sun.source.doctree.UnknownBlockTagTree;
import com.sun.source.doctree.UnknownInlineTagTree;
import com.sun.source.util.SimpleDocTreeVisitor;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import javax.lang.model.element.Element;
import jdk.javadoc.doclet.Taglet;

/**
 * A taglet for processing the {@code @checker_framework.manual} javadoc block tag, which inserts
 * references to the Checker Framework manual into javadoc.
 *
 * <p>The {@code @checker_framework.manual} tag is used as follows:
 *
 * <ul>
 *   <li>{@code @checker_framework.manual #} expands to a top-level link to the Checker Framework
 *       manual
 *   <li>{@code @checker_framework.manual #anchor text} expands to a link with some text to a
 *       particular part of the manual
 * </ul>
 */
public class ManualTaglet implements Taglet {

  private static final String NAME = "checker_framework.manual";

  @Override
  public String getName() {
    return NAME;
  }

  private static final EnumSet<Location> allowedSet = EnumSet.allOf(Location.class);

  @Override
  public Set<Taglet.Location> getAllowedLocations() {
    return allowedSet;
  }

  @Override
  public boolean isInlineTag() {
    return false;
  }

  /**
   * Formats a link, given an array of tokens.
   *
   * @param parts the array of tokens
   * @return a link to the manual top-level if the array size is one, or a link to a part of the
   *     manual if it's larger than one
   */
  private String formatLink(String[] parts) {
    String anchor, text;
    if (parts.length < 2) {
      anchor = "";
      text = "Checker Framework";
    } else {
      anchor = parts[0];
      text = parts[1];
    }
    return String.format("<A HREF=\"https://checkerframework.org/manual/%s\">%s</A>", anchor, text);
  }

  /**
   * Formats the {@code @checker_framework.manual} tag, prepending the tag header to the tag
   * content.
   *
   * @param text the tag content
   * @return the formatted tag
   */
  private String formatHeader(String text) {
    return String.format("<DT><B>See the Checker Framework Manual:</B><DD>%s<BR>", text);
  }

  @Override
  public String toString(List<? extends DocTree> tags, Element element) {
    if (tags.isEmpty()) {
      return "";
    }
    StringJoiner sb = new StringJoiner(", ");
    for (DocTree t : tags) {
      String text = getText(t);
      String[] split = text.split(" ", 2);
      sb.add(formatLink(split));
    }
    return formatHeader(sb.toString());
  }

  static String getText(DocTree dt) {
    return new SimpleDocTreeVisitor<String, Void>() {
      @Override
      public String visitUnknownBlockTag(UnknownBlockTagTree tree, Void p) {
        for (DocTree dt : tree.getContent()) {
          return dt.accept(this, null);
        }
        return "";
      }

      @Override
      public String visitUnknownInlineTag(UnknownInlineTagTree tree, Void p) {
        for (DocTree dt : tree.getContent()) {
          return dt.accept(this, null);
        }
        return "";
      }

      @Override
      public String visitText(TextTree tree, Void p) {
        return tree.getBody();
      }

      @Override
      protected String defaultAction(DocTree tree, Void p) {
        return "";
      }
    }.visit(dt, null);
  }
}
