/*
  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.CommandLineProcessor;
import net.azib.ipscan.config.GUIConfig;
import net.azib.ipscan.config.Labels;
import net.azib.ipscan.core.ScanningResult.ResultType;
import net.azib.ipscan.core.ScanningResultList;
import net.azib.ipscan.core.state.ScanningState;
import net.azib.ipscan.core.state.StateMachine;
import net.azib.ipscan.core.state.StateMachine.Transition;
import net.azib.ipscan.core.state.StateTransitionListener;
import net.azib.ipscan.gui.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;

import static net.azib.ipscan.core.ScanningResult.ResultType.*;

/**
 * ToolsActions
 * 
 * @author Anton Keks
 */
public class ToolsActions {

	public static final class Preferences implements Listener {
		
		private final PreferencesDialog preferencesDialog;
		private final ResultTable resultTable;
		private final StatusBar statusBar;
		
		public Preferences(PreferencesDialog preferencesDialog, ResultTable resultTable, StatusBar statusBar) {
			this.preferencesDialog = preferencesDialog;
			this.resultTable = resultTable;
			this.statusBar = statusBar;
		}

		public void handleEvent(Event event) {
			// show the preferences dialog
			preferencesDialog.open();
			
			// refresh the results and status bar in case anything was changed
			resultTable.updateResults();
			resultTable.updateColumnNames();
			statusBar.updateConfigText();
		}
	}

	public static final class ChooseFetchers implements Listener {
		private final SelectFetchersDialog selectFetchersDialog;
		
		public ChooseFetchers(SelectFetchersDialog selectFetchersDialog) {
			this.selectFetchersDialog = selectFetchersDialog;
		}

		public void handleEvent(Event event) {
			selectFetchersDialog.open();
		}

	}

	public static final class ScanStatistics implements Listener, StateTransitionListener {
		
		private final StatisticsDialog statisticsDialog;
		private final GUIConfig guiConfig;

		public ScanStatistics(GUIConfig guiConfig, StatisticsDialog statisticsDialog, StateMachine stateMachine, CommandLineProcessor commandProcessor) {
			this.guiConfig = guiConfig;
			this.statisticsDialog = statisticsDialog;
			// register for state changes
			if (!commandProcessor.shouldAutoQuit())
				stateMachine.addTransitionListener(this);
		}

		public void handleEvent(Event event) {
			statisticsDialog.open();
		}

		public void transitionTo(ScanningState state, Transition transition) {
			// switching to IDLE means the end of scanning
			if (transition == Transition.COMPLETE && guiConfig.showScanStats) {
				handleEvent(null);						
			}
		}
	}
	
	/**
	 * This listener updates the status bar when user selects many items in the result table
	 */
	public static final class TableSelection implements Listener {
		private final StatusBar statusBar;
		private final StateMachine stateMachine;

		public TableSelection(StatusBar statusBar, StateMachine stateMachine) {
			this.statusBar = statusBar;
			this.stateMachine = stateMachine;
		}

		public void handleEvent(Event event) {
			if (stateMachine.inState(ScanningState.IDLE)) {
				var resultTable = (Table) event.widget;
				var selectionCount = resultTable.getSelectionCount();
				if (selectionCount > 1) 
					statusBar.setStatusText(selectionCount + Labels.getLabel("text.hostsSelected"));
				else
					statusBar.setStatusText(null);
			}
		}
	}
	
	static abstract class SelectDesired implements Listener {
		private final ResultTable resultTable;
		private final ScanningResultList results;

		public SelectDesired(ResultTable resultTable) {
			this.resultTable = resultTable;
			this.results = resultTable.getScanningResults();
		}

		public void handleEvent(Event event) {
			var count = resultTable.getItemCount();
			resultTable.deselectAll();
			for (var i = 0; i < count; i++) {
				if (isDesired(results.getResult(i).getType())) {
					resultTable.select(i);
				}
			}
			event.widget = resultTable;
			resultTable.notifyListeners(SWT.Selection, event);
			resultTable.forceFocus();
		}

		abstract boolean isDesired(ResultType type);
	}
	
	public static final class SelectAlive extends SelectDesired {
		public SelectAlive(ResultTable resultTable) {
			super(resultTable);
		}

		boolean isDesired(ResultType type) {
			return type.ordinal() >= ALIVE.ordinal();
		}
	}

	public static final class SelectDead extends SelectDesired {
		public SelectDead(ResultTable resultTable) {
			super(resultTable);
		}

		boolean isDesired(ResultType type) {
			return type == DEAD;
		}
	}
	
	public static final class SelectWithPorts extends SelectDesired {
		public SelectWithPorts(ResultTable resultTable) {
			super(resultTable);
		}

		boolean isDesired(ResultType type) {
			return type == WITH_PORTS;
		}
	}
	
	public static final class SelectWithoutPorts extends SelectDesired {
		public SelectWithoutPorts(ResultTable resultTable) {
			super(resultTable);
		}

		boolean isDesired(ResultType type) {
			return type == ALIVE;
		}
	}
	
	/** 
	 * This cannot be accessed from the menu, but it provides the Ctrl+A
	 * Select All functionality for Windows (other platforms implement this themselves)
	 */
	public static class SelectAll implements Listener {
		private final ResultTable resultTable;

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

		public void handleEvent(Event event) {
			// Ctrl+A handler
			if (event.type == SWT.KeyDown && event.keyCode == 'a' && event.stateMask == SWT.MOD1) {
				resultTable.selectAll();
				// update selection status
				event.widget = resultTable;
        		resultTable.notifyListeners(SWT.Selection, event);
				event.doit = false;
			}
		}
	}

	public static final class SelectInvert implements Listener {
		private final ResultTable resultTable;

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

		public void handleEvent(Event event) {
			var count = resultTable.getItemCount();
			// the most naive implementation
			resultTable.setRedraw(false);
			for (var i = 0; i < count; i++) {
				if (resultTable.isSelected(i)) 
					resultTable.deselect(i);
				else
					resultTable.select(i);
			}
			resultTable.setRedraw(true);
			resultTable.redraw();
			event.widget = resultTable;
      resultTable.notifyListeners(SWT.Selection, event);
			resultTable.forceFocus();
		}
	}
}
