/*
 * @(#)DrawApplication.java 5.1
 *
 */
package CH.ifa.draw.application;

import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

import bibliothek.gui.dock.common.event.CFocusListener;

import CH.ifa.draw.DrawPlugin;
import CH.ifa.draw.IOHelper;
import CH.ifa.draw.figures.TextFigure;
import CH.ifa.draw.framework.DrawingTypeManager;
import CH.ifa.draw.io.DrawingFileHelper;
import CH.ifa.draw.io.ImportHolder;
import CH.ifa.draw.io.PositionedDrawing;
import CH.ifa.draw.io.SimpleFileFilter;
import CH.ifa.draw.io.importFormats.ImportFormat;
import CH.ifa.draw.standard.*;
import CH.ifa.draw.util.*;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.DrawingListener;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.draw.ui.api.ApplicationApi;
import de.renew.draw.ui.api.EditorApi;
import de.renew.draw.ui.api.ImageApi;
import de.renew.draw.ui.api.SelectionApi;
import de.renew.draw.ui.api.UndoRedoApi;
import de.renew.draw.ui.ontology.DrawingEditor;
import de.renew.draw.ui.ontology.DrawingView;
import de.renew.draw.ui.ontology.DrawingViewContainer;
import de.renew.draw.ui.ontology.ErrorState;
import de.renew.draw.ui.ontology.StatusDisplayer;
import de.renew.draw.ui.ontology.Tool;
import de.renew.draw.ui.ontology.Toolbar;
import de.renew.draw.ui.ontology.undoredo.UndoRedoManager;
import de.renew.ioontology.ExtensionFileFilter;
import de.renew.windowmanagement.RenewShutdownHook;
import de.renew.windowmanagement.Workbench;


/**
 * DrawApplication defines a standard presentation for
 * standalone drawing editors. The presentation is
 * customized in subclasses.
 * The application is started as follows:
 * <pre>
 * public static void main(String[] args) {
 *     MyDrawApp window = new MyDrawApp();
 * }
 * </pre>
 * <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.
 * A single exception is the method {@link #loadAndOpenCommandLineDrawings}.
 * </p>
 * <p>
 * As of February 2018 this class is deprecated. Please use {@link StandardDrawingEditor} instead.
 *
 * @author Martin Wincierz
 **/
public class DrawApplication
    implements DrawingEditor, PaletteListener, StatusDisplayer, AutosaveSaver
{
    /**
     * Retrieve a logger named according to the value of the name parameter.
     * If the named logger already exists, then the existing instance will be returned.
     * Otherwise, a new instance is created.
     */
    public static org.apache.log4j.Logger logger =
        org.apache.log4j.Logger.getLogger(DrawApplication.class);
    private static final int windowCount = 0;

    // the image resource path
    private static final String fgDrawPath = "/CH/ifa/draw/";

    /**
     * Used to notify all enlisted SavedListeners about a saved drawing.
     */
    public static final List<SavedListener> SAVED_LISTENERS = new ArrayList<>();

    /**
     * The image folder path
     */
    public static final String IMAGES = fgDrawPath + "images/";

    /**
     * The index of the file menu in the menu bar.
     */
    public static final int FILE_MENU = 0;

    /**
     * The index of the edit menu in the menu bar.
     */
    public static final int EDIT_MENU = 1;

    /**
     * The index of the alignment menu in the menu bar.
     */
    public static final int LAYOUT_MENU = 2;

    /**
     * The index of the attributes menu in the menu bar.
     */
    public static final int ATTRIBUTES_MENU = 3;

    /**
     * The index of the drawings menu in the menu bar.
     */
    public static final int DRAWINGS_MENU = 4;

    /**
     * The minimum time interval that has to elapse before a
     * drawing is saved automatically.
     */
    private static final int AUTOSAVE_INTERVAL = 120000;
    /**
     *The Editor that coordinates the different objects that are subelements in the drawing <break></break>
     *editor and also listens to Palette events, control events and keyboard events on a component.
     */
    private final StandardDrawingEditor fDelegateEditor;
    /**
     * The DrawingManager that provides dialogs and convenience methods for saving and loading drawings.
     */
    private final DrawingIOManager fIOManager;
    /**
     * Container for a DrawingView.
     *
     */
    //TODO: usage of this field is unclear here, therefore this comment is only brief -> find out function?
    protected DrawingViewContainer fViewContainer;
    private final boolean fAlwaysSticky = false;

    /**
     * The autosave manager saves a drawing automatically in an intervall.
     */
    private final AutosaveManager autosaveManager = new AutosaveManager(this, AUTOSAVE_INTERVAL);

    /**
     * The undo and redo history manager which keeps
     * track of undo/redo snapshots.
     **/
    private final Vector<DrawingListener> drawingListeners;
    /**
     * Workbench that controls all contained windows and allows listening to changes to the view parts.
     */
    protected Workbench fWorkbench;
    /**
     * The font that is used in the Menu.
     */
    private static Font fMenuFont;


    /**
     * Constructs a drawing window and configures it with the
     * given title, icon and default file type. Then loads
     * drawings from the given file names.
     * <p>
     * The constructor has to be called in sync with the AWT
     * event queue (see class documentation).
     * </p>
     * * @param workbench          controls all contained windows and allows listening to changes to the view parts.
     *      * @param editor             coordinates the different objects that participate in the drawing and
     *      *                           listens to palette, control and focus events.
     *
     * @param title              the title of the application window.
     * @param defaultDrawingType the name used during registration of
     *                           <code>defaultFileFilter</code> at the
     *                           {@link DrawingTypeManager}.
     *                           This parameter is ignored when
     *                           <code>defaultFileFilter==null</code>.
     *                           Otherwise, it must not be <code>null</code>!
     * @param defaultFileFilter  the default file type to use for drawings.
     *                           If <code>null</code>, the default type is
     *                           used.
     * @param drawings           an array of file names to load. May not
     *                           be <code>null</code>, but can be an empty
     *                           array.
     * @param icon               the path to the application icon resource.
     *                           If <code>null</code>, the default icon is
     *                           used.
     **/
    public DrawApplication(
        String title, String defaultDrawingType, ExtensionFileFilter defaultFileFilter,
        String[] drawings, String icon, Workbench workbench, StandardDrawingEditor editor)
    {
        DrawPlugin.setGui(this);
        drawingListeners = new Vector<DrawingListener>();
        // get the DrawingTypeManager so that PLugins can Register Drawings and also
        // export filter to those.
        //        drawingTypeManager = DrawingTypeManager.getInstance();
        if (defaultFileFilter != null) {
            DrawingTypeManager dtm = DrawingTypeManager.getInstance();
            dtm.register(defaultDrawingType, defaultFileFilter);
            dtm.setDefaultFileFilter(defaultFileFilter);
        }

        //        lastSelectedFileFilter = drawingTypeManager.getDefaultFileFilter();
        DrawingLoadServer dls = null;
        int loadServerPort = GUIProperties.loadServerPort();
        if (loadServerPort != -1) {
            try {
                dls = new DrawingLoadServer(this, loadServerPort);
            } catch (IOException ioe) {
                logger.error("Server Socket is occupied!");
            }
        }

        // TEMPORARY:
        fWorkbench = workbench;
        fMenuFont = workbench.getMainFrame().getFont();
        fDelegateEditor = editor;
        fIOManager = new DrawingIOManager(fDelegateEditor, fWorkbench);
        fWorkbench.registerShutdownHook(new DrawApplicationShutdownHook(this));
        Iconkit iconkit = new Iconkit(fWorkbench.getMainFrame());
        if ((icon != null) && (!"Mac OS X".equals(System.getProperty("os.name")))) {
            Image iconImage = iconkit.loadImage(icon);
            if (iconImage == null) {
                logger.error("Resource icon " + icon + " could not be loaded!");
            } else {
                fWorkbench.getMainFrame().setIconImage(iconImage);
            }
        }
        loadAndOpenCommandLineDrawings(drawings);
        if (dls != null) {
            dls.start();
        }
        JMenuItem showMainWindowItem = new JMenuItem("Menu and tools");
        showMainWindowItem.setAccelerator(
            KeyStroke
                .getKeyStroke(KeyEvent.VK_M, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        showMainWindowItem.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                JFrame menuFrame = getFrame();
                menuFrame.setExtendedState(menuFrame.getExtendedState() & ~JFrame.ICONIFIED);
                menuFrame.toFront();
            }
        });
        MenuManager.getInstance().registerMenu(
            DrawPlugin.WINDOWS_MENU, showMainWindowItem,
            DrawPlugin.MENU_PREFIX + DrawPlugin.WINDOWS_MENU + "Menu and tools");
        MenuManager.getInstance().setGui(this);
        WindowsMenuManager.setGui(this);
        logger.debug("DrawApplication: started gui.");
    }

    /**
     * Constructs a drawing window and configures it with the
     * given title, icon and default file type. Then loads
     * drawings from the given file names.
     * <p>
     * The constructor has to be called in sync with the AWT
     * event queue (see class documentatione).
     * </p>
     * @param workbench          controls all contained windows and allows listening to changes to the view parts.
     * @param editor             coordinates the different objects that participate in the drawing and
     *                           listens to palette, control and focus events.
     *
     * @param title              the title of the application window.
     * @param defaultDrawingType the name used during registration of
     *                           <code>defaultFileFilter</code> at the
     *                           {@link DrawingTypeManager}.
     *                           This parameter is ignored when
     *                           <code>defaultFileFilter==null</code>.
     *                           Otherwise, it must not be <code>null</code>!
     * @param defaultFileFilter  the default file type to use for drawings.
     *                           If <code>null</code>, the default type is
     *                           used.
     * @param drawings           an array of file names to load. May not
     *                           be <code>null</code>, but can be an empty
     *                           array.
     * @param icon               the path to the application icon resource.
     *                           If <code>null</code>, the default icon is
     *                           used.
     **/
    public DrawApplication(
        String title, String defaultDrawingType, de.renew.ioontology.FileFilter defaultFileFilter,
        String[] drawings, String icon, Workbench workbench, StandardDrawingEditor editor)
    {

        this(
            title, defaultDrawingType, mapFileFilter(defaultFileFilter), drawings, icon, workbench,
            editor);

    }

    private static SimpleFileFilter mapFileFilter(de.renew.ioontology.FileFilter fileFilter) {

        SimpleFileFilter simpleFileFilter = new SimpleFileFilter() {
            @Override
            public boolean accept(File f) {
                return fileFilter.accept(f);
            }
        };
        simpleFileFilter.setDescription(fileFilter.getDescription());
        return simpleFileFilter;

    }

    /**
     * The shutdown hook is contained in an extra private class, so plugins depending on this class don't need to know
     * the {@link RenewShutdownHook} interface, thus reducing the number of dependencies.
     */
    private class DrawApplicationShutdownHook implements RenewShutdownHook {
        DrawApplication application;

        DrawApplicationShutdownHook(DrawApplication app) {
            application = app;
        }

        @Override
        public boolean canClose() {
            return application.canClose();
        }

        @Override
        public void exit() {
            application.exit();
        }
    }

    /**
     * Returns the icon image.
     * @return the icon image
     * @deprecated This method is not to be used externally. Please use the method {@link ImageApi#getIconImage()} instead.
     */
    @Deprecated
    public Image getIconImage() {
        return fDelegateEditor.getIconImage();
    }

    /**
     * @return the menu frame
     */
    public JFrame getFrame() {
        return fWorkbench.getMainFrame();
    }

    public void dispatchEvent(KeyEvent evt) {
        fDelegateEditor.dispatchEvent(evt);
    }

    /**
     * sets the drawing view container supplier to the given value
     *
     * @param newDVCSupplier new supplier
     */
    public void setDrawingViewContainerSupplier(DrawingViewContainerSupplier newDVCSupplier) {
        // Do nothing, because we don't need this anymore.
        // TODO: We might have to change the tutorial plugin though
    }

    private void updateName(Drawing drawing, File filename) {
        fIOManager.updateName(drawing, filename);

    }

    void addMenu(JMenu newMenu, int index) {
        fDelegateEditor.addMenu(newMenu, index);
    }

    void addMenu(JMenu newMenu) {
        fDelegateEditor.addMenu(newMenu);
    }

    void removeMenu(JMenu remove) {
        fDelegateEditor.removeMenu(remove);
    }

    /**
     * Closes the given drawing view frame immediately.
     * The user is <i>not</i> asked to save changes.
     *
     * @param viewContainer the drawing window to close
     **/


    // TODO: Why does this exist? Maybe we need this?
    public void destroyViewContainer(DrawingViewContainer viewContainer) {
        fDelegateEditor.closeDrawing(viewContainer.view().drawing());
    }

    /**
     * @return the menu font used by this application
     */
    public static Font getMenuFont() {
        return fMenuFont;
    }

    /**
     * Creates a command menu with the given name.
     *
     * @param name the name of the new command menu.
     * @return the created command menu.
     */


    // TODO: WHY IS THIS IN HERE?!?!?
    public static CommandMenu createCommandMenu(String name) {
        CommandMenu cm = new CommandMenu(name);
        cm.setFont(fMenuFont);
        return cm;
    }

    /**
     * @return the drawings of this application.
     * @deprecated This method is not to be used externally. Please use
     * the method {@link EditorApi#getCurrentDrawings()} instead.
     */
    @Deprecated
    public Enumeration<Drawing> drawings() {
        return fDelegateEditor.getDrawingLookup().getAllDrawings();
    }

    /**
     * Adds drawing listener to the list of drawing listeners
     *
     * @param listener the listener to add
     * @deprecated This method is not to be used externally. Please use
     * the method {@link EditorApi#addDrawingListener(DrawingListener)} instead.
     */
    @Deprecated
    public void addDrawingListener(DrawingListener listener) {
        fDelegateEditor.getDrawingLookup().addDrawingListener(listener);
    }

    /**
     * Removes drawing listener from the list of drawing listeners.
     *
     * @param listener the listener to remove
     * @deprecated This method is not to be used externally. Please use
     * the method {@link EditorApi#removeDrawingListener(DrawingListener)} instead.
     */
    @Deprecated
    public void removeDrawingListener(DrawingListener listener) {
        fDelegateEditor.getDrawingLookup().removeDrawingListener(listener);
    }

    public void setCurrentDrawing(DrawingViewContainer viewContainer) {
        // We don't need this anymore
    }

    /**
     * Gets the view for the given drawing.
     *
     * @param drawing the drawing whose view is searched.
     * @return the view of the given drawing.
     */
    public DrawingView getView(Drawing drawing) {
        return fDelegateEditor.getView(drawing);
    }

    /**
     * Sets the specified drawing, its view and view frame as
     * the current drawing and view.
     * A view for the drawing will be created if no one exists.
     * Default position and size for the new view will be taken
     * from the view frame.
     *
     * @param drawing the drawing to be shown.
     */
    public void showDrawingViewContainer(Drawing drawing) {
        fDelegateEditor.openDrawing(drawing);
    }

    /**
     * Closes the drawing
     *
     * @param drawing the drawing to close.
     * @return true if the closing was successful.
     * @deprecated This method is not to be used externally. Please use
     * the method {@link EditorApi#closeDrawing(Drawing)} instead.
     */
    @Deprecated
    public boolean closeDrawing(Drawing drawing) {
        fDelegateEditor.closeDrawing(drawing);
        return fIOManager.getClosingResult();
    }

    /**
     * Handles a user selection in the palette.
     *
     * @see PaletteListener
     */
    public void paletteUserSelected(ToolButton button, boolean doubleclick) {
        fDelegateEditor.paletteUserSelected(button, doubleclick);
    }

    /**
     * Handles when the mouse enters or leaves a palette button.
     *
     * @see PaletteListener
     */
    public void paletteUserOver(ToolButton button, boolean inside) {
        fDelegateEditor.paletteUserOver(button, inside);
    }

    /**
     * Gets the current drawing.
     *
     * @see DrawingEditor
     * @deprecated This method is not to be used externally. Please use
     * the method {@link EditorApi#getCurrentDrawing()} instead.
     */
    @Deprecated
    public Drawing drawing() {
        return fDelegateEditor.drawing();
    }

    /**
     * Gets the current tool.
     *
     * @see DrawingEditor
     */
    public Tool tool() {
        return fDelegateEditor.tool();
    }

    @Override
    public Tool defaultTool() {
        return null;
    }

    /**
     * Gets the current drawing view.
     *
     * @see DrawingEditor
     */
    public DrawingView view() {
        return fDelegateEditor.view();
    }

    /**
     * Gets the drawing view that was active before the current drawing view.
     *
     * @see DrawingEditor
     */
    public DrawingView previousView() {
        return fDelegateEditor.previousView();
    }

    /**
     * Toggle the sticky mode.
     */
    public void toggleAlwaysSticky() {
        // TODO: Is this method really used?
    }

    /**
     * Gets the sticky tool mode.
     */
    public boolean isStickyTools() {
        return fDelegateEditor.isStickyTools();
    }

    /**
     * Set the sticky tools mode.
     */
    public void setStickyTools(boolean sticky) {
        fDelegateEditor.setStickyTools(sticky);
    }

    /**
     * Sets the default tool of the editor.
     *
     * @see DrawingEditor
     * @deprecated This method is not to be used externally. Please use the method {@link EditorApi#toolDone()} instead.
     */
    @Deprecated
    public void toolDone() {
        fDelegateEditor.toolDone();
    }

    /**
     * Handles a change of the current selection. Updates all
     * menu items that are selection sensitive.
     *
     * @see DrawingEditor
     */
    public void selectionChanged(DrawingView view) {
        fDelegateEditor.selectionChanged(view);
    }

    public void recheckMenus() {
        menuStateChanged();
    }

    @Override
    public void menuStateChanged() {
        fDelegateEditor.menuStateChanged();
    }

    /**
     * Shows a status message.
     * As an exception to all other gui methods, this one may be called asynchronously.
     *
     * @see DrawingEditor
     * @deprecated This method is not to be used externally. Please use the method {@link ApplicationApi#showStatus(String)} instead.
     */
    @Deprecated
    public void showStatus(final String string) {
        fDelegateEditor.showStatus(string);
    }

    /**
     * Gets tool button dependent of the type of the given figure.
     *
     * @param figure the figure for which the tool button is searched.
     * @return the tool button for the figure.
     */
    public ToolButton toolButtonForTextFigure(TextFigure figure) {
        return fDelegateEditor.toolButtonForTextFigure(figure);
    }

    /**
     * Opens the text edit for the given figure in the given line and column.
     *
     * @param textFigure the figure which should be edited.
     * @param line       the line where the edit should be opened.
     * @param column     the column where the edit should be opened.
     */
    public void doTextEdit(TextFigure textFigure, int line, int column) {
        fDelegateEditor.doTextEdit(textFigure, line, column, 0, 0);
    }

    /**
     * Opens the text edit for the given figure and select part of the text.
     *
     * @param textFigure  the figure which should be edited.
     * @param startLine   the line where the selection should start.
     * @param startColumn the column where the selection should start.
     * @param endLine     the line where the selection should end.
     * @param endColumn   the column where the selection should end.
     */
    public void doTextEditSelected(
        TextFigure textFigure, int startLine, int startColumn, int endLine, int endColumn)
    {
        fDelegateEditor.doTextEdit(textFigure, startLine, startColumn, endLine, endColumn);
    }

    /**
     * Opens the text edit for the given figure.
     *
     * @param textFigure the figure which should be edited.
     */
    public void doTextEdit(TextFigure textFigure) {
        fDelegateEditor.doTextEdit(textFigure, 0, 0, 0, 0);
    }

    @Override
    public Toolbar getToolbar() {
        return fDelegateEditor.getToolbar();
    }

    /**
     * Creates a new drawing view with a new empty drawing of choosable
     * type. The user is presented with all known drawing types.
     *
     * @return the created drawing. Returns <code>null</code>, if the
     * dialog was cancelled by the user.
     **/
    public Drawing promptChooseNew() {
        return fIOManager.promptChooseNew();
    }

    /**
     * Shows a file dialog and opens a drawing.
     *
     * @param ff the file filter for the file dialog.
     */
    public void promptOpen(FileFilter ff) {
        fIOManager.promptOpen(ff);
    }

    void promptOpenURL() {
        fIOManager.promptOpenURL();
    }

    /**
     * Shows a file dialog and inserts the chosen drawing into the current
     * drawing.
     **/
    protected void promptInsert() {
        fIOManager.promptInsert();
    }

    /**
     * Saves the given drawing to a file whose name is queried from the
     * user. The drawing's name can change during this process.
     *
     * @param drawing the drawing to save.
     * @see #promptSaveAs
     **/
    public void saveDrawingAs(Drawing drawing) {
        fIOManager.saveDrawingAs(drawing);
    }

    /**
     * Saves the given drawing to its file. If the drawing's file is not
     * known, the user is asked to supply a file name. In this case, the
     * drawing's name can change.
     *
     * @param drawing the drawing to save.
     * @return <code>true</code>,  if the drawing has been saved.<br>
     * <code>false</code>, if the action was cancelled for
     * some reason.
     * @see #saveDrawingAs(Drawing)
     **/
    public boolean saveDrawing(Drawing drawing) {

        return fIOManager.saveDrawing(drawing) && notifyAllSavedListeners(drawing);
    }

    /**
     * Opens the drawing from the given file. If it is not in the drawing list it will be loaded.
     *
     * @param file the file of the drawing.
     * @deprecated This method is not to be used externally. Please use the method {@link EditorApi#openOrLoadDrawing(File)} instead.
     */
    @Deprecated
    public void openOrLoadDrawing(File file) {
        fDelegateEditor.openOrLoadDrawing(file);
    }


    /**
     * Opens the drawing with the given path. If it is not in the drawing list it will be loaded.
     *
     * @param path the path of the drawing.
     * @deprecated This method is not to be used externally. Please use the method {@link EditorApi#openOrLoadDrawing(File)} instead.
     */
    @Deprecated
    public void openOrLoadDrawing(String path) {
        openOrLoadDrawing(new File(path));
    }


    /**
     * Saves the given drawing to the given file. There is no user
     * interaction.
     *
     * @param drawing  the drawing to save.
     * @param filename the name of the file to write to.
     **/
    protected void saveDrawing(Drawing drawing, File filename) {
        fIOManager.saveDrawing(drawing, filename);
    }

    /**
     * Shows a file dialog for saving the current drawing.
     *
     * @return <code>true</code>,  if a file name was chosen and set.<br>
     * <code>false</code>, if the action was cancelled for
     * some reason.
     * @see #promptSaveAs(Drawing)
     **/
    protected boolean promptSaveAs() {
        return promptSaveAs(drawing());
    }

    /**
     * Shows a file dialog for saving the given drawing. The default file
     * filter of the drawing is used. The chosen file name is checked to
     * end with one of the file filter's allowed extensions.
     * <p>
     * The chosen file name is not returned. Instead, if a file name is
     * determined, the drawing's name (and all related editor information)
     * is set accordingly.
     * </p>
     *  @param drawing the drawing for which the "save as" dialog is opened
     *
     * @return <code>true</code>,  if a file name was chosen and set.<br>
     * <code>false</code>, if the action was cancelled for
     * some reason.
     **/
    protected boolean promptSaveAs(Drawing drawing) {
        return fIOManager.promptSaveAs(drawing);
    }


    /**
     * Prints the drawing.
     */
    public void print() {
        fDelegateEditor.print();
    }


    /**
     * Saves the given drawing to the given autosave file.
     * The drawing's dirty flag is not cleared.
     *
     * @param drawing the drawing to save.
     * @param file    the name of the autosave file.
     * @param loc     the location of the drawing's view window; it will
     *                be saved to the file, too.
     * @param size    width and height of the drawing's view window; they
     *                will be saved to the file, too.
     * @throws IOException if an I/O-failure occurs.
     **/
    public void saveAutosaveFile(Drawing drawing, File file, Point loc, Dimension size)
        throws IOException
    {
        // Not used anymore
    }

    /**
     * Loads drawings from files and opens them in the editor. There is
     * no feedback to the caller whether the operation was successful. But
     * the user will be informed about failures in the same way as if he
     * has requested the file by himself.
     * A failure while loading one file will not prevent the other files
     * from being treated.
     * <p>
     * This method is thread-safe, meaning it can be called from outside
     * the AWT event queue. The request will be detached from the calling
     * thread and synchronized with the AWT thread.
     * </p>
     *
     * @param filenames the names of the files to read, given as array.
     *                  Each array entry stands for exactly one file name.
     *                  The file names are given in OS-specific notation.
     **/
    public void loadAndOpenCommandLineDrawings(final String[] filenames) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                for (int i = 0; i < filenames.length; ++i) {
                    try {
                        getIOHelper().loadAndOpenDrawings(new URL(filenames[i]));
                    } catch (MalformedURLException e2) {
                        openOrLoadDrawing(filenames[i]);
                    }
                }
            }
        });
    }

    /**
     * Load a drawing from the given url and opens it in the editor. There is
     * no feedback to the caller whether the operation was successful. But
     * the user will be informed about failures in the same way as if he
     * has requested the file by himself.
     * <p>
     * This method is thread-safe, meaning it can be called from outside
     * the AWT event queue. The request will be detached from the calling
     * thread and synchronized with the AWT thread.
     * </p>
     *
     * @param url the url to read
     **/
    public void loadAndOpenCommandLineDrawing(final URL url) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                getIOHelper().loadAndOpenDrawings(url);
            }
        });
    }


    /**
     * Opens the given drawing in the editor.
     *
     * @param posDrawing a <code>Drawing</code> along with its positioning
     *                   information.
     * @deprecated This method is not to be used externally. Please use
     * the method {@link de.renew.draw.ui.api.EditorApi#openDrawing(Drawing, Point, Dimension)} instead.
     */
    @Deprecated
    public synchronized void openDrawing(PositionedDrawing posDrawing) {
        fDelegateEditor.openDrawing(posDrawing.getDrawing());
    }


    /**
     * Opens the given drawing in the editor.
     * The view frame is created with default position and size.
     *
     * @param drawing the <code>Drawing</code> to open.
     * @return the drawing.
     * @deprecated This method is not to be used externally. Please use
     * the method {@link de.renew.draw.ui.api.EditorApi#openDrawing(Drawing)} instead.
     */
    @Deprecated
    public Drawing openDrawing(Drawing drawing) {
        return fDelegateEditor.openDrawing(drawing);
    }

    /**
     * Loads a drawing from the given file and inserts all its figures into
     * the current drawing.
     *
     * @param file the file name where to retrieve the drawing.
     **/
    protected synchronized void loadAndInsertDrawing(File file) {
        Drawing existingDrawing = drawing();
        DrawingView existingView = view();
        Drawing newDrawing = DrawingFileHelper.loadDrawing(file, null);
        if (newDrawing != null) {
            FigureEnumeration figures = newDrawing.figures();
            existingView.clearSelection();
            while (figures.hasMoreElements()) {
                Figure fig = figures.nextFigure();
                existingDrawing.add(fig);
                existingView.addToSelection(fig);
            }
            existingView.checkDamage();
        }
    }

    private IOHelper getIOHelper() {
        return DrawPlugin.getCurrent().getIOHelper();
    }

    /**
    * @deprecated This method is not to be used externally. Please use the {@link de.renew.draw.ui.api.UndoRedoApi} instead.
    */
    @Deprecated(since = "5.0", forRemoval = true)
    public UndoRedoManager getUndoRedoManager() {
        return fDelegateEditor.getUndoRedoManager();
    }

    @Override
    public void drawingViewContainerActivated(DrawingViewContainer viewContainer) {}

    @Override
    public void drawingViewContainerClosing(DrawingViewContainer viewContainer) {}

    /**
     * @deprecated This method is not to be used externally.
     * Please use the method {@link de.renew.draw.ui.api.UndoRedoApi#prepareUndoSnapshot()} instead.
     */
    @Deprecated(since = "5.0", forRemoval = true)
    public void prepareUndoSnapshot() {
        fDelegateEditor.prepareUndoSnapshot();
    }

    /**
     * @deprecated This method is not to be used externally.
     * Please use the method {@link UndoRedoApi#commitUndoSnapshot()} instead.
     */
    @Deprecated(since = "5.0", forRemoval = true)
    public void commitUndoSnapshot() {
        fDelegateEditor.commitUndoSnapshot();
    }

    /**
    * @deprecated This method is not to be used externally and will later be hidden.
    */
    @Deprecated(since = "5.0", forRemoval = true)
    public void prepareAccumulatedUndoSnapshot() {
        fDelegateEditor.prepareAccumulatedUndoSnapshot();
    }

    /**
     * @deprecated This method is not to be used externally and will later be hidden.
     */
    @Deprecated(since = "5.0", forRemoval = true)
    public void triggerAccumulatedUndoSnapshot() {
        fDelegateEditor.triggerAccumulatedUndoSnapshot();
    }

    public void registerFocusListener(CFocusListener focusListener) {
        fWorkbench.registerFocusListener(focusListener);
    }

    public void addSavedListener(SavedListener savedListener) {
        if (!SAVED_LISTENERS.contains(savedListener)) {
            SAVED_LISTENERS.add(savedListener);
        }
    }

    private boolean notifyAllSavedListeners(Drawing drawing) {
        try {
            for (SavedListener listener : new ArrayList<>(SAVED_LISTENERS)) {
                listener.drawingWasSaved(drawing);
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Selects the corresponding elements for the given FigureException.
     *
     * @param e the FigureException whose offending elements are searched.
     * @return the offending elements for the exception.
     * @deprecated This method is not to be used externally. Please use the method {@link SelectionApi#selectOffendingElements(ErrorState)} instead.
     */
    @Deprecated
    public boolean selectOffendingElements(ErrorState e) {
        if (e.getErroneousDrawing().isPresent() && !e.getErroneousFigures().isEmpty()) {
            view().clearSelection();
            showDrawingViewContainer(e.getErroneousDrawing().get());
            FigureEnumeration errorFigures = new FigureEnumerator(e.getErroneousFigures());
            view().addToSelectionAll(errorFigures);


            // Redraw the newly selected elements.
            view().repairDamage();
            Optional<TextFigure> erroneousTextFigure = e.getErroneousFigure(TextFigure.class);
            if (erroneousTextFigure.isPresent()) {
                view().showElement(erroneousTextFigure.get());
                doTextEdit(erroneousTextFigure.get(), e.getLine(), e.getColumn());
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Closes all currently open drawings of the DrawingEditor.
     */
    public void closeAllDrawings() {
        fDelegateEditor.getDrawingLookup().closeAllDrawings();
    }

    /**
     * Closes the currently focused drawing of the DrawingEditor.
     */
    public void closeCurrentDrawing() {
        fDelegateEditor.closeCurrentDrawing();
    }

    /**
     * Creates a new window in the DrawingEditor.
     */
    public void promptNew() {
        fDelegateEditor.newDrawing();
    }

    /**
     * This method is called when the user requests application
     * termination via menu or via window decorations.
     * <p>
     * Steps taken:
     * </p>
     * <ol>
     * <li>Check if data would be lost and ask the user if needed
     *     (<code>canClose()</code> method).</li>
     * <li>Close all windows and do other cleanup work
     *     (<code>exit()</code> method, which in turn calls
     *     <code>destroy()</code>).</li>
     * </ol>
     * @deprecated This method is not to be used externally. Please use the method {@link ApplicationApi#requestClose} instead.
     **/
    @Deprecated
    protected void requestClose() {
        if (canClose()) {
            exit();
        }
    }

    /**
     * Checks whether the application may be terminated. May
     * query the user if needed. The default implementation
     * always returns true.
     *
     * @return <code>true</code>, if no objections are made
     * against exiting the application.
     * @deprecated This method is not to be used externally. Please use the method {@link ApplicationApi#canClose()} instead.
     **/
    @Deprecated
    public boolean canClose() {
        boolean result = true;
        Enumeration<Drawing> drawings = fDelegateEditor.getDrawingLookup().getAllDrawings();

        while (result && drawings.hasMoreElements()) {
            Drawing drawing = drawings.nextElement();
            if (drawing.isModified()) {
                result = fIOManager.handleModifiedDrawing(drawing);
            }
        }

        return result;
    }

    public boolean canOpen(URI path) {
        Iterator<ExtensionFileFilter> iterator =
            getIOHelper().getFileFilterKnownTypes().getFileFilters().iterator();
        while (iterator.hasNext()) {
            ExtensionFileFilter filter = iterator.next();
            if (filter.accept(new File(path.getPath()))) {
                return true;
            }
        }
        ImportFormat[] allImportFormats = getImportHolder().allImportFormats();
        for (ImportFormat importFormat : allImportFormats) {
            if (importFormat.canImport(path)) {
                return true;
            }
        }
        return false;
    }

    private ImportHolder getImportHolder() {
        return DrawPlugin.getCurrent().getImportHolder();
    }

    public void exit() {
        // closeAllDrawings();
    }

    @Override
    public void registerDrawingViewFactoryID(Class key, String id) {
        fDelegateEditor.registerDrawingViewFactoryID(key, id);

    }
}


class DrawingMenuListener implements ActionListener {
    private final DrawApplication fEditor;
    private final Drawing fDrawing;

    DrawingMenuListener(DrawApplication editor, Drawing drawing) {
        fEditor = editor;
        fDrawing = drawing;
    }

    public void actionPerformed(ActionEvent e) {
        fEditor.showDrawingViewContainer(fDrawing);
    }
}


class DrawingLoadServer extends Thread {
    public static org.apache.log4j.Logger logger =
        org.apache.log4j.Logger.getLogger(DrawingLoadServer.class);
    private final DrawApplication editor;
    private ServerSocket s = null;

    public DrawingLoadServer(DrawApplication editor, int port) throws IOException {
        this.editor = editor;
        logger.debug(
            "Drawing Load Server setting up server socket at port " + port
                + " on loopback interface...");
        // JavaDoc of InetAddress.getByName states that a name of null
        // returns a local loopback address - exactly what we want here.
        s = new ServerSocket(port, 50, InetAddress.getByName(null));
        logger.debug("Drawing Load Server bound to: " + s);
    }

    public void run() {
        logger.debug("Drawing Load Server waiting for parameters...");
        while (true) {
            Socket client = null;
            try {
                client = s.accept();
                logger.debug("Parameter server accepted client.");
                BufferedReader in =
                    new BufferedReader(new InputStreamReader(client.getInputStream()));
                try {
                    String drawingFileName;
                    do {
                        drawingFileName = in.readLine();
                        if (drawingFileName != null) {
                            logger.debug("Received Parameter " + drawingFileName);
                            editor.loadAndOpenCommandLineDrawings(new String[] { drawingFileName });
                        }
                    } while (drawingFileName != null);
                } catch (IOException e) {
                }
                logger.debug("Connection closed.");
                in.close();
                client.close();
            } catch (Exception e) {
                logger.error("Drawing Load Server threw exception: " + e);
            }
        }
    }
}