package parser;

/**
 * Factory for P grammar non-terminal objects.
 */
public class PFact extends ATVFactory {
    /**
     * Token visitor to parse E grammar non-terminals.
     */
    private ITokVisitor _parseE;

    /**
     * Factory to parse E grammar non-terminals.
     */
    private EFact _eFact;

    /**
     * Token visitor to parse a right parenthesis terminal.
     */
    private ITokVisitor _parseRP = new RightPToken.IRightPVisitor() {
        public Object rightPCase(RightPToken host, Object param) {
            return host;
        }

        public Object defaultCase(AToken host, Object param) {
            throw new IllegalArgumentException("Wrong token: '" + host + "'");
        }
    };

    /**
     * Initializer lambda for this factory.
     */
    private ILambda _initializer = new ILambda() {
        public Object apply(Object param) {
            // change state to no-op
            _initializer = NoOpLambda.Singleton;

            // initialize
            _parseE = _eFact.makeVisitor();
            return null;
        }
    };

    /**
     * Constructor for the T1a factory.
     *
     * @param tkz   tokenizer to use
     * @param eFact factory for E grammar non-terminals
     */
    public PFact(ITokenizer tkz, EFact eFact) {
        super(tkz);
        _eFact = eFact;
    }

    /**
     * Make the visitor.
     */
    private void initialize() {
        _initializer.apply(null);
    }

    /**
     * Constructor for the P factory, Does not take a factory for E grammar non-terminals. That factory has to be set
     * using setParseEFactory().
     *
     * @param tkz tokenizer to use
     */
    public PFact(ITokenizer tkz) {
        super(tkz);
    }

    /**
     * Set the factory for E grammar non-terminals.
     *
     * @param eFact factory for E grammar non-terminals
     */
    public void setParseEFactory(EFact eFact) {
        _eFact = eFact;
    }

    /**
     * Make a node.
     *
     * @param host left parenthesis token
     * @param inp  lambda to apply
     * @return produced node
     */
    private Object makeNode(LeftPToken host, Object inp) {
        ILambda l = (ILambda) inp;
        return l.apply(new P(host,
                             (E) nextToken().execute(_parseE, Identity.Singleton),
                             (RightPToken) nextToken().execute(_parseRP, Identity.Singleton)));
    }


    /**
     * Make a token visitor to parse an E1a non-terminal.
     *
     * @return token visitor
     */
    public ITokVisitor makeVisitor() {
        initialize();
        return new LeftPToken.ILeftPVisitor() {
            public Object leftPCase(LeftPToken host, Object inp) {
                return makeNode(host, inp);
            }

            public Object defaultCase(AToken host, Object param) {
                throw new IllegalArgumentException("Wrong token: '" + host + "'");
            }
        };
    }

    /**
     * Make a token visitor that delegates to the given visitor in a chain of responsibility.
     *
     * @param successor visitor to serve as successor in the chain
     */
    public ITokVisitor makeChainedVisitor(final ITokVisitor successor) {
        initialize();
        // We have a concrete token here, so we can determine a match immediately.
        // Therefore, we do not need to set up a lambda.
        return new LeftPToken.ILeftPVisitor() {
            public Object leftPCase(LeftPToken host, Object inp) {
                return makeNode(host, inp);
            }

            public Object defaultCase(AToken host, Object param) {
                return host.execute(successor, param);
            }
        };
    }

    /**
     * Make a token visitor that delegates to the given visitor in a chain of responsibility.
     *
     * @param successor       visitor to serve as successor in the chain
     * @param successorLambda lambda to pass to the successor in the default case
     */
    public ITokVisitor makeBacktrackChainedVisitor(final ITokVisitor successor, final ILambda successorLambda) {
        initialize();
        // We have a concrete token here, so we can determine a match immediately.
        // Therefore, we do not need to set up a lambda.
        return new LeftPToken.ILeftPVisitor() {
            public Object leftPCase(LeftPToken host, Object inp) {
                return makeNode(host, inp);
            }

            public Object defaultCase(AToken host, Object param) {
                return host.execute(successor, successorLambda);
            }
        };
    }
}

