package de.renew.logging.gui;

import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import CH.ifa.draw.util.Command;
import CH.ifa.draw.util.CommandMenuItem;
import de.renew.draw.ui.api.MenuApi;
import de.renew.draw.ui.ontology.MenuSeparatorFactory;
import de.renew.engine.common.SimulatorEventLogger;
import de.renew.gui.GuiPlugin;
import de.renew.gui.ModeReplacement;
import de.renew.plugin.IPlugin;
import de.renew.plugin.PluginManager;
import de.renew.plugin.annotations.Inject;
import de.renew.plugin.di.DIPlugin;
import de.renew.windowmanagement.Workbench;

/**
 * Provides a graphical user interface (gui) for logging messages
 * produced during a simulation of petri nets.
 *
 * @author Sven Offermann
 **/
public class LoggingGuiPlugin extends DIPlugin {

    /**
     * {@link org.apache.log4j.Logger}
     */
    private static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(LoggingGuiPlugin.class);

    /**
     * A set of references to the controllers of opened logging frames.
     * The set is used during the plugin cleanup process to close
     * opened logging frames.
     */
    private static final Set<LoggingController> LOGGING_FRAME_CONTROLLERS = new HashSet<>();

    /**
     * The menu item to open a windows with a simulation trace.
     */
    private CommandMenuItem _menu;

    /**
     * The controller of the logging configuration tab in the configure simulation frame.
     */
    private ConfigureLoggingController _configTab;
    /**
     * {@link Workbench}
     */
    private final Workbench _workbench;

    /**
     * Initialisation
     *
     * @param workbench {@link Workbench}
     */

    @Inject
    public LoggingGuiPlugin(Workbench workbench) {
        this._workbench = workbench;
    }

    /**
     * Initializes the plugin.
     * Registers commands and connections to underlying plugins.
     **/
    @Override
    public void init() {
        LOGGER.debug("initializing Logging GUI plugin.");

        // register ConfigureLoggingController to the list of ConfigurationSimulationTabController
        // used to configure a simulation.
        _configTab = new ConfigureLoggingController();
        GuiPlugin.getCurrent().addConfigTabController(_configTab);

        // create simulation menu entries
        _menu = new CommandMenuItem(new Command("show simulation trace") {
            @Override
            public boolean isExecutable() {
                if (!super.isExecutable()) {
                    return false;
                }
                return ModeReplacement.getInstance().getSimulation().isSimulationActive();
            }

            @Override
            public void execute() {
                new LoggingController(LOGGING_FRAME_CONTROLLERS, _workbench);
            }
        }, KeyEvent.VK_L);

        MenuSeparatorFactory separatorFactory =
            MenuApi.createMenuSeparatorFactory("de.renew.logging.gui");
        MenuApi.registerMenu(GuiPlugin.SIMULATION_MENU, separatorFactory.createSeparator());
        MenuApi.registerMenu(GuiPlugin.SIMULATION_MENU, _menu, "de.renew.logging.gui.showTrace");

        // add a GuiAppender to the simulation root logger
        org.apache.log4j.Logger.getLogger(SimulatorEventLogger.SIM_LOG_PREFIX)
            .addAppender(new GuiAppender());
    }

    /**
     * Convenience Method for getting the logging gui starter object
     * presently registered in the PluginManager
     *
     * @return The instance of the current plugin or {@code null} if none exist
     */
    public static LoggingGuiPlugin getCurrent() {
        Iterator<IPlugin> it =
            PluginManager.getInstance().getPluginsProviding("de.renew.logging.gui").iterator();
        while (it.hasNext()) {
            IPlugin o = it.next();
            if (o instanceof LoggingGuiPlugin) {
                return (LoggingGuiPlugin) o;
            }
        }
        return null;
    }

    // --------------------------------------- Opening and closing the gui
    // Logging -----------------------------------------

    /**
     * closes all {@link LoggingController}.
     *
     * @return boolean - this value is meaningless.
     */
    @Override
    public boolean cleanup() { // TODO: antipattern; the boolean is useless; at least make a try-catch-block to return false in case of an exception or error.
        Iterator<LoggingController> i = LOGGING_FRAME_CONTROLLERS.iterator();
        while (i.hasNext()) {
            LoggingController c = i.next();
            c.closeFrame();
        }

        MenuApi.unregisterMenu(_menu);

        GuiPlugin.getCurrent().removeConfigTabController(_configTab);

        return true;
    }

    /**
     * removes the controller from the Frame.
     *
     * @param controller {@link LoggingController}
     */
    public void closedLoggingFrame(LoggingController controller) {
        LOGGING_FRAME_CONTROLLERS.remove(controller);
    }
}