package de.renew.logging.gui;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;

import org.apache.log4j.Appender;
import org.apache.log4j.Logger;


/**
 * Creates new instances of the registered Appenders, which
 * the user can add to the loggers.
 *
 * @author Sven Offermann
 *
 */
public class AppenderFactory {
    private static final Logger LOGGER = Logger.getLogger(AppenderFactory.class);
    private static final String APPENDER_TYPE_PREFIX = "appender.type.";
    private static final String APPENDER_TYPE_CLASS = "class";
    private static final String APPENDER_TYPE_BUILDER = "builder";

    private static AppenderFactory _factory = null;

    private Map<String, AppenderBuilder> _builders = new Hashtable<String, AppenderBuilder>();

    private AppenderFactory() {
        Properties props = LoggingGuiPlugin.getCurrent().getProperties();

        Enumeration<Object> enumeration = props.keys();
        while (enumeration.hasMoreElements()) {
            String key = (String) enumeration.nextElement();

            if (key.startsWith(APPENDER_TYPE_PREFIX)) {
                if (key.length() > APPENDER_TYPE_PREFIX.length()) {
                    String typeName = key.substring(APPENDER_TYPE_PREFIX.length());

                    if (typeName.indexOf(".") >= 0) {
                        typeName = typeName.substring(0, typeName.indexOf("."));
                    }

                    if (!this._builders.containsKey(typeName)) {
                        String builderName = props.getProperty(
                            APPENDER_TYPE_PREFIX + typeName + "." + APPENDER_TYPE_BUILDER);

                        AppenderBuilder builder = null;
                        if (builderName != null) {
                            try {
                                Class<?> clazz = getClass().getClassLoader().loadClass(builderName);

                                builder = (AppenderBuilder) clazz.newInstance();
                            } catch (Exception e) {
                                LOGGER.error(
                                    "Can't load instance of builder class for appender type "
                                        + typeName);
                                LOGGER.error(e.getMessage(), e);
                            }
                        }

                        if (builder == null) {
                            String className = props.getProperty(
                                APPENDER_TYPE_PREFIX + typeName + "." + APPENDER_TYPE_CLASS);

                            if (className != null) {
                                try {
                                    builder = new DefaultAppenderBuilder(className);
                                } catch (Exception e) {
                                    LOGGER.error(e.getMessage(), e);
                                }
                            }
                        }

                        if ((builder != null) && (!this._builders.containsKey(typeName))) {
                            this._builders.put(typeName, builder);
                        }
                    }
                }
            }
        }
    }

    /**
     * Creates an instance of the AppenderFactory if it does not exist, or gives back the existing one.
     * @return AppenderFactory
     */
    public static AppenderFactory getInstance() {
        if (_factory == null) {
            _factory = new AppenderFactory();
        }

        return _factory;
    }

    /**
     * {@link org.apache.log4j.Appender}
     * @param appenderTypeName The name of the log4j appender.
     * @return Appender
     * @throws InstantiationException in case the instantiation was unsuccessful.
     */
    public Appender createNewAppender(String appenderTypeName) throws InstantiationException {
        Appender appender = null;

        AppenderBuilder builder = _builders.get(appenderTypeName);

        if (builder != null) {
            appender = builder.newInstance();
        }

        return appender;
    }

    /**
     * {@link de.renew.logging.gui.AppenderBuilder} are hold in a map with the name as key.
     * @return alle names of the created @link de.renew.logging.gui.AppenderFactory.DefaultAppenderBuilder.
     */
    public String[] getAllAppenderTypes() {
        return this._builders.keySet().toArray(new String[] { });
    }

    private class DefaultAppenderBuilder implements AppenderBuilder {
        private final Class<?> _class;

        protected DefaultAppenderBuilder(String appenderClassName) throws ClassNotFoundException {
            getClass();
            this._class = Class.forName(appenderClassName);
        }

        @Override
        public Appender newInstance() throws InstantiationException {
            try {
                return (Appender) _class.newInstance();
            } catch (InstantiationException e) {
                throw e;
            } catch (Exception e) {
                throw new InstantiationException();
            }
        }
    }
}