package uk.me.nxg.unity;

/**
 * A factory which associates string syntaxes with actual parsers.
 */
class SyntaxFactory {

    // closures are bloody hard in Java
    private static final java.util.Map<Syntax,SyntaxMaker> parsers;
    static {
        java.util.HashMap<Syntax,SyntaxMaker> m = new java.util.HashMap<Syntax,SyntaxMaker>();

        // The following statements should add a map entry for every
        // element of the Syntax enumeration – it's a programming
        // error if any are missed, and this is checked by assertions below.
        m.put(Syntax.VOUNITS, new SyntaxMaker() {
                public Parser makeParser() { return new Parser_vounits(); }
                public UnitWriter makeWriter(final UnitExpr e) { return new UnitWriterVOUnits(e); }
            });
        m.put(Syntax.FITS, new SyntaxMaker() {
                public Parser makeParser() { return new Parser_fits(); }
                public UnitWriter makeWriter(final UnitExpr e) { return new UnitWriterFits(e); }
            });
        m.put(Syntax.OGIP, new SyntaxMaker() {
                public Parser makeParser() { return new Parser_ogip(); }
                public UnitWriter makeWriter(final UnitExpr e) { return new UnitWriterOgip(e); }
            });
        m.put(Syntax.CDS, new SyntaxMaker() {
                public Parser makeParser() { return new Parser_cds(); }
                public UnitWriter makeWriter(final UnitExpr e) { return new UnitWriterCds(e); }
            });
        m.put(Syntax.LATEX, new SyntaxMaker() {
                public Parser makeParser()
                        throws UnitParserException {
                    throw new UnitParserException("Can't create a LaTeX parser");
                }
                public UnitWriter makeWriter(final UnitExpr e) { return new UnitWriterLaTeX(e); }
            });
        m.put(Syntax.DEBUG, new SyntaxMaker() {
                public Parser makeParser()
                        throws UnitParserException {
                    throw new UnitParserException("Can't create a debug parser");
                }
                public UnitWriter makeWriter(final UnitExpr e) { return new UnitWriterDebugging(e); }
            });
        parsers = java.util.Collections.unmodifiableMap(m);
    }

    private SyntaxFactory() {
        // does nothing -- prevent callers constructing instance
    }

    /**
     * Create a new parser, to parse a single input.
     * @param syntax the type of parser required – one of the
     * syntaxes of {@link Syntax}; must not be null
     * @param input the string to be parsed
     * @return a Parser which can be called to parse the given input;
     * never returns null
     */
    static Parser createParser(Syntax syntax, java.io.Reader input)
            throws UnitParserException {
        Parser parser;

        if (syntax == null) {
            throw new IllegalArgumentException("syntax must not be null");
        }
        
        SyntaxMaker sm = parsers.get(syntax);
        // We should have a SyntaxMaker for every syntax 
        // -- that's a programming error if not
        assert sm != null;
        
        UnitDefinitionMap.Resolver unitResolver = UnitDefinitionMap.getInstance().getResolver(syntax);
        FunctionDefinitionMap.Resolver functionResolver = FunctionDefinitionMap.getInstance().getResolver(syntax);

        // the following will throw an exception if 'syntax'
        // indicates a write-only syntax
        parser = sm.makeParser();

        // if makeParser hasn't thrown an exception,
        // it should be impossible for resolver to be null 
        assert unitResolver != null;

        // It's OK for functionResolver to be null,
        // since this merely indicates that there 
        // is no set of functions defined for the syntax

        parser.setUnitMaker(new OneUnit.Maker(unitResolver));
        parser.setFunctionMaker(new FunctionDefinition.Maker(functionResolver));
        parser.setReader(input, syntax);

        return parser;
    }

    /**
     * Creates a writer for a single unit-expression
     * @param type the syntax of the string to be written – one of the parser strings returned by {@link getParserTypes}
     * @param ue the single unit-expressin which is to be written out
     * @return a writer
     * @throws UnitParserException if the writer cannot be created
     */
    static UnitWriter createWriter(Syntax type, UnitExpr ue)
            throws UnitParserException {
        if (type == null) {
            throw new IllegalArgumentException("syntax must not be null");
        }
        
        SyntaxMaker sm = parsers.get(type);

        // We should have a SyntaxMaker for every syntax 
        // -- that's a programming error if not
        assert sm != null;

        return sm.makeWriter(ue);
    }
}
