package de.renew.refactoring.parse;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;

import de.renew.formalism.java.JavaNetParser;
import de.renew.formalism.java.JavaNetParserConstants;
import de.renew.formalism.java.JavaNetParserTokenManager;
import de.renew.formalism.java.ParseException;
import de.renew.formalism.java.ParsedDeclarationNode;
import de.renew.formalism.java.Token;
import de.renew.net.inscription.TransitionInscription;


/**
 * Provides static methods to assist refactoring parsers that use {@link
 * JavaNetParser}.
 *
 * @author 2mfriedr
 */
public class JNPParser {
    private static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(JNPParser.class);

    /**
     * Should not be instantiated
     */
    private JNPParser() {}

    /**
     * @see JNPParser#netParser(String, ParsedDeclarationNode)
     */
    public static JavaNetParser netParser(final String input) {
        return netParser(input, new ParsedDeclarationNode());
    }

    /**
     * Returns a {@link JavaNetParser} object with the refactoring flag set to
     * {@code true}, the specified declaration node, and the specified input
     * string. If the declaration node is not needed or does not exist, use
     * {@link #netParser(String)}.
     *
     * {@link JavaNetParser}
     * {@link ParsedDeclarationNode}
     * @param input the input string
     * @param declarationNode the declaration node
     * @return a net parser
     */
    public static JavaNetParser netParser(
        final String input, final ParsedDeclarationNode declarationNode)
    {
        InputStream inputStream = new ByteArrayInputStream(input.getBytes());
        JavaNetParser netParser = new JavaNetParser(inputStream);
        netParser.refactoring = true;
        netParser.setDeclarationNode(declarationNode);
        return netParser;
    }

    /**
     * Returns a token manager for the specified input string. This method is
     * useful if the tokens and their kinds need to be examined.
     *
     * @param netParser the input string
     * @return the token manager
     */
    public static JavaNetParserTokenManager tokenManager(final JavaNetParser netParser) {
        return netParser.token_source;
    }

    /**
     * Checks if the input string contains only one token of kind {@code
     * IDENTIFIER}. This method is useful to check if an input string is i.e.
     * a valid variable or channel name.
     *
     * @param netParser the input string
     * @return {@code true}, if the input string is an identifier, otherwise
     * {@code false}
     */
    public static boolean isIdentifier(final JavaNetParser netParser) {
        JavaNetParserTokenManager tokenManager = tokenManager(netParser);
        Token token = tokenManager.getNextToken();
        boolean isOnlyToken = tokenManager.getNextToken().kind == JavaNetParserConstants.EOF;
        return (isOnlyToken && token.kind == JavaNetParserConstants.IDENTIFIER);
    }

    /**
     * Tries to parse the input as a declaration node. If there is a parse
     * error, {@code null} is returned.
     *
     * @param input the input string
     * @return the parsed declaration node
     */
    public static ParsedDeclarationNode declarationNode(final String input) {
        try {
            return netParser(input).DeclarationNode();
        } catch (ParseException e) {
            LOGGER.debug("Declaration node could not be parsed: " + input + ", " + e.getMessage());
            return null;
        }
    }

    /**
     * Tries to parse the input as a transition inscription. If there is a
     * parse error, an empty list is returned.
     *
     * @param netParser the input string
     * @return a collection of transition inscriptions
     */
    public static Collection<TransitionInscription> transitionInscriptions(
        final JavaNetParser netParser)
    {
        try {
            return netParser.TransitionInscription(false, null);
        } catch (ParseException e) {
            LOGGER.debug(e.getMessage());
            return Collections.emptyList();
        }
    }

    /**
    * Tries to parse the input as a transition inscription and returns only the
    * first inscription.
    *
    * @param netParser the input string
    * @return the first transition inscription
    */
    public static TransitionInscription firstInscription(final JavaNetParser netParser) {
        for (TransitionInscription inscription : JNPParser.transitionInscriptions(netParser)) {
            return inscription;
        }
        return null;
    }
}