package parser;

/**
 * Factory for E grammar non-terminal objects.
 */
public class EFact extends ATVFactory {
    /**
     * Factory for F grammar non-terminals.
     */
    private FFact _fFact;

    /**
     * Visitor for F grammar non-terminals.
     */
    private ITokVisitor _parseF;

    /**
     * Factory for E1 grammar non-terminals.
     */
    private E1Fact _e1Fact;

    /**
     * Visitor for E1 grammar non-terminals.
     */
    private ITokVisitor _parseE1;

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

            // initialize
            _parseF = _fFact.makeVisitor();
            _parseE1 = _e1Fact.makeVisitor();
            return null;
        }
    };

    /**
     * Constructor for the E factory,
     *
     * @param tkz tokenizer to use
     */
    public EFact(ITokenizer tkz) {
        super(tkz);
        _fFact = new FFact(tkz);
        _e1Fact = new E1Fact(tkz);
    }

    /**
     * Make a token visitor to parse an E non-terminal.
     *
     * @return token visitor
     */
    public ITokVisitor makeVisitor() {
        initialize();
        return new ITokVisitor() {
            public Object defaultCase(AToken host, Object param) {
                return new E((F) host.execute(_parseF, param), (E1) nextToken().execute(_parseE1, 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
     */
    public ITokVisitor makeChainedVisitor(final ITokVisitor successor) {
        initialize();
        return new ITokVisitor() {
            public Object defaultCase(AToken host, Object inp) {
                Object o = host.execute(_fFact.makeChainedVisitor(successor), inp);
                return (o instanceof F) ? new E((F) o, (E1) nextToken().execute(_parseE1, inp)) : o;
            }
        };
    }

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

    /**
     * Factory for Empty grammar terminals.
     */
    private class EmptyFact extends ATVFactory {
        /**
         * Constructor for Empty grammar terminals.
         *
         * @param tkz tokenizer to use
         */
        public EmptyFact(ITokenizer tkz) {
            super(tkz);
        }

        /**
         * Make a visitor to parse an empty terminal.
         *
         * @return visitor to parse an empty terminal
         */
        private ITokVisitor makeEmptyVis() {
            return new ITokVisitor() {
                public Object defaultCase(AToken host, Object param) {
                    putBackToken();
                    System.err.println("Pushback");
                    return Empty.Singleton;
                }
            };
        }

        /**
         * Make a token visitor to parse an Empty terminal.
         *
         * @return token visitor
         */
        public ITokVisitor makeVisitor() {
            return makeEmptyVis();
        }

        /**
         * 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(ITokVisitor successor) {
            return makeEmptyVis();
        }
    }

    /**
     * Factory for F grammar non-terminal objects.
     */
    private class FFact extends ATVFactory {
        /**
         * Factory for F1 grammar non-terminals.
         */
        private F1Fact _f1Fact;

        /**
         * Factory for F2 grammar non-terminals.
         */
        private F2Fact _f2Fact;

        /**
         * Constructor for the F factory,
         *
         * @param tkz tokenizer to use
         */
        public FFact(ITokenizer tkz) {
            super(tkz);
            _f1Fact = new F1Fact(tkz);
            _f2Fact = new F2Fact(tkz);
        }

        /**
         * Make a token visitor to parse an F non-terminal.
         *
         * @return token visitor
         */
        public ITokVisitor makeVisitor() {
            return _f1Fact.makeChainedVisitor(_f2Fact.makeVisitor());
        }

        /**
         * 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(ITokVisitor successor) {
            return _f1Fact.makeChainedVisitor(_f2Fact.makeChainedVisitor(successor));
        }

        /**
         * Factory for F1 grammar non-terminal objects.
         */
        private class F1Fact extends ATVFactory {
            /**
             * Initializer lambda for this factory.
             */
            private ILambda _initializer = new ILambda() {
                public Object apply(Object param) {
                    // change state to no-op
                    _initializer = NoOpLambda.Singleton;

                    // nothing to initialize
                    return null;
                }
            };

            /**
             * Constructor for the F1 factory.
             *
             * @param tkz tokenizer to use
             */
            public F1Fact(ITokenizer tkz) {
                super(tkz);
            }

            /**
             * Make a token visitor to parse an E1a non-terminal.
             *
             * @return token visitor
             */
            public ITokVisitor makeVisitor() {
                initialize();
                return new NumToken.INumVisitor() {
                    public Object numCase(NumToken host, Object inp) {
                        return new F1(host);
                    }

                    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();
                return new NumToken.AChainVis(successor) {
                    public Object numCase(NumToken host, Object inp) {
                        return new F1(host);
                    }
                };
            }

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

            }
        }

        /**
         * Factory for F2 grammar non-terminal objects.
         */
        private class F2Fact extends ATVFactory {
            /**
             * Initializer lambda for this factory.
             */
            private ILambda _initializer = new ILambda() {
                public Object apply(Object param) {
                    // change state to no-op
                    _initializer = NoOpLambda.Singleton;

                    // nothing to initialize
                    return null;
                }
            };

            /**
             * Constructor for the F2 factory.
             *
             * @param tkz tokenizer to use
             */
            public F2Fact(ITokenizer tkz) {
                super(tkz);
            }

            /**
             * Make a token visitor to parse an E1a non-terminal.
             *
             * @return token visitor
             */
            public ITokVisitor makeVisitor() {
                initialize();
                return new IdToken.IIdVisitor() {
                    public Object idCase(IdToken host, Object inp) {
                        return new F2(host);
                    }

                    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();
                return new IdToken.AChainVis(successor) {
                    public Object idCase(IdToken host, Object inp) {
                        return new F2(host);
                    }
                };
            }

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

            }
        }
    }

    /**
     * Factory for E1 grammar non-terminal objects.
     */
    private class E1Fact extends ATVFactory {
        /**
         * Factory for E1a grammar non-terminals.
         */
        private E1aFact _e1aFac;

        /**
         * Factory for Empty grammar terminals.
         */
        private EmptyFact _emptyFac;

        /**
         * Constructor for the E1 factory,
         *
         * @param tkz tokenizer to use
         */
        public E1Fact(ITokenizer tkz) {
            super(tkz);
            _e1aFac = new E1aFact(tkz);
            _emptyFac = new EmptyFact(tkz);

        }

        /**
         * Make a token visitor to parse an E1 non-terminal.
         *
         * @return token visitor
         */
        public ITokVisitor makeVisitor() {
            return _e1aFac.makeChainedVisitor(_emptyFac.makeVisitor());
        }

        /**
         * 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(ITokVisitor successor) {
            return _e1aFac.makeChainedVisitor(_emptyFac.makeChainedVisitor(successor));
        }

        /**
         * Factory for E1a grammar non-terminal objects.
         */
        private class E1aFact extends ATVFactory {
            /**
             * Factory for E grammar non-terminals.
             */
            private EFact _eFact;

            /**
             * Visitor for E grammar non-terminals.
             */
            private ITokVisitor _parseE;

            /**
             * 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 E1a factory.
             *
             * @param tkz tokenizer to use
             */
            public E1aFact(ITokenizer tkz) {
                super(tkz);
                _eFact = EFact.this;
            }

            /**
             * Make a token visitor to parse an E1a non-terminal.
             *
             * @return token visitor
             */
            public ITokVisitor makeVisitor() {
                initialize();
                return new PlusToken.IPlusVisitor() {
                    public Object plusCase(PlusToken host, Object inp) {
                        return new E1a(host, (E) nextToken().execute(_parseE, 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();
                return new PlusToken.AChainVis(successor) {
                    public Object plusCase(PlusToken host, Object inp) {
                        return new E1a(host, (E) nextToken().execute(_parseE, inp));
                    }
                };
            }

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

            }
        }
    }
}

