/*
  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.Main;
import net.azib.ipscan.config.Labels;
import net.azib.ipscan.config.Version;
import net.azib.ipscan.core.ScanningResultList;
import net.azib.ipscan.core.UserErrorException;
import net.azib.ipscan.core.state.ScanningState;
import net.azib.ipscan.core.state.StateMachine;
import net.azib.ipscan.exporters.ExportProcessor;
import net.azib.ipscan.exporters.ExportProcessor.ScanningResultFilter;
import net.azib.ipscan.exporters.ExporterRegistry;
import net.azib.ipscan.exporters.TXTExporter;
import net.azib.ipscan.gui.ResultTable;
import net.azib.ipscan.gui.StatusBar;
import net.azib.ipscan.gui.feeders.FeederGUIRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * FileActions
 * 
 * @author Anton Keks
 */
public class ScanMenuActions {

	public static class LoadFromFile implements Listener {

		private final TXTExporter txtExporter;
		private final ExporterRegistry exporterRegistry;
		private final FeederGUIRegistry feederRegistry;
		private final ScanningResultList scanningResults;
		private final ResultTable resultTable;
		private final StateMachine stateMachine;

		public LoadFromFile(TXTExporter txtExporter, ExporterRegistry exporterRegistry, FeederGUIRegistry feederRegistry, ScanningResultList scanningResults, ResultTable resultTable, StateMachine stateMachine) {
			this.txtExporter = txtExporter;
			this.exporterRegistry = exporterRegistry;
			this.feederRegistry = feederRegistry;
			this.scanningResults = scanningResults;
			this.resultTable = resultTable;
			this.stateMachine = stateMachine;
		}

		public void handleEvent(Event event) {
			var fileDialog = new FileDialog(resultTable.getShell(), SWT.OPEN);

			// gather lists of extensions and exporter names
			List<String> extensions2 = new ArrayList<>();
			List<String> descriptions = new ArrayList<>();
			var labelBuffer = new StringBuffer(Labels.getLabel("title.load"));
			addFileExtensions(extensions2, descriptions, labelBuffer);

			List<String> extensions = new ArrayList<>();
			extensions.add(extensions2.get(0));

			fileDialog.setText(labelBuffer.toString());
			fileDialog.setFilterExtensions(extensions.toArray(new String[extensions.size()]));
			fileDialog.setFilterNames(descriptions.toArray(new String[descriptions.size()]));

			var fileName = fileDialog.open();
			if (fileName == null) return;

			loadResultsFrom(fileName);
		}

		private void loadResultsFrom(String fileName) {
			try {
				feederRegistry.select("feeder.range");
				scanningResults.initNewScan(feederRegistry.current().createFeeder());

				var results = txtExporter.importResults(fileName, feederRegistry.current());
				resultTable.clearAll();
				for (var result : results) {
					resultTable.addOrUpdateResultRow(result);
				}

				if (!results.isEmpty()) {
					var lastLoadedIP = results.get(results.size()-1).getAddress().getHostAddress();
					var feederIPs = feederRegistry.current().serialize();

					if (resumePreviousScan(lastLoadedIP, feederIPs[1]))
						stateMachine.continueScanning();
				}

			}
			catch (Exception e) {
				throw new UserErrorException("fileLoad.failed", e);
			}
		}

		private boolean resumePreviousScan(String lastIP, String endIP) {
			if (lastIP.equals(endIP)) return false;

			var box = new MessageBox(resultTable.getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.SHEET);
			box.setText(Labels.getLabel("menu.scan.load").replace("&", ""));
			box.setMessage(Labels.getLabel("text.scan.resume").replace("%LASTIP", lastIP).replace("%ENDIP", endIP));
			return box.open() == SWT.YES;
		}

		private void addFileExtensions(List<String> extensions, List<String> descriptions, StringBuffer sb) {
			sb.append(" (");
			for (var exporter : exporterRegistry) {
				extensions.add("*." + exporter.getFilenameExtension());
				sb.append(exporter.getFilenameExtension()).append(", ");
				descriptions.add(Labels.getLabel(exporter.getId()));
			}
			// strip the last comma
			sb.delete(sb.length() - 2, sb.length());
			sb.append(")");
		}
	}

	static abstract class SaveResults implements Listener {
		private final ExporterRegistry exporterRegistry;
		private final ResultTable resultTable;
		private final StatusBar statusBar;
		private final boolean isSelection;
		private final StateMachine stateMachine;

		SaveResults(ExporterRegistry exporterRegistry, ResultTable resultTable, StatusBar statusBar, StateMachine stateMachine, boolean isSelection) {
			this.exporterRegistry = exporterRegistry;
			this.resultTable = resultTable;
			this.statusBar = statusBar;
			this.stateMachine = stateMachine;
			this.isSelection = isSelection;
		}

		public void handleEvent(Event event) {
			if (resultTable.getItemCount() <= 0) {
				throw new UserErrorException("commands.noResults");
			}

			if (!stateMachine.inState(ScanningState.IDLE)) {
				// ask the user whether to save incomplete results
				var box = new MessageBox(resultTable.getShell(), SWT.YES | SWT.NO | SWT.ICON_WARNING | SWT.SHEET);
				box.setText(Version.NAME);
				box.setMessage(Labels.getLabel("exception.ExporterException.scanningInProgress"));
				if (box.open() != SWT.YES)
					return;
			}

			var fileDialog = new FileDialog(resultTable.getShell(), SWT.SAVE);

			// gather lists of extensions and exporter names
			List<String> extensions = new ArrayList<>();
			List<String> descriptions = new ArrayList<>();
			var labelBuffer = new StringBuffer(Labels.getLabel(isSelection ? "title.exportSelection" : "title.exportAll"));
			addFileExtensions(extensions, descriptions, labelBuffer);

			fileDialog.setText(labelBuffer.toString());
			fileDialog.setFilterExtensions(extensions.toArray(new String[extensions.size()]));
			fileDialog.setFilterNames(descriptions.toArray(new String[descriptions.size()]));

			var fileName = fileDialog.open();
			if (fileName == null) return;

			var exporter = exporterRegistry.createExporter(fileName);

			statusBar.setStatusText(Labels.getLabel("state.exporting"));

			// TODO: expose appending feature in the GUI
			var exportProcessor = new ExportProcessor(exporter, new File(fileName), false);

			// in case of isSelection we need to create our filter
			ScanningResultFilter filter = null;
			if (isSelection) {
				filter = (index, result) -> resultTable.isSelected(index);
			}

			exportProcessor.process(resultTable.getScanningResults(), filter);
			statusBar.setStatusText(null);
		}

		private void addFileExtensions(List<String> extensions, List<String> descriptions, StringBuffer sb) {
			sb.append(" (");
			for (var exporter : exporterRegistry) {
				extensions.add("*." + exporter.getFilenameExtension());
				sb.append(exporter.getFilenameExtension()).append(", ");
				descriptions.add(Labels.getLabel(exporter.getId()));
			}
			// strip the last comma
			sb.delete(sb.length() - 2, sb.length());
			sb.append(")");
		}
	}

	public static final class SaveAll extends SaveResults {
		public SaveAll(ExporterRegistry exporterRegistry, ResultTable resultTable, StatusBar statusBar, StateMachine stateMachine) {
			super(exporterRegistry, resultTable, statusBar, stateMachine, false);
		}
	}

	public static final class SaveSelection extends SaveResults {
		public SaveSelection(ExporterRegistry exporterRegistry, ResultTable resultTable, StatusBar statusBar, StateMachine stateMachine) {
			super(exporterRegistry, resultTable, statusBar, stateMachine, true);
		}
	}

	public static final class Quit implements Listener {
		private final Shell shell;

		public Quit(Shell shell) {
			this.shell = shell;
		}

		public void handleEvent(Event event) {
			shell.close();
		}
	}

	public static final class NewWindow implements Listener {
		public void handleEvent(Event event) {
			// start another instance in a new thread
			// doesn't currently work...
			new Thread("main") {
				public void run() {					
					Main.main();
				}
			}.start();
		}
	}

}
