/*
  This file is a part of Angry IP Scanner source code,
  see http://www.angryip.org/ for more information.
  Licensed under GPLv2.
 */
package net.azib.ipscan.gui.actions;

import net.azib.ipscan.config.Labels;
import net.azib.ipscan.config.OpenersConfig;
import net.azib.ipscan.core.UserErrorException;
import net.azib.ipscan.core.state.ScanningState;
import net.azib.ipscan.core.state.StateMachine;
import net.azib.ipscan.fetchers.FetcherRegistry;
import net.azib.ipscan.gui.DetailsWindow;
import net.azib.ipscan.gui.EditOpenersDialog;
import net.azib.ipscan.gui.ResultTable;
import net.azib.ipscan.gui.StatusBar;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;

/**
 * Commands and Context menu Actions.
 * All these operate on the items, selected in the results list.
 *
 * @author Anton Keks
 */
public class CommandsMenuActions {
	public Details details;
	public Delete delete;
	public Rescan rescan;
	public CopyIP copyIP;
	public CopyIPDetails copyIPDetails;
	public ShowOpenersMenu showOpenersMenu;
	public EditOpeners editOpeners;
	public SelectOpener selectOpener;

	public CommandsMenuActions(Details details, Delete delete, Rescan rescan, CopyIP copyIP, CopyIPDetails copyIPDetails, ShowOpenersMenu showOpenersMenu, EditOpeners editOpeners, SelectOpener selectOpener) {
		this.details = details;
		this.delete = delete;
		this.rescan = rescan;
		this.copyIP = copyIP;
		this.copyIPDetails = copyIPDetails;
		this.showOpenersMenu = showOpenersMenu;
		this.editOpeners = editOpeners;
		this.selectOpener = selectOpener;
	}

	/**
	 * Checks that there is at least one item selected in the results list.
	 */
	static void checkSelection(ResultTable resultTable) {
		if (resultTable.getItemCount() <= 0) {
			throw new UserErrorException("commands.noResults");
		}
		else
		if (resultTable.getSelectionIndex() < 0) {
			throw new UserErrorException("commands.noSelection");
		}
	}

	public static class Details implements Listener {
		private final ResultTable resultTable;
		private final DetailsWindow detailsWindow;
		
		public Details(ResultTable resultTable, DetailsWindow detailsWindow) {
			this.resultTable = resultTable;
			this.detailsWindow = detailsWindow;
			resultTable.addListener(SWT.Traverse, this);
			resultTable.addListener(SWT.MouseDoubleClick, this);
		}

		public void handleEvent(Event event) {
			// activate only if something is selected
			if (event.type == SWT.Selection || (resultTable.getSelectionIndex() >= 0 && (event.type == SWT.MouseDoubleClick || event.detail == SWT.TRAVERSE_RETURN))) {
				event.doit = false;
				checkSelection(resultTable);
				detailsWindow.open(); 
			}
		}
	}
	
	public static final class Delete implements Listener {
		private final ResultTable resultTable;
		private final StateMachine stateMachine;

		public Delete(ResultTable resultTable, StateMachine stateMachine) {
			this.resultTable = resultTable;
			this.stateMachine = stateMachine;
		}

		public void handleEvent(Event event) {
			// ignore other keys if this is a KeyDown event - 
			// the same listener is used for several events
			if (event.type == SWT.KeyDown && event.keyCode != SWT.DEL) return;
			// deletion not allowed when scanning
			if (!stateMachine.inState(ScanningState.IDLE)) return;

			var firstSelection = resultTable.getSelectionIndex();
			if (firstSelection < 0) return;

			resultTable.remove(resultTable.getSelectionIndices());
			resultTable.setSelection(firstSelection);
			event.widget = resultTable;
			resultTable.notifyListeners(SWT.Selection, event);
		}
	}

	public static final class Rescan implements Listener {
		private final ResultTable resultTable;
		private final StateMachine stateMachine;

		public Rescan(ResultTable resultTable, StateMachine stateMachine) {
			this.resultTable = resultTable;
			this.stateMachine = stateMachine;
		}

		public void handleEvent(Event event) {
			checkSelection(resultTable);
			stateMachine.rescan();
		}
	}
	
	/**
	 * Copies currently selected IP to the clipboard.
	 * Used as both menu item listener and key down listener.
	 */
	public static final class CopyIP implements Listener {
		private final ResultTable resultTable;

		public CopyIP(ResultTable resultTable) {
			this.resultTable = resultTable;
		}

		public void handleEvent(Event event) {
			if (event.type == SWT.KeyDown) {
				// if this is not Ctrl+C or nothing is selected, then simply do nothing
				if ((event.keyCode != 'c' && event.stateMask != SWT.MOD1) || resultTable.getSelectionIndex() < 0)
					return;
			}
			else {
				// if selected from the menu, check selection
				checkSelection(resultTable);
			}
			var clipboard = new Clipboard(event.display);
			clipboard.setContents(new Object[] {resultTable.getItem(resultTable.getSelectionIndex()).getText()}, new Transfer[] {TextTransfer.getInstance()});
			clipboard.dispose();
		}
	}
	
	public static final class CopyIPDetails implements Listener {
		private final ResultTable resultTable;

		public CopyIPDetails(ResultTable resultTable) {
			this.resultTable = resultTable;
		}

		public void handleEvent(Event event) {
			checkSelection(resultTable);
			var clipboard = new Clipboard(event.display);
			clipboard.setContents(new Object[] {resultTable.getSelectedResult().toString()}, new Transfer[] {TextTransfer.getInstance()});
			clipboard.dispose();
		}
	}
	
	public static final class ShowOpenersMenu implements Listener {
		
		private final Listener openersSelectListener;
		private final OpenersConfig openersConfig;

		public ShowOpenersMenu(OpenersConfig openersConfig, SelectOpener selectOpener) {
			this.openersConfig = openersConfig;
			this.openersSelectListener = selectOpener;
		}

		public void handleEvent(Event event) {
			var openersMenu = (Menu)event.widget;
			var menuItems = openersMenu.getItems();
			for (var i = 2; i < menuItems.length; i++) {
				menuItems[i].dispose();
			}
			
			// update menu items
			var index = 0;
			for (var name : openersConfig) {
				var menuItem = new MenuItem(openersMenu, SWT.CASCADE);
				
				index++;
				if (index <= 9) {
					name += "\tCtrl+" + index;
					menuItem.setAccelerator(SWT.MOD1 | ('0' + index));
				}
				
				menuItem.setText(name);
				menuItem.setData(index);
				menuItem.addListener(SWT.Selection, openersSelectListener);
			}

		}
	}
		
	public static final class EditOpeners implements Listener {
		
		private final FetcherRegistry fetcherRegistry;
		private final OpenersConfig openersConfig;

		public EditOpeners(FetcherRegistry fetcherRegistry, OpenersConfig openersConfig) {
			this.fetcherRegistry = fetcherRegistry;
			this.openersConfig = openersConfig;
		}

		public void handleEvent(Event event) {
			new EditOpenersDialog(fetcherRegistry, openersConfig).open(); 
		}
	}
	
	public static final class SelectOpener implements Listener {
		
		private final StatusBar statusBar;
		private final ResultTable resultTable;
		private final OpenerLauncher openerLauncher;
		private final OpenersConfig openersConfig;

		public SelectOpener(OpenersConfig openersConfig, StatusBar statusBar, ResultTable resultTable, OpenerLauncher openerLauncher) {
			this.openersConfig = openersConfig;
			this.statusBar = statusBar;
			this.resultTable = resultTable;
			this.openerLauncher = openerLauncher;
		}
		
		public void handleEvent(Event event) {
			var menuItem = (MenuItem) event.widget;
			var name = menuItem.getText();
			var indexOf = name.lastIndexOf('\t');
			if (indexOf >= 0) {
				name = name.substring(0, indexOf);
			}
			var opener = openersConfig.getOpener(name);

			var selectionIndices = resultTable.getSelectionIndices();
			if (selectionIndices.length == 0)
				throw new UserErrorException("commands.noSelection");

			for (var i : selectionIndices) {
				try {
					statusBar.setStatusText(Labels.getLabel("state.opening") + name);
					openerLauncher.launch(opener, i);
					// wait a bit to make status visible
					Thread.sleep(100);
				}
				catch (InterruptedException ignore) {}
				finally {
					statusBar.setStatusText(null);
				}
			}
		}
	}
}
