/*
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.java.ast;

import java.util.NoSuchElementException;
import java.util.Objects;

import net.sourceforge.pmd.lang.ast.GenericToken;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;

/**
 * PRIVATE FOR NOW, find out what is useful to move to the interface
 * (probably everything).
 *
 * @author Clément Fournier
 */
final class TokenUtils {

    // mind: getBeginLine and getEndLine on JavaccToken are now very slow.

    private TokenUtils() {

    }

    public static <T extends GenericToken<T>> T nthFollower(T token, int n) {
        if (n < 0) {
            throw new IllegalArgumentException("Negative index?");
        }
        while (n-- > 0 && token != null) {
            token = token.getNext();
        }
        if (token == null) {
            throw new NoSuchElementException("No such token");
        }

        return token;
    }

    /**
     * This is why we need to doubly link tokens... otherwise we need a
     * start hint.
     *
     * @param startHint Token from which to start iterating,
     *                  needed because tokens are not linked to their
     *                  previous token. Must be strictly before the anchor
     *                  and as close as possible to the expected position of
     *                  the anchor.
     * @param anchor    Anchor from which to apply the shift. The n-th previous
     *                  token will be returned
     * @param n         An int > 0
     *
     * @throws NoSuchElementException If there's less than n tokens to the left of the anchor.
     */
    // test only
    public static <T extends GenericToken<T>> T nthPrevious(T startHint, T anchor, int n) {
        if (startHint.compareTo(anchor) >= 0) {
            throw new IllegalStateException("Wrong left hint, possibly not left enough");
        }
        if (n <= 0) {
            throw new IllegalArgumentException("Offset can't be less than 1");
        }
        int numAway = 0;
        T target = startHint;
        T current = startHint;
        while (current != null && !current.equals(anchor)) {
            current = current.getNext();
            // wait "n" iterations before starting to advance the target
            // then advance "target" at the same rate as "current", but
            // "n" tokens to the left
            if (numAway == n) {
                target = target.getNext();
            } else {
                numAway++;
            }
        }
        if (!Objects.equals(current, anchor)) {
            throw new IllegalStateException("Wrong left hint, possibly not left enough");
        } else if (numAway != n) {
            // We're not "n" tokens away from the anchor
            throw new NoSuchElementException("No such token");
        }

        return target;
    }

    public static void expectKind(JavaccToken token, int kind) {
        assert token.kind == kind : "Expected " + token.getDocument().describeKind(kind) + ", got " + token;
    }

}
