package de.renew.gui;

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.rmi.RemoteException;
import java.util.Enumeration;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import CH.ifa.draw.application.DrawApplication;
import CH.ifa.draw.figures.TextFigure;
import CH.ifa.draw.framework.FigureWithID;
import CH.ifa.draw.standard.StandardDrawingView;
import CH.ifa.draw.standard.ToolButton;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.ui.ontology.Tool;
import de.renew.engine.searchqueue.SearchQueue;
import de.renew.gui.tool.CPNSelectionTool;
import de.renew.io.FileFilterCreator;
import de.renew.plugin.PluginManager;
import de.renew.remote.NetInstanceAccessor;
import de.renew.remote.PlaceInstanceAccessor;
import de.renew.shadowcompiler.ShadowCompilerFactory;
import de.renew.simulatorontology.shadow.SyntaxException;
import de.renew.windowmanagement.Workbench;


/** This class represents the main frame of the graphical Renew
 * user interface. It is based on an old and heavily modified
 * version of JHotDraw that can be found in the CH plugin.
 * The frame comprises the menu bar and all editing tools.
 * It also covers all saving, loading, compiling and simulating
 * features of the application (and has grown too fat, by the way).
 *
 * <p> <strong>All</strong> methods of this class
 * <strong>must</strong> be called from the AWT/Swing event
 * thread to avoid concurrency problems, including the
 * constructor.  </p>
 *
 * @author Frank Wienberg
 * @author Olaf Kummer
 * @author Michael Duvigneau
 * @author Joern Schumacher
 * @author Lawrence Cabac
 *
 * @see DrawApplication
 **/
public class CPNApplication extends DrawApplication {
    private static final org.apache.log4j.Logger logger =
        org.apache.log4j.Logger.getLogger(CPNApplication.class);
    static public final String CPNIMAGES = "/de/renew/gui/images/";
    private final CPNDrawingEditor fDelegateEditor;

    private GuiPlugin fPlugin;

    protected CPNApplication(
        String[] args, GuiPlugin plugin, Workbench workbench, CPNDrawingEditor editor)
    {
        super(
            "Reference Net Workshop", "de.renew.gui.CPNDrawing",
            FileFilterCreator.createRNWFileFilter(), args, CPNIMAGES + "RENEW.gif", workbench,
            editor);

        fDelegateEditor = editor;
        fPlugin = plugin;
        if (fPlugin != null) {
            fPlugin.notifyGuiStart(this);
        }
    }

    public ShadowCompilerFactory getDefaultCompilerFactory() {
        return ModeReplacement.getInstance().getDefaultCompilerFactory();
    }

    protected Drawing createDrawing() {
        return new CPNDrawing();
    }

    /**
     * Informs this gui that the simulation has been terminated.
     * All simulation-related windows will be closed.
     * <p>
     * This method must be called in sync with the AWT event queue.
     * </p>
     **/
    void cleanupSimulationWindows() {
        showStatus("Simulation terminated.");
        BindingSelectionFrame.close();
        closeAllSimulationDrawings();
    }

    void closeAllSimulationDrawings() {
        boolean goon;

        do {
            goon = false;
            Enumeration<Drawing> drawenumeration =
                fDelegateEditor.getDrawingLookup().getAllDrawings();

            while (drawenumeration.hasMoreElements()) {
                Drawing drawing = drawenumeration.nextElement();

                if (drawing instanceof CPNInstanceDrawing || drawing instanceof TokenBagDrawing) {
                    closeDrawing(drawing);
                    goon = true;
                    break;
                }
            }
        } while (goon);
    }

    public void openNetPatternDrawing(String netName) {
        openNetPatternDrawing(netName, FigureWithID.NOID);
    }

    public void openNetPatternDrawing(String netName, int elementID) {
        fDelegateEditor.showNetPatternDrawing(netName, elementID);
    }

    protected StandardDrawingView createDrawingView(int width, int height) {
        return new CPNDrawingView(this, width, height);
    }

    protected Component wrapStatusLine(Component statusLine) {
        boolean showClock = ModeReplacement.getInstance().getSimulation().isStrictlySequential();
        if (showClock) {
            JPanel panel = new JPanel();
            GridBagLayout layout = new GridBagLayout();

            panel.setLayout(layout);

            GridBagConstraints constraints = new GridBagConstraints();

            constraints.fill = GridBagConstraints.HORIZONTAL;

            constraints.weightx = 1;
            layout.setConstraints(statusLine, constraints);
            panel.add(statusLine);
            if (statusLine instanceof JTextField textStatusLine) {
                // make original status line a bit smaller
                textStatusLine.setColumns(textStatusLine.getColumns() - 23);
            }
            final JTextField clock = new JTextField("0.0", 23);

            SearchQueue.insertTimeListener(
                () -> EventQueue
                    .invokeLater(() -> clock.setText(String.valueOf(SearchQueue.getTime()))));
            clock.setEditable(false);
            constraints.weightx = 0;
            layout.setConstraints(clock, constraints);
            panel.add(clock);

            return panel;
        } else {
            return statusLine;
        }
    }

    boolean syntaxCheck() {
        if (ModeReplacement.getInstance().getSimulation().isSimulationActive()) {
            showStatus("Terminate the current simulation " + "before a syntax check.");
        } else {
            try {
                ModeReplacement.getInstance().getSimulation().syntaxCheckOnly();
                showStatus("Syntax check successful.");
                GuiPlugin.getCurrent().closeSyntaxErrorFrame();
                return true;
            } catch (SyntaxException e) {
                logger.debug(e.getMessage(), e);
                GuiPlugin.getCurrent()
                    .processSyntaxException(FigureExceptionFactory.createFigureException(e), true);
            }
        }
        return false;
    }

    @Override
    public JFrame getFrame() {
        return super.getFrame();
    }

    /**
     * Opens an instance drawing for a given net instance accessor.
     * @param instance The net instance accessor.
     */
    public void openInstanceDrawing(NetInstanceAccessor instance) {
        assert EventQueue.isDispatchThread() : "Must be called within AWT event thread.";
        try {
            InstanceDrawing instDraw = CPNInstanceDrawing.getInstanceDrawing(instance);

            // If the instance drawing could not be created, don't open a window...
            if (instDraw != null) {
                fDelegateEditor.openDrawing(instDraw);
            }
        } catch (RemoteException e) {
            logger.error(e.getMessage(), e);
            JOptionPane.showMessageDialog(
                null, "A problem occurred: " + e + "\n" + "See the console for details.", "Renew",
                JOptionPane.ERROR_MESSAGE);
        }
    }

    public void openTokenBagDrawing(PlaceInstanceAccessor pi) throws RemoteException {
        TokenBagDrawing tokenDraw = TokenBagDrawing.getTokenBagDrawing(pi);

        InstanceDrawing instDraw = CPNInstanceDrawing.getInstanceDrawing( //mode,
            pi.getNetInstance());

        fDelegateEditor.openTokenBagDrawing(tokenDraw, instDraw);
    }

    /**
     * {@inheritDoc}
     * There are two different ways of termination:
     * <ul>
     * <li>If the property <code>de.renew.gui.shutdownOnClose</code>
     *     is set, the responsibility for closing the application is
     *     forwarded to the {@link PluginManager}. </li>
     * <li>If the property is not set (or set to <code>false</code>),
     *     the superclass can do its normal termination job.</li>
     * </ul>
     * {@link DrawApplication#requestClose()}
     **/
    @Override
    public void requestClose() {
        boolean shutdownPluginSystem = false;
        if (fPlugin != null) {
            shutdownPluginSystem = fPlugin.isShutdownOnClose();
        }
        if (shutdownPluginSystem) {
            showStatus("Shutting down plugin system...");
            fPlugin.doShutdownOnClose();
        } else {
            showStatus("Closing Renew Gui...");
            super.requestClose();
        }
    }

    /**
     * Exits the application.
     * (DrawApplication asks to never override this method,
     * but this method calls its super class.
     * It just closes all simulation windows before.)
     */
    @Override
    public void exit() {
        BindingSelectionFrame.close();
        closeAllSimulationDrawings();
        super.exit();
        destroy();
    }

    /**
     * Informs the plugin system about gui termination.
     **/
    protected void destroy() {
        if (fPlugin != null) {
            fPlugin.notifyGuiClosed(this);
            fPlugin = null;
        }
    }

    @Override
    public boolean canClose() {
        // Ask for simulation termination only if it's not the system
        // shutdown anyway. And if there is a running simulation.
        if (!PluginManager.getInstance().isStopping()
            && ModeReplacement.getInstance().getSimulation().isSimulationActive()) {
            int answer = JOptionPane.showConfirmDialog(
                null,
                "Renew: " + "The simulation engine is still active."
                    + "\nIt can continue running without graphical feedback."
                    + "\n Do you want to terminate it now?");
            switch (answer) {
                case 0:
                    // user told us to terminate the simulation.
                    ModeReplacement.getInstance().getSimulation().simulationTerminate();
                    break;
                case 1:
                    // user said "no", so simulation may continue.
                    break;
                case 2:
                    // user said "cancel", so shutdown is not ok
                    return false;
            }
        }
        return super.canClose();
    }

    @Override
    public ToolButton toolButtonForTextFigure(TextFigure figure) {
        PaletteHolder paletteHolder = fPlugin.getPaletteHolder();
        ToolButton toolButton =
            paletteHolder.getTextFigureToolButtonRegistry().toolButtonForTextFigure(figure);
        if (toolButton != null) {
            return toolButton;
        } else {
            return fDelegateEditor.toolButtonForTextFigure(figure);
        }
    }


    /**
     * Creates the selection tool used in this editor.
     */
    protected Tool createSelectionTool() {
        return new CPNSelectionTool(this);
    }

    public void recheckMenus() {
        menuStateChanged();
    }

    // -- main -----------------------------------------------------------
    public static void main(String[] args) {
        System.out
            .println("The CPNApplication class does no longer provide a stand-alone application.");
        System.out.println("Please run de.renew.plugin.PluginManager with the command \"gui\".");
        System.out.println("A simple command line is:");
        System.out.println("       java -jar loader.jar gui [filenames]");
    }
}
