package uk.me.nxg.unity;

import java.util.Map;

/**
 * Provides a mapping from function abbreviations to function definitions.
 * <p>This is a singleton class, so clients must first obtain the
 * instance of the class using {@link #getInstance}.
 */
public abstract class FunctionDefinitionMap
{
    /**
     * The singleton instance of this class.
     */
    private static FunctionDefinitionMap singletonMap = null;
    //private static final FunctionDefinitionMap singletonMap = new UnityFunctionDefinitionMap().initialize();

    /**
     * Constructs a new function-definition map.  This is protected on
     * purpose – instances should be retrieved using the
     * {@link #getInstance} method.
     */
    protected FunctionDefinitionMap() {
        // nothing to do
    }

    /**
     * Obtain an instance of the function-definition map.
     * @return the singleton instance of this class
     */
    public static FunctionDefinitionMap getInstance() {
        if (singletonMap == null) {
            singletonMap = new UnityFunctionDefinitionMap().initialize();
        }
        assert singletonMap != null;
        return singletonMap;
    }

    /**
     * Return the FunctionDefinition corresponding to a name, in a
     * particular syntax.  Returns null if the function is not known
     * in this syntax.
     * @param syntax one of the syntaxes of {@link Syntax}
     * @param functionName the name of the function (eg "log") to be looked up
     * @return a function definition
     */
    public static FunctionDefinition lookupFunctionDefinition(Syntax syntax, String functionName) {
        // Unlike the Resolver class, this function is public, and so
        // can be called from outside this package
        FunctionDefinitionMap fdm = getInstance();
        FunctionDefinitionMap.Resolver r = fdm.getResolver(syntax);
        return r.getFunctionDefinition(functionName);
    }

    /**
     * Return the name corresponding to a function definition in a
     * particular syntax.  The 'name' is the name of the function for
     * display, for example <code>log</code> for Logarithm, and this
     * is in principle syntax-specific (though in fact there is no
     * variation between syntaxes).
     * @param syntax we look up functions known in only this syntax
     * @param fd the definition of the function, which we want the name of
     * @return the name of a function, for display
     */
    public static String lookupFunctionName(Syntax syntax, FunctionDefinition fd) {
        FunctionDefinitionMap fdm = getInstance();
        FunctionDefinitionMap.Resolver r = fdm.getResolver(syntax);
        return r.getFunctionName(fd);
    }

    /**
     * Initialize an instance of the FunctionDefinitionMap.  The class
     * guarantees that this method will be called before the
     * (singleton) instance is used.  Returns an instance of an
     * initialized FunctionDefinitionMap, which becomes the singleton
     * instance, and which may or may not be the same as 'this' when
     * the initialiser is invoked.
     *
     * @return an instance of an initialized FunctionDefinitionMap
     */
    abstract FunctionDefinitionMap initialize();

    /**
     * Obtains a resolver for the given syntax.  This resolver will
     * look up symbol names in this syntax.
     * @param syntax which syntax's resolver do we want?
     * @return a Resolver, or null if the syntax is unrecognised
     */
    abstract Resolver getResolver(Syntax syntax);

    interface Resolver {
        public FunctionDefinition getFunctionDefinition(String name);
        public String getFunctionName(FunctionDefinition fd);
    }
}

