package net.oni2.aeinstaller.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.GridLayout;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.html.CSS;

import net.oni2.SettingsManager;
import net.oni2.aeinstaller.AEInstaller2;
import net.oni2.aeinstaller.backend.ImageResizer;
import net.oni2.aeinstaller.backend.Paths;
import net.oni2.aeinstaller.backend.RuntimeOptions;
import net.oni2.aeinstaller.backend.SizeFormatter;
import net.oni2.aeinstaller.backend.oni.OniLauncher;
import net.oni2.aeinstaller.backend.oni.OniSplit;
import net.oni2.aeinstaller.backend.oni.management.Initializer;
import net.oni2.aeinstaller.backend.oni.management.InstallProgressListener;
import net.oni2.aeinstaller.backend.oni.management.Installer;
import net.oni2.aeinstaller.backend.oni.management.tools.ToolInstallationList;
import net.oni2.aeinstaller.backend.oni.management.tools.ToolsManager;
import net.oni2.aeinstaller.backend.packages.Package;
import net.oni2.aeinstaller.backend.packages.PackageManager;
import net.oni2.aeinstaller.backend.packages.Type;
import net.oni2.aeinstaller.gui.about.AboutDialog;
import net.oni2.aeinstaller.gui.corepackages.CorePackagesDialog;
import net.oni2.aeinstaller.gui.downloadwindow.Downloader;
import net.oni2.aeinstaller.gui.modtable.EApplyFilterTo;
import net.oni2.aeinstaller.gui.modtable.ModInstallSelectionListener;
import net.oni2.aeinstaller.gui.modtable.ModSelectionListener;
import net.oni2.aeinstaller.gui.modtable.ModTable;
import net.oni2.aeinstaller.gui.modtable.ModTable.ETableContentType;
import net.oni2.aeinstaller.gui.packageinfobox.PackageInfoBox;
import net.oni2.aeinstaller.gui.reporter.ReporterDialog;
import net.oni2.aeinstaller.gui.settings.SettingsDialog;
import net.oni2.aeinstaller.gui.toolmanager.ToolManager;
import net.oni2.moddepot.DepotManager;
import net.oni2.platformtools.PlatformInformation;
import net.oni2.platformtools.PlatformInformation.Platform;
import net.oni2.platformtools.applicationinvoker.ApplicationInvoker;
import net.oni2.platformtools.applicationinvoker.ERuntimeNotInstalledException;
import net.oni2.resourcebundle.UTF8ResourceBundleLoader;
import net.oni2.swingcomponents.HTMLLinkLabel;

import org.javabuilders.BuildResult;
import org.javabuilders.annotations.DoInBackground;
import org.javabuilders.event.BackgroundEvent;
import org.javabuilders.swing.SwingJavaBuilder;
import org.simplericity.macify.eawt.ApplicationEvent;
import org.simplericity.macify.eawt.ApplicationListener;

/**
 * @author Christian Illy
 */
public class MainWin extends JFrame implements ApplicationListener,
		ModInstallSelectionListener, ModSelectionListener, KeyEventDispatcher {
	private static final long serialVersionUID = -4027395051382659650L;

	private ResourceBundle bundle = UTF8ResourceBundleLoader
			.getBundle("net.oni2.aeinstaller.localization."
					+ getClass().getSimpleName());
	@SuppressWarnings("unused")
	private BuildResult result = SwingJavaBuilder.build(this, bundle);

	private JMenu mainMenu;
	private JMenu toolsMenu;
	private Vector<JMenuItem> toolsMenuItems = new Vector<JMenuItem>();

	private JSplitPane contents;

	private JComboBox cmbModTypes;
	private JRadioButton radAll;
	private JRadioButton radOnline;
	private JRadioButton radLocal;
	private JRadioButton radInstalled;
	private JTextField txtShowFilter;
	private JComboBox cmbShowFilterTo;
	private JScrollPane scrollMods;
	private ModTable tblMods;
	private JLabel lblSelectedModsVal;
	private JLabel lblDownloadSizeVal;

	private PackageInfoBox pkgInfo;

	private JButton btnInstall;

	private TreeSet<Package> execCoreUpdates = new TreeSet<Package>();
	private TreeSet<Package> execUpdates = null;

	private enum EInstallState {
		DONE,
		READY,
		ABORTED,
		OFFLINE,
		INCOMPATIBLE,
		CHECKING
	};

	private EInstallState installState = EInstallState.DONE;
	private TreeSet<Package> installMods = null;
	private TreeSet<Package> installDeps = null;

	/**
	 * Constructor of main window.
	 */
	public MainWin() {
		this.setTitle(SwingJavaBuilder.getConfig().getResource("appname")
				+ SwingJavaBuilder.getConfig().getResource("appversion"));

		tblMods = new ModTable(ETableContentType.MODS);
		tblMods.setVisible(false);
		scrollMods.setViewportView(tblMods);

		contents.setDividerLocation(SettingsManager.getInstance().get(
				"win_main_divloc", 700));
		contents.setResizeWeight(0.4);

		if (PlatformInformation.getPlatform() == Platform.MACOS) {
			mainMenu.setVisible(false);
		} else {
		}

		ToolTipManager.sharedInstance().setInitialDelay(250);

		getRootPane().setDefaultButton(btnInstall);
		lblSelectedModsVal.setText("0");
		lblDownloadSizeVal.setText(SizeFormatter.format(0, 2));
		radAll.setSelected(true);

		for (EApplyFilterTo f : EApplyFilterTo.values()) {
			cmbShowFilterTo.addItem(f);
		}
		txtShowFilter.addKeyListener(new KeyAdapter() {
			@Override
			public void keyReleased(KeyEvent e) {
				super.keyReleased(e);
				updateTableFilter();
			}
		});

		tblMods.addModSelectionListener(this);
		tblMods.addDownloadSizeListener(this);

		KeyboardFocusManager.getCurrentKeyboardFocusManager()
				.addKeyEventDispatcher(this);

		setSize(SettingsManager.getInstance().get("win_main_width", 1050),
				SettingsManager.getInstance().get("win_main_height", 600));
		setLocationRelativeTo(null);
	}

	private void initModTypeBox() {
		cmbModTypes.removeAllItems();

		TreeMap<String, Type> types = new TreeMap<String, Type>();
		for (Type t : PackageManager.getInstance().getTypesWithContent()) {
			types.put(t.getName(), t);
		}
		cmbModTypes.addItem("-All-");
		for (Type t : types.values()) {
			cmbModTypes.addItem(t);
		}
		cmbModTypes.setSelectedIndex(0);
	}

	private void exit() {
		dispose();
		System.exit(0);
	}

	private void saveLocalData() {
		SettingsManager.getInstance().put("win_main_divloc",
				contents.getDividerLocation());
		SettingsManager.getInstance().put("win_main_width", getWidth());
		SettingsManager.getInstance().put("win_main_height", getHeight());
		SettingsManager.getInstance().serializeToFile(
				Paths.getSettingsFilename());
	}

	@DoInBackground(progressMessage = "updateDepot.title", cancelable = false, indeterminateProgress = false)
	private void execDepotUpdate(final BackgroundEvent evt) {
		boolean hasUpdated = false;
		if (!RuntimeOptions.isOfflineMode()
				&& !RuntimeOptions.isNoCacheUpdateMode()) {
			long start = new Date().getTime();
			hasUpdated = DepotManager.getInstance().updateInformation();
			System.out.println("Took: " + (new Date().getTime() - start)
					+ " msec");
		}

		PackageManager.loadFromCacheFile(Paths.getPacManCacheFilename());

		if (hasUpdated || !Paths.getPacManCacheFilename().exists()) {
			PackageManager.getInstance().init();
			PackageManager.getInstance().saveToCacheFile(
					Paths.getPacManCacheFilename());
		}
		tblMods.reloadData();
		initModTypeBox();

		tblMods.setVisible(true);
	}

	@SuppressWarnings("unused")
	private void checkUpdates(Object evtSource) {
		if ((evtSource != this)
				|| SettingsManager.getInstance().get("notifyupdates", true)) {
			if (RuntimeOptions.isOfflineMode()) {
				if (evtSource != this) {
					JOptionPane.showMessageDialog(
							this,
							SwingJavaBuilder.getConfig().getResource(
									"offlineMode.text"),
							SwingJavaBuilder.getConfig().getResource(
									"offlineMode.title"),
							JOptionPane.WARNING_MESSAGE);
				}
			} else {
				TreeSet<Package> mods = PackageManager.getInstance()
						.getUpdatableMods();
				TreeSet<Package> tools = PackageManager.getInstance()
						.getUpdatableTools();
				JPanel panPackages = new JPanel(new GridLayout(0, 1));
				execUpdates = new TreeSet<Package>();
				execUpdates.addAll(mods);
				execUpdates.addAll(tools);
				final JLabel lblSize = new JLabel("<html>"
						+ String.format(
								bundle.getString("updatesAvailableSize.text"),
								SizeFormatter.format(0, 3)) + "</html>");
				int size = 0;
				for (final Package m : mods) {
					size += m.getZipSize();
					JCheckBox check = new JCheckBox("Mod: " + m.getName()
							+ " (" + SizeFormatter.format(m.getZipSize(), 1)
							+ ")");
					check.setSelected(true);
					check.addItemListener(new ItemListener() {
						@Override
						public void itemStateChanged(ItemEvent e) {
							if (e.getStateChange() == ItemEvent.SELECTED)
								execUpdates.add(m);
							else
								execUpdates.remove(m);
							int s = 0;
							for (Package p : execUpdates)
								s += p.getZipSize();
							lblSize.setText("<html>"
									+ String.format(
											bundle.getString("updatesAvailableSize.text"),
											SizeFormatter.format(s, 3))
									+ "</html>");
						}
					});
					panPackages.add(check);
				}
				for (final Package m : tools) {
					size += m.getZipSize();
					JCheckBox check = new JCheckBox("Tool: " + m.getName()
							+ " (" + SizeFormatter.format(m.getZipSize(), 1)
							+ ")");
					check.setSelected(true);
					check.addItemListener(new ItemListener() {
						@Override
						public void itemStateChanged(ItemEvent e) {
							if (e.getStateChange() == ItemEvent.SELECTED)
								execUpdates.add(m);
							else
								execUpdates.remove(m);
							int s = 0;
							for (Package p : execUpdates)
								s += p.getZipSize();
							lblSize.setText("<html>"
									+ String.format(
											bundle.getString("updatesAvailableSize.text"),
											SizeFormatter.format(s, 3))
									+ "</html>");
						}
					});
					panPackages.add(check);
				}
				lblSize.setText("<html>"
						+ String.format(
								bundle.getString("updatesAvailableSize.text"),
								SizeFormatter.format(size, 3)) + "</html>");
				if (size > 0) {
					// Build info dialog content
					JPanel packages = new JPanel(new BorderLayout(0, 7));
					JLabel lblIntro = new JLabel("<html>"
							+ bundle.getString("updatesAvailable.text")
							+ "</html>");
					packages.add(lblIntro, BorderLayout.NORTH);
					packages.add(panPackages, BorderLayout.CENTER);
					packages.add(lblSize, BorderLayout.SOUTH);

					JPanel pan = new JPanel(new BorderLayout(0, 25));
					pan.add(packages, BorderLayout.CENTER);
					JCheckBox checkFutureUpdates = new JCheckBox(
							bundle.getString("checkOnStartup.text"));
					checkFutureUpdates.setSelected(SettingsManager
							.getInstance().get("notifyupdates", true));
					checkFutureUpdates.addItemListener(new ItemListener() {
						@Override
						public void itemStateChanged(ItemEvent evt) {
							SettingsManager.getInstance().put("notifyupdates",
									evt.getStateChange() == ItemEvent.SELECTED);
						}
					});
					pan.add(checkFutureUpdates, BorderLayout.SOUTH);

					// Show dialog
					int res = JOptionPane.showConfirmDialog(this, pan,
							bundle.getString("updatesAvailable.title"),
							JOptionPane.YES_NO_OPTION,
							JOptionPane.QUESTION_MESSAGE);
					if (res == JOptionPane.NO_OPTION) {
						execUpdates = null;
					}
				} else {
					if (evtSource != this) {
						JOptionPane.showMessageDialog(this,
								bundle.getString("updatesNotAvailable.text"),
								bundle.getString("updatesNotAvailable.title"),
								JOptionPane.INFORMATION_MESSAGE);
					}
				}
			}
		}
	}

	@SuppressWarnings("unused")
	private void doUpdate() {
		if (execUpdates != null && execUpdates.size() > 0) {
			Downloader dl = new Downloader(execUpdates, null, false);
			try {
				dl.setVisible(true);
				if (dl.isFinished()) {
					ToolInstallationList til = ToolInstallationList
							.getInstance();
					TreeSet<Package> tools = new TreeSet<Package>();
					for (Package m : execUpdates)
						if (m.isTool() && til.isInstalled(m.getPackageNumber()))
							tools.add(m);
					if (tools.size() > 0) {
						ToolsManager.installTools(tools, true);
						ToolsManager.installTools(tools, false);
					}
				}
			} finally {
				dl.dispose();
			}
		}
		execUpdates = null;
	}

	@SuppressWarnings("unused")
	private void showNewPackages() {
		PackageManager pm = PackageManager.getInstance();
		if ((pm.getNewModsOnDepot().size() >= pm.getModsValidAndNotCore()
				.size())
				|| (pm.getNewToolsOnDepot().size() >= pm.getTools().size()))
			return;
		if (SettingsManager.getInstance().get("notifynewpackages", true)) {
			if (pm.getNewModsOnDepot().size() > 0
					|| pm.getNewToolsOnDepot().size() > 0) {
				TreeSet<Package> mods = new TreeSet<Package>(pm
						.getNewModsOnDepot().values());
				TreeSet<Package> tools = new TreeSet<Package>(pm
						.getNewToolsOnDepot().values());
				StringBuffer modsString = new StringBuffer();
				StringBuffer toolsString = new StringBuffer();
				for (final Package m : mods) {
					modsString.append("<li>" + m.getName() + "</li>");
				}
				for (final Package m : tools) {
					toolsString.append("<li>" + m.getName() + "</li>");
				}
				// Build info dialog content
				String message = "<html>"
						+ bundle.getString("newPackages.text") + "</html>";
				if (modsString.length() > 0) {
					modsString.insert(0, "Mods:<ul>");
					modsString.append("</ul>");
					message = message.replaceAll("%1", modsString.toString());
				} else {
					message = message.replaceAll("%1", "");
				}
				if (toolsString.length() > 0) {
					toolsString.insert(0, "Tools:<ul>");
					toolsString.append("</ul>");
					message = message.replaceAll("%2", toolsString.toString());
				} else {
					message = message.replaceAll("%2", "");
				}

				JPanel pan = new JPanel(new BorderLayout(0, 25));
				JLabel lblTxt = new JLabel(message);
				pan.add(lblTxt, BorderLayout.CENTER);
				JCheckBox checkFutureUpdates = new JCheckBox(
						bundle.getString("checkNewPackagesOnStartup.text"));
				checkFutureUpdates.setSelected(SettingsManager.getInstance()
						.get("notifynewpackages", true));
				checkFutureUpdates.addItemListener(new ItemListener() {
					@Override
					public void itemStateChanged(ItemEvent evt) {
						SettingsManager.getInstance().put("notifynewpackages",
								evt.getStateChange() == ItemEvent.SELECTED);
					}
				});
				pan.add(checkFutureUpdates, BorderLayout.SOUTH);

				JOptionPane.showMessageDialog(this, pan,
						bundle.getString("newPackages.title"),
						JOptionPane.INFORMATION_MESSAGE);
			}
		}
	}

	@SuppressWarnings("unused")
	private void showTips() {
		if (SettingsManager.getInstance().get("showTips", true)) {
			HTMLLinkLabel text = new HTMLLinkLabel();
			text.setCssAttribute("h2", CSS.Attribute.MARGIN_TOP, "0");
			text.setCssAttribute("h3", CSS.Attribute.MARGIN_TOP, "0");
			text.setCssAttribute("ul", CSS.Attribute.MARGIN_TOP, "0");
			text.setCssAttribute("ul", CSS.Attribute.MARGIN_LEFT, "20");
			text.setCssAttribute("ul li", CSS.Attribute.MARGIN_TOP, "10");
			text.setCssAttribute("ul li", CSS.Attribute.PADDING_LEFT, "10");
			text.setText(bundle.getString("startupTips.text"));
			JOptionPane.showMessageDialog(this, text,
					bundle.getString("startupTips.title"),
					JOptionPane.INFORMATION_MESSAGE);
		}
	}

	@SuppressWarnings("unused")
	private void focus() {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				toFront();
				repaint();
			}
		});

	}

	@SuppressWarnings("unused")
	private void showCorePackagesDialog() {
		new CorePackagesDialog().setVisible(true);
	}

	private void showSettings() {
		new SettingsDialog().setVisible(true);
	}

	private void showAbout() {
		new AboutDialog().setVisible(true);
	}

	@SuppressWarnings("unused")
	private void showHelp() {
		try {
			Desktop.getDesktop().browse(new URI("http://wiki.oni2.net/AEI"));
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("unused")
	private void showReport() {
		new ReporterDialog().setVisible(true);
	}

	private JFileChooser getConfigOpenSaveDialog(boolean save) {
		JFileChooser fc = new JFileChooser();
		fc.setCurrentDirectory(Paths.getEditionBasePath());
		if (save)
			fc.setDialogType(JFileChooser.SAVE_DIALOG);
		else
			fc.setDialogType(JFileChooser.OPEN_DIALOG);
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fc.setFileFilter(new FileFilter() {
			@Override
			public String getDescription() {
				return "XML files";
			}

			@Override
			public boolean accept(File arg0) {
				return (arg0.isDirectory())
						|| (arg0.getName().toLowerCase().endsWith(".xml"));
			}
		});
		fc.setMultiSelectionEnabled(false);
		return fc;
	}

	@SuppressWarnings("unused")
	private void loadConfig() {
		JFileChooser fc = getConfigOpenSaveDialog(false);
		int res = fc.showOpenDialog(this);
		if (res == JFileChooser.APPROVE_OPTION) {
			if (fc.getSelectedFile().exists())
				tblMods.reloadSelection(fc.getSelectedFile());
		}
	}

	@SuppressWarnings("unused")
	private void saveConfig() {
		JFileChooser fc = getConfigOpenSaveDialog(true);
		int res = fc.showSaveDialog(this);
		if (res == JFileChooser.APPROVE_OPTION) {
			File f = fc.getSelectedFile();
			if (!f.getName().endsWith(".xml"))
				f = new File(f.getParentFile(), f.getName() + ".xml");
			PackageManager.getInstance().saveModSelection(f,
					tblMods.getSelectedMods());
		}
	}

	@SuppressWarnings("unused")
	private void copyConfig() {
		StringBuffer b = new StringBuffer();
		b.append("[code]");
		for (Package p : PackageManager.getInstance().getInstalledMods())
			b.append(String.format("%s %s%n", p.getPackageNumberString(),
					p.getName()));
		b.append("[/code]");

		StringSelection selection = new StringSelection(b.toString());
		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
		clipboard.setContents(selection, selection);
	}

	@SuppressWarnings("unused")
	private boolean reglobalizeVerify() {
		int res = JOptionPane.showConfirmDialog(this,
				bundle.getString("rebuildCore.text"),
				bundle.getString("rebuildCore.title"),
				JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
		return res == JOptionPane.YES_OPTION;
	}

	@DoInBackground(progressMessage = "initializingEdition.title", cancelable = false, indeterminateProgress = false)
	private void reglobalize(final BackgroundEvent evt) {
		Initializer.initializeEdition(new InstallProgressListener() {
			@Override
			public void installProgressUpdate(int done, int total, String step) {
				evt.setProgressEnd(total);
				evt.setProgressValue(done);
				evt.setProgressMessage(step);
			}
		});
	}

	private void refreshLocalMods() {
		PackageManager.getInstance().updateLocalData();
		tblMods.reloadData();
		initModTypeBox();
	}

	@SuppressWarnings("unused")
	private void tools() {
		new ToolManager().setVisible(true);
	}

	@SuppressWarnings("unused")
	private void refreshToolsMenu() {
		for (JMenuItem i : toolsMenuItems) {
			toolsMenu.remove(i);
		}
		toolsMenuItems.clear();
		for (final Package m : PackageManager.getInstance().getInstalledTools()) {
			File exe = m.getExeFile();
			if (exe != null && exe.exists()) {
				JMenuItem item = new JMenuItem();
				ImageIcon ico = null;
				if (m.getIconFile() != null && m.getIconFile().exists()) {
					ico = new ImageIcon(m.getIconFile().getPath());
				} else {
					URL icon = AEInstaller2.class
							.getResource("images/transparent.png");
					ico = new ImageIcon(icon);
				}
				ico = ImageResizer.resizeImage(ico, 32, 32);
				item.setAction(new AbstractAction(m.getName(), ico) {
					private static final long serialVersionUID = 1L;

					@Override
					public void actionPerformed(ActionEvent evt) {
						try {
							ApplicationInvoker.execute(m.getExeType(),
									m.getWorkingDir(), m.getExeFile(), null,
									false);
						} catch (ERuntimeNotInstalledException e) {
							JOptionPane.showMessageDialog(null,
									bundle.getString("exeNotFound.text"),
									bundle.getString("exeNotFound.title"),
									JOptionPane.ERROR_MESSAGE);
							e.printStackTrace();
						} catch (FileNotFoundException e) {
							if (e.getMessage().contains("JRE"))
								JOptionPane.showMessageDialog(null,
										bundle.getString("jreNotFound.text"),
										bundle.getString("jreNotFound.title"),
										JOptionPane.ERROR_MESSAGE);
							if (e.getMessage().contains(".NET"))
								JOptionPane.showMessageDialog(
										null,
										bundle.getString("dotNetNotFound.text"),
										bundle.getString("dotNetNotFound.title"),
										JOptionPane.ERROR_MESSAGE);
							if (e.getMessage().contains("Wine"))
								JOptionPane.showMessageDialog(null,
										bundle.getString("wineNotFound.text"),
										bundle.getString("wineNotFound.title"),
										JOptionPane.ERROR_MESSAGE);
							e.printStackTrace();
						}
					}
				});
				toolsMenuItems.add(item);
				toolsMenu.add(item);
			}
		}
	}

	private void revertSelection() {
		tblMods.revertSelection();
	}

	@SuppressWarnings("unused")
	private void unSelectAll() {
		tblMods.unSelectAll();
	}

	@SuppressWarnings("unused")
	private void checkCorePackages() {
		if (!RuntimeOptions.isOfflineMode()) {
			TreeSet<Package> tools = new TreeSet<Package>();
			for (Package m : PackageManager.getInstance().getCoreTools()) {
				if (m.isNewerAvailable()) {
					execCoreUpdates.add(m);
					tools.add(m);
				} else if (!m.isInstalled())
					tools.add(m);
			}
			for (Package m : PackageManager.getInstance().getCoreMods()) {
				if (m.isNewerAvailable()) {
					execCoreUpdates.add(m);
				}
			}
			if (execCoreUpdates.size() > 0) {
				Downloader dl = new Downloader(execCoreUpdates, null, true);
				try {
					dl.setVisible(true);
				} finally {
					dl.dispose();
				}
			}

			ToolsManager.installTools(tools, true);
			ToolsManager.installTools(tools, false);
		}
		ToolsManager.verifyToolsIntegrity();
		if (ToolInstallationList.getInstance().getModifiedTools().size() > 0)
			System.out.println("Locally modified tools: "
					+ ToolInstallationList.getInstance().getModifiedTools()
							.toString());
	}

	@SuppressWarnings("unused")
	private void infoCorePackages() {
		if (execCoreUpdates.size() > 0) {
			String packages = "";
			for (Package m : execCoreUpdates) {
				packages += String.format("\n - %s (%s)", m.getName(),
						m.getVersion());
			}
			JOptionPane.showMessageDialog(this, String.format(
					bundle.getString("corePackagesUpdated.text"), packages),
					bundle.getString("corePackagesUpdated.title"),
					JOptionPane.INFORMATION_MESSAGE);
		}
	}

	@SuppressWarnings("unused")
	private void install() {
		TreeSet<Package> mods = new TreeSet<Package>();
		mods.addAll(PackageManager.getInstance().getCoreMods());
		mods.addAll(tblMods.getSelectedMods());

		installDeps = new TreeSet<Package>();

		installState = EInstallState.CHECKING;

		while (installState == EInstallState.CHECKING) {
			TreeSet<Package> toDownload = new TreeSet<Package>();
			for (Package m : mods) {
				if (!m.isLocalAvailable())
					toDownload.add(m);
			}

			if (toDownload.size() > 0 && RuntimeOptions.isOfflineMode()) {
				installState = EInstallState.OFFLINE;
				break;
			}

			if (toDownload.size() > 0) {
				Downloader dl = new Downloader(toDownload, installDeps, false);
				try {
					dl.setVisible(true);
					if (!dl.isFinished()) {
						installState = EInstallState.ABORTED;
						break;
					}
				} finally {
					dl.dispose();
				}
			}

			HashMap<Package, HashSet<Package>> dependencies = PackageManager
					.getInstance().checkDependencies(mods);
			if (dependencies.size() > 0) {
				for (HashSet<Package> hm : dependencies.values()) {
					installDeps.addAll(hm);
				}

				int size = 0;
				String depsLocalString = "";
				String depsDownloadString = "";
				for (Package m : dependencies.keySet()) {
					for (Package mDep : dependencies.get(m)) {
						if (!mods.contains(mDep)) {
							mods.add(mDep);
							if (!mDep.isLocalAvailable()) {
								size += mDep.getZipSize();
								if (depsDownloadString.length() > 0)
									depsDownloadString += "\n";
								depsDownloadString += " - " + mDep.getName();
							} else {
								if (depsLocalString.length() > 0)
									depsLocalString += "\n";
								depsLocalString += " - " + mDep.getName();
							}
						}
					}
				}

				if (depsLocalString.length() == 0)
					depsLocalString = bundle
							.getString("installDependencies.none");
				if (depsDownloadString.length() == 0)
					depsDownloadString = bundle
							.getString("installDependencies.none");

				if (!SettingsManager.getInstance().get(
						"notifyDepsAfterInstall", false)) {
					int res = JOptionPane.showConfirmDialog(this, String
							.format(bundle
									.getString("installDependencies.text"),
									depsLocalString, depsDownloadString,
									SizeFormatter.format(size, 3)), bundle
							.getString("installDependencies.title"),
							JOptionPane.YES_NO_OPTION,
							JOptionPane.INFORMATION_MESSAGE);

					if (res == JOptionPane.NO_OPTION) {
						installState = EInstallState.ABORTED;
						break;
					}
				}
			} else {
				HashMap<Package, HashSet<Package>> incompatibilities = PackageManager
						.getInstance().checkIncompabitilites(mods);
				if (incompatibilities.size() > 0) {
					installState = EInstallState.INCOMPATIBLE;

					String incompatString = "";
					for (Package m : incompatibilities.keySet()) {
						if (incompatString.length() > 0)
							incompatString += "\n";
						incompatString += m.getName() + ": ";
						String confMods = "";
						for (Package mConf : incompatibilities.get(m)) {
							if (confMods.length() > 0)
								confMods += ", ";
							confMods += mConf.getName();
						}
						incompatString += confMods;
					}

					JOptionPane.showMessageDialog(this, String.format(
							bundle.getString("installIncompatibilities.text"),
							incompatString), bundle
							.getString("installIncompatibilities.title"),
							JOptionPane.ERROR_MESSAGE);
					break;
				} else {
					installState = EInstallState.READY;
				}
			}
		}

		if (installState == EInstallState.READY) {
			installMods = new TreeSet<Package>();
			TreeSet<Package> actuallyTools = new TreeSet<Package>();

			for (Package m : mods) {
				if (m.isTool())
					actuallyTools.add(m);
				else
					installMods.add(m);
			}

			if (actuallyTools.size() > 0) {
				ToolsManager.installTools(actuallyTools, false);
			}
		}
	}

	@DoInBackground(progressMessage = "installing.title", cancelable = false, indeterminateProgress = false)
	private void installExec(final BackgroundEvent evt) {
		if (installState == EInstallState.READY) {
			Installer.install(installMods, new InstallProgressListener() {
				@Override
				public void installProgressUpdate(int done, int total,
						String step) {
					evt.setProgressEnd(total);
					evt.setProgressValue(done);
					evt.setProgressMessage(step);
				}
			});
			installState = EInstallState.DONE;
		}
		installMods = null;
	}

	@SuppressWarnings("unused")
	private void installDone() {
		switch (installState) {
			case DONE:
				revertSelection();
				if (installDeps.size() > 0
						&& SettingsManager.getInstance().get(
								"notifyDepsAfterInstall", false)) {
					String installedDeps = "";
					for (Package m : installDeps) {
						if (installedDeps.length() > 0)
							installedDeps += "\n";
						installedDeps += " - " + m.getName();
					}
					JOptionPane.showMessageDialog(this, String.format(
							bundle.getString("installDoneDeps.text"),
							installedDeps), bundle
							.getString("installDone.title"),
							JOptionPane.INFORMATION_MESSAGE);
				} else {
					JOptionPane.showMessageDialog(this,
							bundle.getString("installDone.text"),
							bundle.getString("installDone.title"),
							JOptionPane.INFORMATION_MESSAGE);
				}
				break;
			case OFFLINE:
				JOptionPane.showMessageDialog(
						this,
						SwingJavaBuilder.getConfig().getResource(
								"offlineMode.text"),
						SwingJavaBuilder.getConfig().getResource(
								"offlineMode.title"),
						JOptionPane.WARNING_MESSAGE);
				break;
			default:
				break;
		}
		installDeps = null;
	}

	@Override
	public void modSelectionChanged(ModTable source, Package m) {
		pkgInfo.updateInfo(m);
	}

	@SuppressWarnings("unused")
	private void clearFilter() {
		txtShowFilter.setText("");
		updateTableFilter();
	}

	private void updateTableFilter() {
		Object o = cmbModTypes.getSelectedItem();
		Type t = null;
		if (o instanceof Type)
			t = (Type) o;
		int downloadState = 0;
		if (radOnline.isSelected())
			downloadState = 1;
		if (radLocal.isSelected())
			downloadState = 2;
		if (radInstalled.isSelected())
			downloadState = 3;
		tblMods.setFilter(t, downloadState, txtShowFilter.getText(),
				(EApplyFilterTo) cmbShowFilterTo.getSelectedItem());
	}

	@Override
	public void modInstallSelectionChanged(int newSize, int newCount) {
		lblSelectedModsVal.setText(String.valueOf(newCount));
		lblDownloadSizeVal.setText(SizeFormatter.format(newSize, 2));
	}

	@SuppressWarnings("unused")
	private void checkInitialize() {
		if (!Installer.isEditionInitialized()) {
			if (!OniSplit.isOniSplitInstalled()) {
				JOptionPane.showMessageDialog(this,
						bundle.getString("noOniSplit.text"),
						bundle.getString("noOniSplit.title"),
						JOptionPane.ERROR_MESSAGE);
				exit();
			} else {
				int res = JOptionPane
						.showConfirmDialog(this,
								bundle.getString("askInitialize.text"),
								bundle.getString("askInitialize.title"),
								JOptionPane.YES_NO_OPTION,
								JOptionPane.QUESTION_MESSAGE);
				if (res == JOptionPane.NO_OPTION) {
					saveLocalData();
					exit();
				}
			}
		}
	}

	@DoInBackground(progressMessage = "initializingEdition.title", cancelable = false, indeterminateProgress = false)
	private void initialize(final BackgroundEvent evt) {
		if (!Installer.isEditionInitialized()) {
			Initializer.initializeEdition(new InstallProgressListener() {
				@Override
				public void installProgressUpdate(int done, int total,
						String step) {
					evt.setProgressEnd(total);
					evt.setProgressValue(done);
					evt.setProgressMessage(step);
				}
			});
		}
	}

	private void oni(boolean windowed) {
		if (!Paths.getEditionGDF().isDirectory()) {
			JOptionPane.showMessageDialog(this,
					bundle.getString("notInstalled.text"),
					bundle.getString("notInstalled.title"),
					JOptionPane.WARNING_MESSAGE);
		} else {
			try {
				OniLauncher.launch(windowed);
			} catch (FileNotFoundException e) {
				JOptionPane.showMessageDialog(this,
						bundle.getString("oniExeNotFound.text"),
						bundle.getString("oniExeNotFound.title"),
						JOptionPane.ERROR_MESSAGE);
				e.printStackTrace();
			} catch (ERuntimeNotInstalledException e) {
				JOptionPane.showMessageDialog(this,
						bundle.getString("wineNotFound.text"),
						bundle.getString("wineNotFound.title"),
						JOptionPane.ERROR_MESSAGE);
				e.printStackTrace();
			}
		}
	}

	@SuppressWarnings("unused")
	private void oniFull() {
		oni(false);
	}

	@SuppressWarnings("unused")
	private void oniWin() {
		oni(true);
	}

	@SuppressWarnings("unused")
	private void openEditionFolder() {
		try {
			Desktop.getDesktop().open(Paths.getEditionBasePath());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private boolean componentBelongsToWindow(Component c) {
		while (c != null) {
			if (c == this)
				return true;
			c = c.getParent();
		}
		return false;
	}

	@Override
	public boolean dispatchKeyEvent(KeyEvent e) {
		if (e.getID() == KeyEvent.KEY_PRESSED) {
			switch (e.getKeyCode()) {
				case KeyEvent.VK_F5:
					if (componentBelongsToWindow(e.getComponent()))
						refreshLocalMods();
					return true;
			}
		}
		return false;
	}

	@Override
	public void handleAbout(ApplicationEvent event) {
		event.setHandled(true);
		showAbout();
	}

	@Override
	public void handleOpenApplication(ApplicationEvent event) {
	}

	@Override
	public void handleOpenFile(ApplicationEvent event) {
	}

	@Override
	public void handlePreferences(ApplicationEvent event) {
		showSettings();
	}

	@Override
	public void handlePrintFile(ApplicationEvent event) {
	}

	@Override
	public void handleQuit(ApplicationEvent event) {
		event.setHandled(true);
		saveLocalData();
		exit();
	}

	@Override
	public void handleReOpenApplication(ApplicationEvent event) {
	}

}