/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Martin Boyle <martingboyle@gmail.com> - Fix for
 *     		Bug 183013 [Wizards] Error importing into linked EFS folder - "undefined path variable"
 *     Marc-Andre Laperle (Ericsson) - Bug 279902 - [Import/Export] WizardResourceImportPage.createDestinationGroup is final
 *******************************************************************************/
package org.eclipse.ui.dialogs;

import java.util.ArrayList;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.equinox.bidi.StructuredTextTypeHandlerFactory;
import org.eclipse.jface.util.BidiUtils;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.ide.dialogs.IElementFilter;
import org.eclipse.ui.ide.dialogs.ResourceTreeAndListGroup;
import org.eclipse.ui.internal.ide.DialogUtil;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.model.WorkbenchViewerComparator;

/**
 * The abstract superclass for a typical import wizard's main page.
 * <p>
 * Clients may subclass this page to inherit its common destination resource
 * selection facilities.
 * </p>
 * <p>
 * Subclasses must implement
 * <ul>
 *   <li><code>createSourceGroup</code></li>
 * </ul>
 * </p>
 * <p>
 * Subclasses may override
 * <ul>
 *   <li><code>allowNewContainerName</code></li>
 * </ul>
 * </p>
 * <p>
 * Subclasses may extend
 * <ul>
 *   <li><code>handleEvent</code></li>
 * </ul>
 * </p>
 */
public abstract class WizardResourceImportPage extends WizardDataTransferPage {
    private IResource currentResourceSelection;

    // initial value stores
    private String initialContainerFieldValue;

    protected java.util.List selectedTypes = new ArrayList();

    // widgets
    private Text containerNameField;

    private Button containerBrowseButton;

    /**
	 * The <code>selectionGroup</code> field should have been created with a
	 * private modifier. Subclasses should not access this field directly.
	 *
	 * @noreference This field is not intended to be referenced by clients.
	 */
    protected ResourceTreeAndListGroup selectionGroup;

    //messages
    private static final String EMPTY_FOLDER_MESSAGE = IDEWorkbenchMessages.WizardImportPage_specifyFolder;

    private static final String EMPTY_PROJECT_MESSAGE = IDEWorkbenchMessages.WizardImportPage_specifyProject;

    private static final String INACCESSABLE_FOLDER_MESSAGE = IDEWorkbenchMessages.WizardImportPage_folderMustExist;

    /**
     * Creates an import wizard page. If the initial resource selection
     * contains exactly one container resource then it will be used as the default
     * import destination.
     *
     * @param name the name of the page
     * @param selection the current resource selection
     */
    protected WizardResourceImportPage(String name,
            IStructuredSelection selection) {
        super(name);

        //Initialize to null
        currentResourceSelection = null;
        if (selection.size() == 1) {
            Object firstElement = selection.getFirstElement();
			currentResourceSelection = Adapters.adapt(firstElement, IResource.class);
        }

        if (currentResourceSelection != null) {
            if (currentResourceSelection.getType() == IResource.FILE) {
				currentResourceSelection = currentResourceSelection.getParent();
			}

            if (!currentResourceSelection.isAccessible()) {
				currentResourceSelection = null;
			}
        }

    }

    /**
     * The <code>WizardResourceImportPage</code> implementation of this
     * <code>WizardDataTransferPage</code> method returns <code>true</code>.
     * Subclasses may override this method.
     */
    @Override
	protected boolean allowNewContainerName() {
        return true;
    }

    @Override
	public void createControl(Composite parent) {

        initializeDialogUnits(parent);

        Composite composite = new Composite(parent, SWT.NULL);
        composite.setLayout(new GridLayout());
        composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
                | GridData.HORIZONTAL_ALIGN_FILL));
        composite.setSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
        composite.setFont(parent.getFont());

        createSourceGroup(composite);

        createDestinationGroup(composite);

        createOptionsGroup(composite);

        restoreWidgetValues();
        updateWidgetEnablements();
        setPageComplete(determinePageCompletion());
        setErrorMessage(null);	// should not initially have error message

        setControl(composite);
    }

    /**
     * Creates the import destination specification controls.
     *
     * @param parent the parent control
     */
    protected void createDestinationGroup(Composite parent) {
        // container specification group
        Composite containerGroup = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.numColumns = 3;
        containerGroup.setLayout(layout);
        containerGroup.setLayoutData(new GridData(
                GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
        containerGroup.setFont(parent.getFont());

        // container label
        Label resourcesLabel = new Label(containerGroup, SWT.NONE);
        resourcesLabel.setText(IDEWorkbenchMessages.WizardImportPage_folder);
        resourcesLabel.setFont(parent.getFont());

        // container name entry field
        containerNameField = new Text(containerGroup, SWT.SINGLE | SWT.BORDER);
        BidiUtils.applyBidiProcessing(containerNameField, StructuredTextTypeHandlerFactory.FILE);

        containerNameField.addListener(SWT.Modify, this);
        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
                | GridData.GRAB_HORIZONTAL);
        data.widthHint = SIZING_TEXT_FIELD_WIDTH;
        containerNameField.setLayoutData(data);
        containerNameField.setFont(parent.getFont());

        // container browse button
        containerBrowseButton = new Button(containerGroup, SWT.PUSH);
        containerBrowseButton.setText(IDEWorkbenchMessages.WizardImportPage_browse2);
        containerBrowseButton.setLayoutData(new GridData(
                GridData.HORIZONTAL_ALIGN_FILL));
        containerBrowseButton.addListener(SWT.Selection, this);
        containerBrowseButton.setFont(parent.getFont());
        setButtonLayoutData(containerBrowseButton);

        initialPopulateContainerField();
    }

    /**
     *	Create the import source selection widget
     */
    protected void createFileSelectionGroup(Composite parent) {

        //Just create with a dummy root.
        this.selectionGroup = new ResourceTreeAndListGroup(parent,
                new FileSystemElement("Dummy", null, true),//$NON-NLS-1$
                getFolderProvider(), new WorkbenchLabelProvider(),
                getFileProvider(), new WorkbenchLabelProvider(), SWT.NONE,
                DialogUtil.inRegularFontMode(parent));

        ICheckStateListener listener = event -> updateWidgetEnablements();

        WorkbenchViewerComparator comparator = new WorkbenchViewerComparator();
        this.selectionGroup.setTreeComparator(comparator);
        this.selectionGroup.setListComparator(comparator);
        this.selectionGroup.addCheckStateListener(listener);

    }

    /**
     * Creates the import source specification controls.
     * <p>
     * Subclasses must implement this method.
     * </p>
     *
     * @param parent the parent control
     */
    protected abstract void createSourceGroup(Composite parent);

    /*
     * @see WizardDataTransferPage.getErrorDialogTitle()
     */
    @Override
	protected String getErrorDialogTitle() {
        return IDEWorkbenchMessages.WizardImportPage_errorDialogTitle;
    }

    /**
     * Returns the path of the container resource specified in the container
     * name entry field, or <code>null</code> if no name has been typed in.
     * <p>
     * The container specified by the full path might not exist and would need to
     * be created.
     * </p>
     *
     * @return the full path of the container resource specified in
     *   the container name entry field, or <code>null</code>
     */
    protected IPath getContainerFullPath() {
        IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();

        //make the path absolute to allow for optional leading slash
        IPath testPath = getResourcePath();

        if (testPath.equals(workspace.getRoot().getFullPath())) {
			return testPath;
		}

        IStatus result = workspace.validatePath(testPath.toString(),
                IResource.PROJECT | IResource.FOLDER | IResource.ROOT);
        if (result.isOK()) {
            return testPath;
        }

        return null;
    }

    /**
     * Returns a content provider for <code>FileSystemElement</code>s that returns
     * only files as children.
     */
    protected abstract ITreeContentProvider getFileProvider();

    /**
     * Returns a content provider for <code>FileSystemElement</code>s that returns
     * only folders as children.
     */
    protected abstract ITreeContentProvider getFolderProvider();

    /**
     * Return the path for the resource field.
     * @return IPath
     */
    protected IPath getResourcePath() {
        if (this.containerNameField != null) {
            return getPathFromText(this.containerNameField);
        }

        if (this.initialContainerFieldValue != null && this.initialContainerFieldValue.length() > 0) {
            return new Path(this.initialContainerFieldValue).makeAbsolute();
        }

        return Path.EMPTY;
    }

    /**
     * Returns this page's list of currently-specified resources to be
     * imported. This is the primary resource selection facility accessor for
     * subclasses.
     *
     * @return a list of resources currently selected
     * for export (element type: <code>IResource</code>)
     */
    protected java.util.List getSelectedResources() {
        return this.selectionGroup.getAllCheckedListItems();
    }

    /**
     * Returns this page's list of currently-specified resources to be
     * imported filtered by the IElementFilter.
     * @since 3.10
     */
    protected void getSelectedResources(IElementFilter filter, IProgressMonitor monitor) throws InterruptedException {
        this.selectionGroup.getAllCheckedListItems(filter, monitor);
    }

    /**
     * <bold>DO NOT USE THIS METHOD</bold>
     * Returns this page's list of currently-specified resources to be
     * imported filtered by the IElementFilter.
     * @deprecated Should use the API {@link IElementFilter}
     */
    @Deprecated
	protected void getSelectedResources(org.eclipse.ui.internal.ide.dialogs.IElementFilter filter, IProgressMonitor monitor) throws InterruptedException {
        this.selectionGroup.getAllCheckedListItems(filter, monitor);
    }

    /**
     * Returns the container resource specified in the container name entry field,
     * or <code>null</code> if such a container does not exist in the workbench.
     *
     * @return the container resource specified in the container name entry field,
     *   or <code>null</code>
     */
    protected IContainer getSpecifiedContainer() {
        IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
        IPath path = getContainerFullPath();
        if (workspace.getRoot().exists(path)){
        	IResource resource = workspace.getRoot().findMember(path);
        	if(resource.getType() == IResource.FILE) {
				return null;
			}
        	return (IContainer) resource;

        }


        return null;
    }

    /**
     * Returns a collection of the currently-specified resource types for
     * use by the type selection dialog.
     */
    protected java.util.List getTypesToImport() {

        return selectedTypes;
    }

    /**
     * Opens a container selection dialog and displays the user's subsequent
     * container resource selection in this page's container name field.
     */
    protected void handleContainerBrowseButtonPressed() {
        // see if the user wishes to modify this container selection
        IPath containerPath = queryForContainer(getSpecifiedContainer(),
                IDEWorkbenchMessages.WizardImportPage_selectFolderLabel,
                IDEWorkbenchMessages.WizardImportPage_selectFolderTitle);

        // if a container was selected then put its name in the container name field
        if (containerPath != null) { // null means user cancelled
            setErrorMessage(null);
            containerNameField.setText(containerPath.makeRelative().toString());
        }
    }

    /**
     * The <code>WizardResourceImportPage</code> implementation of this
     * <code>Listener</code> method handles all events and enablements for controls
     * on this page. Subclasses may extend.
     * @param event Event
     */
    @Override
	public void handleEvent(Event event) {
        Widget source = event.widget;

        if (source == containerBrowseButton) {
			handleContainerBrowseButtonPressed();
		}

        updateWidgetEnablements();
    }

    /**
     *	Open a registered type selection dialog and note the selections
     *	in the receivers types-to-export field
     */
    protected void handleTypesEditButtonPressed() {

        TypeFilteringDialog dialog = new TypeFilteringDialog(getContainer()
                .getShell(), getTypesToImport());

        dialog.open();

        Object[] newSelectedTypes = dialog.getResult();
        if (newSelectedTypes != null) { // ie.- did not press Cancel
            this.selectedTypes = new ArrayList(newSelectedTypes.length);
            for (Object newSelectedType : newSelectedTypes) {
				this.selectedTypes.add(newSelectedType);
			}

            setupSelectionsBasedOnSelectedTypes();
        }

    }

    /**
     * Sets the initial contents of the container name field.
     */
    protected final void initialPopulateContainerField() {
        if (initialContainerFieldValue != null) {
			containerNameField.setText(initialContainerFieldValue);
		} else if (currentResourceSelection != null) {
			containerNameField.setText(currentResourceSelection.getFullPath()
                    .makeRelative().toString());
		}
    }

    /**
     * Set all of the selections in the selection group to value
     * @param value boolean
     */
    protected void setAllSelections(boolean value) {
        selectionGroup.setAllSelections(value);
    }

    /**
     * Sets the value of this page's container resource field, or stores
     * it for future use if this page's controls do not exist yet.
     *
     * @param value String
     */
    public void setContainerFieldValue(String value) {
        if (containerNameField == null) {
			initialContainerFieldValue = value;
		} else {
			containerNameField.setText(value);
		}
    }

    /**
     * Update the tree to only select those elements that match the selected types.
     * Do nothing by default.
     */
    protected void setupSelectionsBasedOnSelectedTypes() {
    }

    /**
     * Update the selections with those in map .
     * @param map Map - key tree elements, values Lists of list elements
     */
    protected void updateSelections(final Map map) {

        Runnable runnable = () -> selectionGroup.updateSelections(map);

        BusyIndicator.showWhile(getShell().getDisplay(), runnable);
    }

    /**
     * Check if widgets are enabled or disabled by a change in the dialog.
     */
    @Override
	protected void updateWidgetEnablements() {

        boolean pageComplete = determinePageCompletion();
        setPageComplete(pageComplete);
        if (pageComplete) {
			setMessage(null);
		}
        super.updateWidgetEnablements();
    }

    @Override
	protected final boolean validateDestinationGroup() {

        IPath containerPath = getContainerFullPath();
        if (containerPath == null) {
            setMessage(EMPTY_FOLDER_MESSAGE);
            return false;
        }

        // If the container exist, validate it
        IContainer container = getSpecifiedContainer();
        if (container == null) {
        	//If it exists but is not valid then abort
        	if(IDEWorkbenchPlugin.getPluginWorkspace().getRoot().exists(getContainerFullPath())) {
				return false;
			}

            //if it is does not exist be sure the project does
            IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
            IPath projectPath = containerPath.removeLastSegments(containerPath
                    .segmentCount() - 1);

            if (workspace.getRoot().exists(projectPath)) {
				return true;
			}
            setErrorMessage(IDEWorkbenchMessages.WizardImportPage_projectNotExist);
            return false;
        }
        if (!container.isAccessible()) {
             setErrorMessage(INACCESSABLE_FOLDER_MESSAGE);
             return false;
        }
        if (container.getLocationURI() == null) {
          if (container.isLinked()) {
               setErrorMessage(IDEWorkbenchMessages.WizardImportPage_undefinedPathVariable);
          } else {
               setErrorMessage(IDEWorkbenchMessages.WizardImportPage_containerNotExist);
          }
         return false;
        }


        if (sourceConflictsWithDestination(containerPath)) {
            setErrorMessage(getSourceConflictMessage());
            return false;
        }

        if (container instanceof IWorkspaceRoot){
        	setErrorMessage(EMPTY_PROJECT_MESSAGE);
        	return false;
        }
        return true;

    }

    /**
     * Returns the error message for when the source conflicts
     * with the destination.
     */
    protected final String getSourceConflictMessage() {
        return (IDEWorkbenchMessages.WizardImportPage_importOnReceiver);
    }

    /**
     * Returns whether or not the source location conflicts
     * with the destination resource. By default this is not
     * checked, so <code>false</code> is returned.
     *
     * @param sourcePath the path being checked
     * @return <code>true</code> if the source location conflicts with the
     *   destination resource, <code>false</code> if not
     */
    protected boolean sourceConflictsWithDestination(IPath sourcePath) {
        return false;
    }

    /*
     * @see WizardDataTransferPage.determinePageCompletion.
     */
    @Override
	protected boolean determinePageCompletion() {
        //Check for valid projects before making the user do anything
        if (noOpenProjects()) {
            setErrorMessage(IDEWorkbenchMessages.WizardImportPage_noOpenProjects);
            return false;
        }
        return super.determinePageCompletion();
    }

    /**
     * Returns whether or not the passed workspace has any
     * open projects
     * @return boolean
     */
    private boolean noOpenProjects() {
		IProject[] projects = IDEWorkbenchPlugin.getPluginWorkspace().getRoot().getProjects();
        for (IProject project : projects) {
            if (project.isOpen()) {
				return false;
			}
        }
        return true;
    }
}
