package p.studio.compiler.pbs.parser;

import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.utilities.structures.ReadOnlyList;

import java.util.ArrayList;

/**
 * Lightweight cursor used by the manual parser to navigate the token stream.
 *
 * <p>This type keeps token access logic out of the higher-level parser so the
 * declaration parser can read more like grammar and less like array plumbing.
 */
final class PbsTokenCursor {
    private final ArrayList<PbsToken> tokens = new ArrayList<>();
    private int current;

    PbsTokenCursor(final ReadOnlyList<PbsToken> tokens) {
        for (final var token : tokens) {
            if (token.kind() == PbsTokenKind.COMMENT) {
                continue;
            }
            this.tokens.add(token);
        }
    }

    /**
     * Consumes the next token if it matches any provided kind.
     */
    boolean match(final PbsTokenKind... kinds) {
        for (final var kind : kinds) {
            if (check(kind)) {
                advance();
                return true;
            }
        }
        return false;
    }

    /**
     * Returns whether the current token matches the expected kind.
     */
    boolean check(final PbsTokenKind kind) {
        if (isAtEnd()) return kind == PbsTokenKind.EOF;
        return peek().kind() == kind;
    }

    /**
     * Advances to the next token and returns the previously current token.
     */
    PbsToken advance() {
        if (!isAtEnd()) current++;
        return previous();
    }

    /**
     * Returns whether the cursor reached the synthetic EOF token.
     */
    boolean isAtEnd() {
        return peek().kind() == PbsTokenKind.EOF;
    }

    /**
     * Returns the current token without consuming it.
     */
    PbsToken peek() {
        return tokens.get(current);
    }

    /**
     * Returns the most recently consumed token.
     */
    PbsToken previous() {
        return tokens.get(Math.max(current - 1, 0));
    }

    /**
     * Returns the token at a positive lookahead offset from current token.
     */
    PbsToken peek(final int offset) {
        final var index = Math.min(current + Math.max(offset, 0), tokens.size() - 1);
        return tokens.get(index);
    }

    /**
     * Returns whether the token at lookahead offset 1 matches the expected kind.
     */
    boolean checkNext(final PbsTokenKind kind) {
        return peek(1).kind() == kind;
    }
}
