/*******************************************************************************
 * Copyright (c) 2016, 2018 Red Hat Inc. and others.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - Initial Contribution
 *******************************************************************************/
package org.eclipse.linuxtools.internal.docker.ui.commands;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.linuxtools.docker.core.DockerException;
import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IDockerContainer;
import org.eclipse.linuxtools.docker.ui.Activator;
import org.eclipse.linuxtools.docker.ui.wizards.ImageSearch;
import org.eclipse.linuxtools.internal.docker.core.DockerConnection;
import org.eclipse.linuxtools.internal.docker.ui.wizards.ContainerCopyTo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;

/**
 * Command handler that opens the {@link ImageSearch} wizard and pulls the
 * selected image in background on completion.
 *
 */
public class CopyToContainerCommandHandler extends AbstractHandler {

	private static final String ERROR_COPYING_TO_CONTAINER_NO_CONNECTION = "command.copytocontainer.failure.no_connection"; //$NON-NLS-1$
	private static final String MISSING_CONNECTION = "missing_connection"; //$NON-NLS-1$
	private static final String ERROR_COPYING_TO_CONTAINER = "command.copytocontainer.error.msg"; //$NON-NLS-1$
	private static final String COPY_TO_CONTAINER_JOB_TASK = "command.copytocontainer.job.task"; //$NON-NLS-1$
	private static final String COPY_TO_CONTAINER_JOB_TITLE = "command.copytocontainer.job.title"; //$NON-NLS-1$
	private static final String COPY_TO_CONTAINER_JOB_SUBTASK = "command.copytocontainer.job.subtask"; //$NON-NLS-1$

	@Override
	public Object execute(final ExecutionEvent event) {
		final IWorkbenchPart activePart = HandlerUtil.getActivePart(event);
		final IDockerConnection connection = CommandUtils
				.getCurrentConnection(activePart);
		final List<IDockerContainer> selectedContainers = CommandUtils
				.getSelectedContainers(activePart);
		if (selectedContainers.size() != 1) {
			return null;
		}
		final IDockerContainer container = selectedContainers.get(0);
		if (connection == null) {
			MessageDialog.openError(
					PlatformUI.getWorkbench().getActiveWorkbenchWindow()
							.getShell(),
					CommandMessages.getString(MISSING_CONNECTION),
					CommandMessages
							.getString(
									ERROR_COPYING_TO_CONTAINER_NO_CONNECTION));
		} else {
			final ContainerCopyTo wizard = new ContainerCopyTo(connection,
					container);
			final boolean copyToContainer = CommandUtils.openWizard(wizard,
					HandlerUtil.getActiveShell(event));
			if (copyToContainer) {
				performCopyToContainer(connection, container,
						wizard.getTarget(), wizard.getSources());
			}
		}
		return null;
	}

	private void performCopyToContainer(final IDockerConnection connection,
			final IDockerContainer container, final String target,
			final List<Object> files) {
		final Job copyToContainerJob = new Job(
				CommandMessages.getFormattedString(
						COPY_TO_CONTAINER_JOB_TITLE, container.name())) {

			@Override
			protected IStatus run(final IProgressMonitor monitor) {
				monitor.beginTask(
						CommandMessages.getString(COPY_TO_CONTAINER_JOB_TASK),
						files.size() + 1);
				java.nio.file.Path tmpDir = null;
				try (Closeable token = ((DockerConnection) connection)
						.getOperationToken()) {
					for (Object proxy : files) {
						File file = (File) proxy;
						if (monitor.isCanceled()) {
							monitor.done();
							return Status.CANCEL_STATUS;
						}
						try {
							monitor.setTaskName(
									CommandMessages.getFormattedString(
											COPY_TO_CONTAINER_JOB_SUBTASK,
											proxy.toString()));
							monitor.worked(1);
							// we can only copy from a directory so copy the
							// files to a temporary directory
							if (tmpDir == null) {
								tmpDir = Files.createTempDirectory(
										Activator.PLUGIN_ID);
							}
							if (file.isDirectory()) {
								java.nio.file.Path sourcePath = FileSystems
										.getDefault()
										.getPath(file.getAbsolutePath());
								final java.nio.file.Path target = tmpDir
										.resolve(sourcePath.getFileName());
								Files.createDirectory(target);
								Files.walkFileTree(sourcePath,
										new SimpleFileVisitor<Path>() {
											@Override
											public FileVisitResult preVisitDirectory(
													final Path dir,
													final BasicFileAttributes attrs)
													throws IOException {
												java.nio.file.Path targetPath = target
														.resolve(sourcePath
																.relativize(
																		dir));
												Files.createDirectories(
														targetPath);
												return FileVisitResult.CONTINUE;
											}

											@Override
											public FileVisitResult visitFile(
													final Path file,
													final BasicFileAttributes attrs)
													throws IOException {
												monitor.setTaskName(
														CommandMessages
																.getFormattedString(
																		COPY_TO_CONTAINER_JOB_SUBTASK,
																		file.toString()));

												Files.copy(file, target.resolve(
														sourcePath.relativize(
																file)));
												return FileVisitResult.CONTINUE;
											}
										});


							} else {
								monitor.worked(1);
								java.nio.file.Path sourcePath = FileSystems
										.getDefault()
										.getPath(file.getAbsolutePath());
								java.nio.file.Path targetPath = tmpDir
										.resolve(sourcePath.getFileName());
								Files.copy(sourcePath,
										targetPath,
										StandardCopyOption.REPLACE_EXISTING);
							}
						} catch (final IOException e) {
							Display.getDefault()
									.syncExec(() -> MessageDialog.openError(
											PlatformUI.getWorkbench()
													.getActiveWorkbenchWindow()
													.getShell(),
											CommandMessages.getFormattedString(
													ERROR_COPYING_TO_CONTAINER,
													proxy.toString(),
													container.name()),
											e.getMessage()));
						}
					}
					// copy any individual files picked to Container
					// via the temporary directory
					try {
						((DockerConnection) connection).copyToContainer(
								token, tmpDir.toString(), container.id(),
								target);
						deleteTmpDir(tmpDir);
					} catch (final DockerException | IOException e) {
						Display.getDefault()
								.syncExec(() -> MessageDialog.openError(
										PlatformUI.getWorkbench()
												.getActiveWorkbenchWindow()
												.getShell(),
										CommandMessages.getFormattedString(
												ERROR_COPYING_TO_CONTAINER,
												target, container.name()),
										e.getCause() != null
												? e.getCause().getMessage()
												: e.getMessage()));

					}
				} catch (InterruptedException e) {
					// do nothing
				} catch (IOException e1) {
					Activator.log(e1);
				} catch (DockerException e1) {
					Activator.log(e1);
				} finally {
					monitor.done();
				}
				return Status.OK_STATUS;
			}

		};

		copyToContainerJob.schedule();

	}

	private void deleteTmpDir(java.nio.file.Path tmpPath) {
		File tmpDir = tmpPath.toFile();
		for (File f : tmpDir.listFiles()) {
			f.delete();
		}
		tmpDir.delete();
	}

}
