/*
 * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.dmdirc.addons.dcc;

import com.dmdirc.Main;
import com.dmdirc.Server;
import com.dmdirc.actions.ActionManager;
import com.dmdirc.actions.CoreActionType;
import com.dmdirc.actions.interfaces.ActionType;
import com.dmdirc.addons.dcc.kde.KFileChooser;
import com.dmdirc.addons.dcc.actions.DCCActions;
import com.dmdirc.addons.ui_swing.MainFrame;
import com.dmdirc.addons.ui_swing.components.frames.TextFrame;
import com.dmdirc.addons.ui_swing.components.text.TextLabel;
import com.dmdirc.addons.ui_swing.dialogs.StandardQuestionDialog;
import com.dmdirc.commandparser.CommandManager;
import com.dmdirc.config.Identity;
import com.dmdirc.config.IdentityManager;
import com.dmdirc.config.prefs.PluginPreferencesCategory;
import com.dmdirc.config.prefs.PreferencesCategory;
import com.dmdirc.config.prefs.PreferencesManager;
import com.dmdirc.config.prefs.PreferencesSetting;
import com.dmdirc.config.prefs.PreferencesType;
import com.dmdirc.interfaces.ActionListener;
import com.dmdirc.logger.ErrorLevel;
import com.dmdirc.logger.Logger;
import com.dmdirc.parser.interfaces.ClientInfo;
import com.dmdirc.parser.interfaces.Parser;
import com.dmdirc.plugins.Plugin;
import com.dmdirc.ui.WindowManager;
import com.dmdirc.ui.interfaces.Window;

import java.awt.Dialog.ModalityType;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

/**
 * This plugin adds DCC to dmdirc.
 *
 * @author Shane 'Dataforce' McCormack
 */
public final class DCCPlugin extends Plugin implements ActionListener {

    /** The DCCCommand we created. */
    private DCCCommand command;

    /** Our DCC Container window. */
    private DCCFrame container;

    /** Child Frames. */
    private final List<DCCFrame> childFrames = new ArrayList<DCCFrame>();

    /**
     * Creates a new instance of the DCC Plugin.
     */
    public DCCPlugin() {
        super();
    }

    /**
     * Ask a question, if the answer is the answer required, then recall handleProcessEvent.
     *
     * @param question Question to ask
     * @param title Title of question dialog
     * @param desiredAnswer Answer required
     * @param type Actiontype to pass back
     * @param format StringBuffer to pass back
     * @param arguments arguments to pass back
     */
    public void askQuestion(final String question, final String title, final int desiredAnswer, final ActionType type, final StringBuffer format, final Object... arguments) {
        // New thread to ask the question in to stop us locking the UI
        final Thread questionThread = new Thread(new Runnable() {

            /** {@inheritDoc} */
            @Override
            public void run() {
                int result = JOptionPane.showConfirmDialog(null, question, title, JOptionPane.YES_NO_OPTION);
                if (result == desiredAnswer) {
                    handleProcessEvent(type, format, true, arguments);
                }
            }

        }, "QuestionThread: " + title);
        // Start the thread
        questionThread.start();
    }

    /**
     * Ask the location to save a file, then start the download.
     *
     * @param nickname Person this dcc is from.
     * @param send The DCCSend to save for.
     * @param parser The parser this send was received on
     * @param reverse Is this a reverse dcc?
     * @param sendFilename The name of the file which is being received
     * @param token Token used in reverse dcc.
     */
    public void saveFile(final String nickname, final DCCTransfer send, final Parser parser, final boolean reverse, final String sendFilename, final String token) {
        // New thread to ask the user where to save in to stop us locking the UI
        final Thread dccThread = new Thread(new Runnable() {

            /** {@inheritDoc} */
            @Override
            public void run() {
                final JFileChooser jc = KFileChooser.getFileChooser(DCCPlugin.this, IdentityManager.getGlobalConfig().getOption(getDomain(), "receive.savelocation"));
                jc.setDialogTitle("Save " + sendFilename + " As - DMDirc");
                jc.setFileSelectionMode(JFileChooser.FILES_ONLY);
                jc.setMultiSelectionEnabled(false);
                jc.setSelectedFile(new File(send.getFileName()));
                int result;
                if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
                    result = JFileChooser.APPROVE_OPTION;
                } else {
                    result = jc.showSaveDialog((JFrame) Main.getUI().getMainWindow());
                }
                if (result == JFileChooser.APPROVE_OPTION) {
                    send.setFileName(jc.getSelectedFile().getPath());
                    boolean resume = false;
                    if (jc.getSelectedFile().exists()) {
                        if (send.getFileSize() > -1 && send.getFileSize() <= jc.getSelectedFile().length()) {
                            if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
                                return;
                            } else {
                                JOptionPane.showMessageDialog((JFrame) Main.getUI().getMainWindow(), "This file has already been completed, or is longer than the file you are receiving.\nPlease choose a different file.", "Problem with selected file", JOptionPane.ERROR_MESSAGE);
                                saveFile(nickname, send, parser, reverse, sendFilename, token);
                                return;
                            }
                        } else {
                            if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
                                resume = true;
                            } else {
                                result = JOptionPane.showConfirmDialog((JFrame) Main.getUI().getMainWindow(), "This file exists already, do you want to resume an exisiting download?", "Resume Download?", JOptionPane.YES_NO_OPTION);
                                resume = (result == JOptionPane.YES_OPTION);
                            }
                        }
                    }
                    if (reverse && !token.isEmpty()) {
                        new DCCTransferWindow(DCCPlugin.this, send, "*Receive: " + nickname, nickname, null);
                        send.setToken(token);
                        if (resume) {
                            if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.reverse.sendtoken")) {
                                parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " 0 " + jc.getSelectedFile().length() + " " + token);
                            } else {
                                parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " 0 " + jc.getSelectedFile().length());
                            }
                        } else {
                            if (listen(send)) {
                                parser.sendCTCP(nickname, "DCC", "SEND " + sendFilename + " " + DCC.ipToLong(getListenIP(parser)) + " " + send.getPort() + " " + send.getFileSize() + " " + token);
                            } else {
                                // Listen failed.
                            }
                        }
                    } else {
                        new DCCTransferWindow(DCCPlugin.this, send, "Receive: " + nickname, nickname, null);
                        if (resume) {
                            parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " " + send.getPort() + " " + jc.getSelectedFile().length());
                        } else {
                            send.connect();
                        }
                    }
                }
            }

        }, "saveFileThread: " + sendFilename);
        // Start the thread
        dccThread.start();
    }

    /**
     * Process an event of the specified type.
     *
     * @param type The type of the event to process
     * @param format Format of messages that are about to be sent. (May be null)
     * @param arguments The arguments for the event
     */
    @Override
    public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
        handleProcessEvent(type, format, false, arguments);
    }

    /**
     * Make the given DCC start listening.
     * This will either call dcc.listen() or dcc.listen(startPort, endPort)
     * depending on config.
     *
     * @param dcc DCC to start listening.
     * @return True if Socket was opened.
     */
    protected boolean listen(final DCC dcc) {

        final boolean usePortRange = IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "firewall.ports.usePortRange");
        final int startPort = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "firewall.ports.startPort");
        final int endPort = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "firewall.ports.endPort");

        try {
            if (usePortRange) {
                dcc.listen(startPort, endPort);
            } else {
                dcc.listen();
            }
            return true;
        } catch (IOException ioe) {
            return false;
        }
    }

    /**
     * Process an event of the specified type.
     *
     * @param type The type of the event to process
     * @param format Format of messages that are about to be sent. (May be null)
     * @param dontAsk Don't ask any questions, assume yes.
     * @param arguments The arguments for the event
     */
    public void handleProcessEvent(final ActionType type, final StringBuffer format, final boolean dontAsk, final Object... arguments) {
        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept") && !dontAsk) {
            handleProcessEvent(type, format, true, arguments);
            return;
        }

        if (type == CoreActionType.SERVER_CTCP) {
            final String ctcpType = (String) arguments[2];
            final String[] ctcpData = ((String) arguments[3]).split(" ");
            if (ctcpType.equalsIgnoreCase("DCC")) {
                if (ctcpData[0].equalsIgnoreCase("chat") && ctcpData.length > 3) {
                    final String nickname = ((ClientInfo) arguments[1]).getNickname();
                    if (dontAsk) {
                        final DCCChat chat = new DCCChat();
                        try {
                            chat.setAddress(Long.parseLong(ctcpData[2]), Integer.parseInt(ctcpData[3]));
                        } catch (NumberFormatException nfe) {
                            return;
                        }
                        final String myNickname = ((Server) arguments[0]).getParser().getLocalClient().getNickname();
                        final DCCFrame f = new DCCChatWindow(this, chat, "Chat: " + nickname, myNickname, nickname);
                        f.getFrame().addLine("DCCChatStarting", nickname, chat.getHost(), chat.getPort());
                        chat.connect();
                    } else {
                        ActionManager.processEvent(DCCActions.DCC_CHAT_REQUEST, null, ((Server) arguments[0]), nickname);
                        askQuestion("User " + nickname + " on " + ((Server) arguments[0]).toString() + " would like to start a DCC Chat with you.\n\nDo you want to continue?", "DCC Chat Request", JOptionPane.YES_OPTION, type, format, arguments);
                        return;
                    }
                } else if (ctcpData[0].equalsIgnoreCase("send") && ctcpData.length > 3) {
                    final String nickname = ((ClientInfo) arguments[1]).getNickname();
                    final String filename;
                    String tmpFilename;
                    // Clients tend to put files with spaces in the name in "" so lets look for that.
                    final StringBuilder filenameBits = new StringBuilder();
                    int i;
                    final boolean quoted = ctcpData[1].startsWith("\"");
                    if (quoted) {
                        for (i = 1; i < ctcpData.length; i++) {
                            String bit = ctcpData[i];
                            if (i == 1) {
                                bit = bit.substring(1);
                            }
                            if (bit.endsWith("\"")) {
                                filenameBits.append(" " + bit.substring(0, bit.length() - 1));
                                break;
                            } else {
                                filenameBits.append(" " + bit);
                            }
                        }
                        tmpFilename = filenameBits.toString().trim();
                    } else {
                        tmpFilename = ctcpData[1];
                        i = 1;
                    }

                    // Try to remove path names if sent.
                    // Change file separatorChar from other OSs first
                    if (File.separatorChar == '/') {
                        tmpFilename = tmpFilename.replace('\\', File.separatorChar);
                    } else {
                        tmpFilename = tmpFilename.replace('/', File.separatorChar);
                    }
                    // Then get just the name of the file.
                    filename = (new File(tmpFilename)).getName();

                    final String ip = ctcpData[++i];
                    final String port = ctcpData[++i];
                    long size;
                    if (ctcpData.length + 1 > i) {
                        try {
                            size = Integer.parseInt(ctcpData[++i]);
                        } catch (NumberFormatException nfe) {
                            size = -1;
                        }
                    } else {
                        size = -1;
                    }
                    final String token = (ctcpData.length - 1 > i && !ctcpData[i + 1].equals("T")) ? ctcpData[++i] : "";

                    // Ignore incorrect ports, or non-numeric IP/Port
                    try {
                        int portInt = Integer.parseInt(port);
                        if (portInt > 65535 || portInt < 0) {
                            return;
                        }
                        Long.parseLong(ip);
                    } catch (NumberFormatException nfe) {
                        return;
                    }

                    DCCTransfer send = DCCTransfer.findByToken(token);

                    if (send == null && !dontAsk) {
                        if (!token.isEmpty() && !port.equals("0")) {
                            // This is a reverse DCC Send that we no longer care about.
                            return;
                        } else {
                            ActionManager.processEvent(DCCActions.DCC_SEND_REQUEST, null, ((Server) arguments[0]), nickname, filename);
                            askQuestion("User " + nickname + " on " + ((Server) arguments[0]).toString() + " would like to send you a file over DCC.\n\nFile: " + filename + "\n\nDo you want to continue?", "DCC Send Request", JOptionPane.YES_OPTION, type, format, arguments);
                            return;
                        }
                    } else {
                        final boolean newSend = send == null;
                        if (newSend) {
                            send = new DCCTransfer(IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "send.blocksize"));
                            send.setTurbo(IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "send.forceturbo"));
                        }
                        try {
                            send.setAddress(Long.parseLong(ip), Integer.parseInt(port));
                        } catch (NumberFormatException nfe) {
                            return;
                        }
                        if (newSend) {
                            send.setFileName(filename);
                            send.setFileSize(size);
                            saveFile(nickname, send, ((Server) arguments[0]).getParser(), "0".equals(port), (quoted) ? "\"" + filename + "\"" : filename, token);
                        } else {
                            send.connect();
                        }
                    }
                } else if ((ctcpData[0].equalsIgnoreCase("resume") || ctcpData[0].equalsIgnoreCase("accept")) && ctcpData.length > 2) {

                    final String filename;
                    // Clients tend to put files with spaces in the name in "" so lets look for that.
                    final StringBuilder filenameBits = new StringBuilder();
                    int i;
                    final boolean quoted = ctcpData[1].startsWith("\"");
                    if (quoted) {
                        for (i = 1; i < ctcpData.length; i++) {
                            String bit = ctcpData[i];
                            if (i == 1) {
                                bit = bit.substring(1);
                            }
                            if (bit.endsWith("\"")) {
                                filenameBits.append(" " + bit.substring(0, bit.length() - 1));
                                break;
                            } else {
                                filenameBits.append(" " + bit);
                            }
                        }
                        filename = filenameBits.toString().trim();
                    } else {
                        filename = ctcpData[1];
                        i = 1;
                    }

                    try {
                        final int port = Integer.parseInt(ctcpData[++i]);
                        final int position = Integer.parseInt(ctcpData[++i]);
                        final String token = (ctcpData.length - 1 > i) ? " " + ctcpData[++i] : "";

                        // Now look for a dcc that matches.
                        for (DCCTransfer send : DCCTransfer.getTransfers()) {
                            if (send.port == port && (new File(send.getFileName())).getName().equalsIgnoreCase(filename)) {
                                if ((!token.isEmpty() && !send.getToken().isEmpty()) && (!token.equals(send.getToken()))) {
                                    continue;
                                }
                                final Parser parser = ((Server) arguments[0]).getParser();
                                final String nickname = ((ClientInfo) arguments[1]).getNickname();
                                if (ctcpData[0].equalsIgnoreCase("resume")) {
                                    parser.sendCTCP(nickname, "DCC", "ACCEPT " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + port + " " + send.setFileStart(position) + token);
                                } else {
                                    send.setFileStart(position);
                                    if (port == 0) {
                                        // Reverse dcc
                                        if (listen(send)) {
                                            if (send.getToken().isEmpty()) {
                                                parser.sendCTCP(nickname, "DCC", "SEND " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + DCC.ipToLong(send.getHost()) + " " + send.getPort() + " " + send.getFileSize());
                                            } else {
                                                parser.sendCTCP(nickname, "DCC", "SEND " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + DCC.ipToLong(send.getHost()) + " " + send.getPort() + " " + send.getFileSize() + " " + send.getToken());
                                            }
                                        } else {
                                            // Listen failed.
                                        }
                                    } else {
                                        send.connect();
                                    }
                                }
                            }
                        }
                    } catch (NumberFormatException nfe) {
                    }
                }
            }
        }
    }

    /**
     * Create the container window.
     */
    protected void createContainer() {
        container = new PlaceholderDCCFrame(this);
        final TextLabel label = new TextLabel("This is a placeholder window to group DCCs together.");
        label.setText(label.getText() + "\n\nClosing this window will close all the active DCCs");
        ((TextFrame) container.getFrame()).getContentPane().add(label);
        WindowManager.addWindow(container.getFrame());
        container.getFrame().open();
    }

    /**
     * Add a window to the container window.
     *
     * @param window Window to remove
     */
    protected synchronized void addWindow(final DCCFrame window) {
        if (window == container) {
            return;
        }
        if (container == null) {
            createContainer();
        }

        WindowManager.addWindow(container.getFrame(), window.getFrame());
        childFrames.add(window);
        window.getFrame().open();
    }

    /**
     * Remove a window from the container window.
     *
     * @param window Window to remove
     */
    protected synchronized void delWindow(final DCCFrame window) {
        if (container == null) {
            return;
        }
        if (window == container) {
            container = null;
            for (DCCFrame win : childFrames) {
                if (win != window) {
                    win.close();
                }
            }
            childFrames.clear();
        } else {
            childFrames.remove(window);
            if (childFrames.isEmpty()) {
                container.close();
                container = null;
            }
        }
    }

    /** {@inheritDoc} */
    @Override
    public void domainUpdated() {
        final Identity defaults = IdentityManager.getAddonIdentity();

        defaults.setOption(getDomain(), "receive.savelocation",
                Main.getConfigDir() + "downloads" + System.getProperty("file.separator"));
    }

    /**
     * Called when the plugin is loaded.
     */
    @Override
    public void onLoad() {
        final File dir = new File(IdentityManager.getGlobalConfig().getOption(getDomain(), "receive.savelocation"));
        if (dir.exists()) {
            if (!dir.isDirectory()) {
                Logger.userError(ErrorLevel.LOW, "Unable to create download dir (file exists instead)");
            }
        } else {
            try {
                dir.mkdirs();
                dir.createNewFile();
            } catch (IOException ex) {
                Logger.userError(ErrorLevel.LOW, "Unable to create download dir");
            }
        }

        command = new DCCCommand(this);
        ActionManager.registerActionTypes(DCCActions.values());
        ActionManager.addListener(this, CoreActionType.SERVER_CTCP);
    }

    /**
     * Called when this plugin is Unloaded.
     */
    @Override
    public synchronized void onUnload() {
        CommandManager.unregisterCommand(command);
        ActionManager.removeListener(this);
        if (container != null) {
            container.close();
        }
    }

    /**
     * Get the IP Address we should send as our listening IP.
     *
     * @return The IP Address we should send as our listening IP.
     */
    public String getListenIP() {
        return getListenIP(null);
    }

    /**
     * Get the IP Address we should send as our listening IP.
     *
     * @param parser Parser the IRC Parser where this dcc is initiated
     * @return The IP Address we should send as our listening IP.
     */
    public String getListenIP(final Parser parser) {
        final String configIP = IdentityManager.getGlobalConfig().getOption(getDomain(), "firewall.ip");
        if (!configIP.isEmpty()) {
            try {
                return InetAddress.getByName(configIP).getHostAddress();
            } catch (UnknownHostException ex) {
                //Fallthrough
            }
        }
        if (parser != null) {
            final String myHost = parser.getLocalClient().getHostname();
            if (!myHost.isEmpty()) {
                try {
                    return InetAddress.getByName(myHost).getHostAddress();
                } catch (UnknownHostException e) { /* Will return default host below */ }
            }
        }
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            // This is almost certainly not what we want, but we can't work out
            // the right one.
            return "127.0.0.1";
        }
    }

    /** {@inheritDoc} */
    @Override
    public void showConfig(final PreferencesManager manager) {
        final PreferencesCategory general = new PluginPreferencesCategory(getPluginInfo(), "DCC", "", "category-dcc");
        final PreferencesCategory firewall = new PluginPreferencesCategory(getPluginInfo(), "Firewall", "");
        final PreferencesCategory sending = new PluginPreferencesCategory(getPluginInfo(), "Sending", "");
        final PreferencesCategory receiving = new PluginPreferencesCategory(getPluginInfo(), "Receiving", "");

        manager.getCategory("Plugins").addSubCategory(general.setInlineAfter());
        general.addSubCategory(firewall.setInline());
        general.addSubCategory(sending.setInline());
        general.addSubCategory(receiving.setInline());

        firewall.addSetting(new PreferencesSetting(PreferencesType.TEXT,
                getDomain(), "firewall.ip", "Forced IP",
                "What IP should be sent as our IP (Blank = work it out)"));
        firewall.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
                getDomain(), "firewall.ports.usePortRange", "Use Port Range",
                "Useful if you have a firewall that only forwards specific ports"));
        firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
                getDomain(), "firewall.ports.startPort", "Start Port",
                "Port to try to listen on first"));
        firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
                getDomain(), "firewall.ports.endPort", "End Port",
                "Port to try to listen on last"));
        receiving.addSetting(new PreferencesSetting(PreferencesType.DIRECTORY,
                getDomain(), "receive.savelocation", "Default save location",
                "Where the save as window defaults to?"));
        sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
                getDomain(), "send.reverse", "Reverse DCC",
                "With reverse DCC, the sender connects rather than " +
                "listens like normal dcc"));
        sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
                getDomain(), "send.forceturbo", "Use Turbo DCC",
                "Turbo DCC doesn't wait for ack packets. this is " +
                "faster but not always supported."));
        receiving.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
                getDomain(), "receive.reverse.sendtoken",
                "Send token in reverse receive",
                "If you have problems with reverse dcc receive resume," +
                " try toggling this."));
        general.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
                getDomain(), "send.blocksize", "Blocksize to use for DCC",
                "Change the block size for send/receive, this can " +
                "sometimes speed up transfers."));
        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
                getDomain(), "general.percentageInTitle",
                "Show percentage of transfers in the window title",
                "Show the current percentage of transfers in the DCC window title"));
    }

}

/**
 * Creates a placeholder DCC Frame.
 */
class PlaceholderDCCFrame extends DCCFrame {

    /**
     * Creates a placeholder dcc frame.
     *
     * @param plugin Parent plugin
     */
    public PlaceholderDCCFrame(final DCCPlugin plugin) {
        super(plugin, "DCCs", "dcc");
    }

    @Override
    public void close() {
        final Window[] windows = WindowManager.getChildren(getFrame());
        int dccs = 0;
        for (Window window : windows) {
            if (window instanceof EmptyFrame) {
                if (((DCCTransferWindow)((EmptyFrame) window).getContainer()).getDCC().isActive()) {
                    dccs++;
                }
            } else if (window instanceof DCCChatWindow) {
                if (((DCCChatWindow)((EmptyFrame) window).getContainer()).getDCC().isActive()) {
                    dccs++;
                }
            }
        }

        if (dccs > 0) {
            new StandardQuestionDialog(
                    (MainFrame) Main.getUI().getMainWindow(),
                    ModalityType.MODELESS, "Close confirmation",
                    "Closing this window will cause all existing DCCs " +
                    "to terminate, are you sure you want to do this?") {
                /**
                 * A version number for this class. It should be changed whenever the class
                 * structure is changed (or anything else that would prevent serialized
                 * objects being unserialized with the new class).
                 */
                private static final long serialVersionUID = 1;

                /** {@inheritDoc} */
                @Override
                public boolean save() {
                    PlaceholderDCCFrame.super.close();
                    return true;
                }

                /** {@inheritDoc} */
                @Override
                public void cancelled() {
                }
            }.display();
        } else {
            super.close();
        }
    }
}

