// Keep this file in sync with
// ../../../../../taglet/java/org/checkerframework/taglet/ManualTaglet.java .

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 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 node, Void p) {
                for (DocTree dt : node.getContent()) {
                    return dt.accept(this, null);
                }
                return "";
            }

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

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

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