/*******************************************************************************
 * Copyright (c) 2014, 2017 Ericsson.
 *
 * All rights reserved. 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:
 *   Bernd Hufmann  - Initial API and implementation
 *******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;

import java.util.List;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceFolder;

/**
 * Handler to check for name clashes during import operations. It will allow
 * users to select renaming, overwriting or skipping of a given trace as well
 * as upcoming traces by keeping track of the user selection. In case of
 * overwriting the original trace will be deleted.
 *
 * See {@link ImportConfirmation} for users selection choices.
 *
 * @author Bernd Hufmann
 */
public class ImportConflictHandler {

    // ------------------------------------------------------------------------
    // Attributes
    // ------------------------------------------------------------------------
    private Shell fShell;
    private TmfTraceFolder fTraceFolderElement;
    private ImportConfirmation fConfirmationMode;

    // ------------------------------------------------------------------------
    // Constructor(s)
    // ------------------------------------------------------------------------
    /**
     * @param shell
     *              shell to display confirmation dialog
     * @param folder
     *              Target folder for the traces
     * @param initialMode
     *              Initial confirmation mode
     */
    public ImportConflictHandler(Shell shell, TmfTraceFolder folder, ImportConfirmation initialMode) {
        fShell = shell;
        fTraceFolderElement = folder;
        fConfirmationMode = initialMode;
    }

    // ------------------------------------------------------------------------
    // Operation(s)
    // ------------------------------------------------------------------------
    /**
     * It checks for name clashes. In case of a name clash it will open a
     * confirmation dialog where the use can rename, overwrite or skip
     * the trace. The user has also the choice to rename, overwrite or
     * skip all traces of subsequent calls to this method. This class will
     * keep track about the {@link ImportConfirmation} mode selected by the
     * user.
     *
     * In case of {@link ImportConfirmation#RENAME} or
     * {@link ImportConfirmation#RENAME_ALL} a new name will be return by
     * adding sequence number surrounded by (), e.g. (1) or (2).
     *
     * In case of {@link ImportConfirmation#OVERWRITE} or
     * {@link ImportConfirmation#OVERWRITE_ALL} the original trace will be
     * deleted and the original name will be returned.
     *
     * In case the dialog {@link ImportConfirmation#SKIP} or
     * {@link ImportConfirmation#SKIP_ALL} it will return null to indicate
     * the skipping.
     *
     * @param tracePath
     *                The trace to check
     * @param monitor
     *                The progress monitor
     * @return the trace name to use or null
     * @throws InterruptedException
     *                If the dialog box was cancelled
     * @throws CoreException
     *                If an error during deletion occurred
     */
    public String checkAndHandleNameClash(IPath tracePath, IProgressMonitor monitor) throws InterruptedException, CoreException {
        ImportConfirmation mode = checkForNameClash(tracePath);
        switch (mode) {
        case RENAME:
        case RENAME_ALL:
            return rename(tracePath);
        case OVERWRITE:
        case OVERWRITE_ALL:
            delete(tracePath, monitor);
            //$FALL-THROUGH$
        case CONTINUE:
            return tracePath.lastSegment();
        case SKIP:
        case SKIP_ALL:
        default:
            return null;
        }
    }

    // ------------------------------------------------------------------------
    // Helper methods
    // ------------------------------------------------------------------------
    private ImportConfirmation checkForNameClash(IPath tracePath) throws InterruptedException {
        // handle rename
        if (getExistingResource(tracePath) != null) {
            if ((fConfirmationMode == ImportConfirmation.RENAME_ALL) ||
                    (fConfirmationMode == ImportConfirmation.OVERWRITE_ALL) ||
                    (fConfirmationMode == ImportConfirmation.SKIP_ALL)) {
                return fConfirmationMode;
            }

            int returnCode = promptForOverwrite(tracePath);
            if (returnCode < 0) {
                // Cancel
                throw new InterruptedException();
            }
            fConfirmationMode = ImportConfirmation.values()[returnCode];
            return fConfirmationMode;
        }
        return ImportConfirmation.CONTINUE;
    }

    private int promptForOverwrite(IPath tracePath) {
        final MessageDialog dialog = new MessageDialog(fShell,
                Messages.ImportTraceWizard_MessageTitle, null, NLS.bind(Messages.ImportTraceWizard_TraceAlreadyExists, tracePath.makeRelativeTo(fTraceFolderElement.getProject().getPath())),
                MessageDialog.QUESTION, new String[] {
                        ImportConfirmation.RENAME.getInName(),
                        ImportConfirmation.RENAME_ALL.getInName(),
                        ImportConfirmation.OVERWRITE.getInName(),
                        ImportConfirmation.OVERWRITE_ALL.getInName(),
                        ImportConfirmation.SKIP.getInName(),
                        ImportConfirmation.SKIP_ALL.getInName(),
                }, 4) {
            @Override
            protected int getShellStyle() {
                return super.getShellStyle() | SWT.SHEET;
            }
        };

        final int[] returnValue = new int[1];
        fShell.getDisplay().syncExec(() -> returnValue[0] = dialog.open());
        return returnValue[0];
    }

    private static String rename(IPath tracePath) {
        IResource existingResource = getExistingResource(tracePath);
        if (existingResource == null) {
            return tracePath.lastSegment();
        }

        // Not using IFolder on purpose to leave the door open to import
        // directly into an IProject
        IContainer folder = existingResource.getParent();

        int i = 2;
        while (true) {
            String name = existingResource.getName() + '(' + Integer.toString(i++) + ')';
            IResource resource = folder.findMember(name);
            if (resource == null) {
                return name;
            }
        }
    }

    private void delete(IPath tracePath, IProgressMonitor monitor) throws CoreException {
        IResource existingResource = getExistingResource(tracePath);
        if (existingResource == null) {
            return;
        }
        TmfTraceElement existingTraceElement = getExistingTrace(tracePath);
        if (existingTraceElement != null) {
            // Delete existing TmfTraceElement
            existingTraceElement.delete(monitor, true);
            return;
        }

        // Delete resource existing in workspace
        existingResource.delete(true, monitor);
    }

    private TmfTraceElement getExistingTrace(IPath tracePath) {
        List<TmfTraceElement> traces = fTraceFolderElement.getTraces();
        for (TmfTraceElement t : traces) {
            if (t.getPath().equals(tracePath)) {
                return t;
            }
        }
        return null;
    }

    private static IResource getExistingResource(IPath tracePath) {
        // Look for existing resource
        return ResourcesPlugin.getWorkspace().getRoot().findMember(tracePath);
    }
}

