package de.renew.gui.configure;

import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.rmi.RemoteException;
import javax.swing.JOptionPane;

import org.apache.log4j.Logger;

import de.renew.draw.ui.api.MenuApi;
import de.renew.gui.CPNApplication;
import de.renew.gui.configure.RemoteServerWindow.WeakNetInstanceWrapper;
import de.renew.remote.NetInstanceAccessor;
import de.renew.remote.RemoteServerRegistry;


/**
 * Displays and controls a window, where connections to remote servers can
 * be established, listed and terminated. The Swing part of this window is
 * defined in {@link RemoteServerWindow}.
 * @author Timo Carl
 * @since Renew 2.0
 * @see RemoteServerWindow
 **/
public class RemoteServerController {
    /**
     * The logger for this RemoteSeverController.
     */
    private static final org.apache.log4j.Logger logger =
        Logger.getLogger(RemoteServerController.class);

    /**
     * A reference to the application where net instance windows can be
     * opened.
     **/
    private final CPNApplication _editor;

    /**
     * The Swing dialog associated with this window controller.
     **/
    private final RemoteServerWindow _gui;

    /**
     * Creates a new <code>RemoteServerController</code> instance along
     * with its <code>RemoteServerWindow</code>. The window is not visible
     * until {@link #show} is called.
     *
     * @param editor the application where this window should belong to and
     *               where net instance windows can be opened.
     **/
    public RemoteServerController(CPNApplication editor) {
        _editor = editor;
        _gui = new RemoteServerWindow();
        _gui.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(final WindowEvent e) {
                MenuApi.unregisterWindowsMenuFrame(_gui);
            }
        });
        _gui.addDisconnectListener(e -> this.doDisconnect());
        _gui.addConnectListener(e -> this.doConnect());
        _gui.addServerSelectedListener(e -> this.doSelectServer());
        _gui.addOpenNetInstanceListener(this::openNetInstance);
    }

    /**
     * Displays the window and updates its contents.
     **/
    public void show() {
        updateView();
        _gui.setVisible(true);
        _gui.setState(Frame.NORMAL);
        _gui.toFront();
        MenuApi.registerWindowsMenuFrame(MenuApi.getWindowsCategoryNameTools(), _gui);
    }

    /**
     * Called when the user chooses a server from the list of active
     * connections. Updates the window contents.
     **/
    public void doSelectServer() {
        updateView();
    }

    /**
     * Called when the user wants to connect to a remote server.
     * Establishes a connection to the server specified by the user,
     * if possible.
     **/
    public void doConnect() {
        String name = _gui.getDialogName();
        String host = _gui.getDialogServer();
        RemoteServerRegistry.ServerDescriptor server = null;
        try {
            if (!"".equals(name) && !"".equals(host)) {
                server = RemoteServerRegistry.instance().connectServer(host, name);
            }
        } catch (Exception e) {
            logger.debug(e.getMessage(), e);
            JOptionPane.showMessageDialog(
                _gui,
                "An error occurred while connecting to remote server.\nReason:\n" + e
                    + "\nTry setting the property de.renew.remote.rmi-host-name to\n"
                    + "the network-address you want to use.",
                "RMI-Problem", JOptionPane.ERROR_MESSAGE);
        }
        _gui.disposeDialog();
        if (server != null) {
            // We need to update before choosing the new server entry
            // because the entry is not added to the combo box before.
            updateView();
            _gui.selectServer(server);
        }
        updateView();
    }

    /**
     * Called when the user wants to disconnect from the currently selected
     * server. Terminates the connection, if applicable.
     **/
    public void doDisconnect() {
        int index = _gui.getSelectedServerIndex();
        if (index >= 1) {
            // We need to reduce the index by one because the local
            // nets are included in the combo box at first position.           
            RemoteServerRegistry.instance().removeServer(index - 1);
            updateView();
        }
    }

    /**
     * Called when the user requests to display a remote net instance.
     * Opens an instance drawing based on the selected net instance.
     **/
    public void openNetInstance() {
        int index = _gui.getSelectedNetInstanceIndex();
        if (index >= 0) {
            Object netObj = _gui.getNetInstanceModelByIndex(index);
            NetInstanceAccessor net = null;
            if (netObj instanceof NetInstanceAccessor netInstanceAccessor) {
                net = netInstanceAccessor;
            } else if (netObj instanceof WeakNetInstanceWrapper wrapper) {
                net = wrapper.getInstance();
                if (net == null) {
                    // Does this mean that the weak reference has been discarded?
                    logger.debug("Refreshing net list due to null reference...");
                    updateView();
                }
            } else {
                assert false : "Unexpected object type in net instance list: " + netObj.getClass();
            }
            if (net != null) {
                _editor.openInstanceDrawing(net);
            }
        }
    }

    /**
     * Refreshes all information displayed in the window.
     **/
    private void updateView() {
        try {
            _gui.updateView();
        } catch (RemoteException e) {
            logger.debug(e.getMessage(), e);
            JOptionPane.showMessageDialog(
                _gui, "An error occurred while calling remote objects.\nReason:\n" + e,
                "RMI-Problem", JOptionPane.ERROR_MESSAGE);
        }
    }
}