/*******************************************************************************
 * Copyright (c) 2016, 2018 Red Hat.
 * 
 * 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.wizards;

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

import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.linuxtools.docker.ui.Activator;
import org.eclipse.linuxtools.internal.docker.core.ContainerFileProxy;
import org.eclipse.linuxtools.internal.docker.ui.CheckboxTreeAndListGroup;
import org.eclipse.linuxtools.internal.docker.ui.MinimizedFileSystemElement;
import org.eclipse.linuxtools.internal.docker.ui.SWTImagesFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.FileSystemElement;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.model.WorkbenchViewerComparator;
import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;

/**
 * A standard file selection dialog which solicits a list of files from the user.
 * The <code>getResult</code> method returns the selected files.
 * <p>
 * This class may be instantiated; it is not intended to be subclassed.
 * </p>
 * <p>
 * Example:
 * <pre>
 *	FileSelectionDialog dialog =
 *		new FileSelectionDialog(getShell(), rootElement, msg);
 *	dialog.setInitialSelections(selectedResources);
 *	dialog.open();
 *	return dialog.getResult();
 * </pre>
 * </p>
 * @noextend This class is not intended to be subclassed by clients.
 */
public class ContainerCopyFromPage extends WizardPage {

	private final static String NAME = "ContainerCopyFrom.name"; //$NON-NLS-1$
	private final static String TITLE = "ContainerCopyFrom.title"; //$NON-NLS-1$
	private final static String DESC = "ContainerCopyFrom.desc"; //$NON-NLS-1$
	private static final String TARGET_LABEL = "ContainerCopyFrom.target.label"; //$NON-NLS-1$
	private static final String TARGET_TOOLTIP = "ContainerCopyFrom.target.tooltip"; //$NON-NLS-1$
	private static final String SOURCE_LABEL = "ContainerCopyFrom.source.label"; //$NON-NLS-1$
	private static final String SOURCE_TOOLTIP = "ContainerCopyFrom.source.tooltip"; //$NON-NLS-1$
	private static final String NO_TARGET_SPECIFIED = "ContainerCopyFrom.notarget.error"; //$NON-NLS-1$
	private static final String NO_SOURCE_SPECIFIED = "ContainerCopyFrom.nosource.error"; //$NON-NLS-1$
	private static final String BROWSE_LABEL = "ContainerCopyFrom.browse.label"; //$NON-NLS-1$

	// the root file representative to populate the viewer with
    private FileSystemElement root;

	private IImportStructureProvider structureProvider;

	private boolean canBrowseContainer;

    // the visual selection widget group
    CheckboxTreeAndListGroup selectionGroup;

	private Text targetText;
	private Text sourceText;
	private Button browseButton;

	private String target;

    // sizing constants
    private static final int SIZING_SELECTION_WIDGET_WIDTH = 500;

    private static final int SIZING_SELECTION_WIDGET_HEIGHT = 250;
	private static final int COLUMNS = 3;

    /**
	 * Wizard page for copying files from container
	 * 
	 * @param fileSystemElement
	 *            - FileSystemElement of root
	 * @param structureProvider
	 *            - IImportStructureProvider to get file system structure from
	 *            container
	 * @param containerName
	 *            - name of container
	 * @param canBrowseContainer
	 *            - whether we can browse the Container's file system
	 */
	public ContainerCopyFromPage(FileSystemElement fileSystemElement,
			IImportStructureProvider structureProvider, String containerName,
			boolean canBrowseContainer) {
		super(WizardMessages.getString(NAME));
		setDescription(WizardMessages.getFormattedString(DESC,
				containerName));
		setTitle(WizardMessages.getString(TITLE));
		setImageDescriptor(SWTImagesFactory.DESC_WIZARD);
        root = fileSystemElement;
		this.structureProvider = structureProvider;
		this.canBrowseContainer = canBrowseContainer;
    }


	/*
	 * Return the host directory for the copy operation
	 */
	public String getTarget() {
		return target;
	}

	/*
	 * Get an iterator for the selected items to copy
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Iterator getValueIterator() {
		if (canBrowseContainer) {
			return selectionGroup.getAllWhiteCheckedItems();
		} else {
			// We need to fake a collection of MinimizedFileSystemElement with
			// one element.
			ArrayList out = new ArrayList();
			String sourceName = sourceText.getText();
			MinimizedFileSystemElement element = new MinimizedFileSystemElement(
					sourceName, null, false);
			element.setFileSystemObject(
					new ContainerFileProxy(sourceName, "", false)); //$NON-NLS-1$
			out.add(element);
			return out.iterator();
		}
	}

	private ModifyListener Listener = e -> validate();
	private ICheckStateListener CheckListener = e -> validate();

	private void validate() {
		boolean complete = true;
		boolean error = false;

		if (targetText.getText().length() == 0) {
			error = true;
			setErrorMessage(WizardMessages.getString(NO_TARGET_SPECIFIED));
		} else {
			File f = new File(targetText.getText());
			if (!f.exists()) {
				error = true;
				setErrorMessage(WizardMessages.getString(NO_TARGET_SPECIFIED));
			}
		}

		if (!error) {
			if (canBrowseContainer) {
				if (selectionGroup.getCheckedElementCount() == 0) {
					error = true;
					setErrorMessage(
							WizardMessages.getString(NO_SOURCE_SPECIFIED));
				}
			} else {
				if (sourceText.getText().length() == 0) {
					error = true;
					setErrorMessage(
							WizardMessages.getString(NO_SOURCE_SPECIFIED));
				}
			}
		}

		if (!error) {
			target = targetText.getText();
			setErrorMessage(null);
		}
		setPageComplete(complete && !error);
	}

    @Override
	public void createControl(Composite parent) {
        // page group
		Composite composite = new Composite(parent, SWT.NULL);
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
				.grab(true, false).applyTo(composite);
		GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6)
				.applyTo(composite);

		Label targetLabel = new Label(composite, SWT.NULL);
		targetLabel.setText(WizardMessages.getString(TARGET_LABEL));

		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(1, 1)
				.grab(false, false).applyTo(targetLabel);

		targetText = new Text(composite, SWT.BORDER | SWT.SINGLE);
		targetText.addModifyListener(Listener);
		targetText.setToolTipText(WizardMessages.getString(TARGET_TOOLTIP));

		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
				.grab(true, false).applyTo(targetText);

		browseButton = new Button(composite, SWT.NONE);
		browseButton.setText(WizardMessages.getString(BROWSE_LABEL));
		browseButton.addSelectionListener(onBrowseSelect());

		GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL).span(1, 1)
				.grab(false, false).applyTo(browseButton);

		// Create a fake parent of the root to be the dialog input element.
        // Use an empty label so that display of the element's full name
        // doesn't include a confusing label
        FileSystemElement input = new FileSystemElement("", null, true);//$NON-NLS-1$
        input.addChild(root);
        root.setParent(input);

		Composite selectionComposite = null;

		if (canBrowseContainer) {
			selectionComposite = new Composite(composite, SWT.NULL);
			GridLayout selectionLayout = new GridLayout();
			selectionComposite.setLayout(selectionLayout);

			selectionGroup = new CheckboxTreeAndListGroup(selectionComposite,
					input, getFolderProvider(), getDynamicFolderProvider(),
					new WorkbenchLabelProvider(), getFileProvider(),
					new WorkbenchLabelProvider(), SWT.NONE,
					SIZING_SELECTION_WIDGET_WIDTH, // since this page has no
													// other significantly-sized
					SIZING_SELECTION_WIDGET_HEIGHT); // widgets we need to
														// hardcode the combined
														// widget's
			// size, otherwise it will open too small
			GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(3, 1)
					.grab(true, false).applyTo(selectionComposite);
		} else {
			Label sourceLabel = new Label(composite, SWT.NONE);
			sourceLabel.setText(WizardMessages.getString(SOURCE_LABEL));

			GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
					.span(1, 1).grab(false, false).applyTo(sourceLabel);

			sourceText = new Text(composite, SWT.BORDER | SWT.SINGLE);
			sourceText.addModifyListener(Listener);
			sourceText.setToolTipText(WizardMessages.getString(SOURCE_TOOLTIP));

			GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
					.grab(true, false).applyTo(sourceText);

		}

		if (canBrowseContainer) {
			WorkbenchViewerComparator comparator = new WorkbenchViewerComparator();
			selectionGroup.setTreeComparator(comparator);
			selectionGroup.setListComparator(comparator);
			selectionGroup.addCheckStateListener(CheckListener);
		}

		setControl(composite);
		validate();
		setPageComplete(false);

		if (canBrowseContainer) {
			selectionGroup.aboutToOpen();
		}
    }

	private SelectionListener onBrowseSelect() {
		final ContainerCopyFromPage page = this;
		return SelectionListener.widgetSelectedAdapter(e -> {
			DirectoryDialog d = new DirectoryDialog(
					Activator.getActiveWorkbenchShell());
			String x = d.open();
			if (x != null) {
				page.targetText.setText(x);
			}
		});
	}
    /**
     * Returns a content provider for <code>FileSystemElement</code>s that returns
     * only files as children.
     */
    private ITreeContentProvider getFileProvider() {
        return new WorkbenchContentProvider() {
            @Override
			public Object[] getChildren(Object o) {
				if (o instanceof FileSystemElement) {
					return ((FileSystemElement) o).getFiles().getChildren(o);
                }

                return new Object[0];
            }
        };
    }

    /**
     * Returns a content provider for <code>FileSystemElement</code>s that returns
     * only folders as children.
     */
    private ITreeContentProvider getFolderProvider() {
        return new WorkbenchContentProvider() {
            @Override
			public Object[] getChildren(Object o) {
				if (o instanceof FileSystemElement) {
					return ((FileSystemElement) o).getFolders().getChildren(o);
                }

                return new Object[0];
			}
        };
    }

    /**
	 * Returns a content provider for <code>FileSystemElement</code>s that
	 * returns only folders as children.
	 */
	private ITreeContentProvider getDynamicFolderProvider() {
		return new WorkbenchContentProvider() {
			@Override
			public Object[] getChildren(Object o) {
				if (o instanceof MinimizedFileSystemElement) {
					return ((MinimizedFileSystemElement) o)
							.getFolders(structureProvider)
							.getChildren(o);
				} else if (o instanceof FileSystemElement) {
					return ((FileSystemElement) o).getFolders().getChildren(o);
				}

				return new Object[0];
			}
		};
	}

}
