/*******************************************************************************
 * Copyright (c) 2016 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
 *******************************************************************************/

package org.eclipse.tracecompass.tmf.ui.dialog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;

import com.google.common.annotations.VisibleForTesting;

/**
 * A file dialog factory.
 * <p>
 * This allows file dialogs to be stubbed out for SWTBot tests.
 *
 * @author Matthew Khouzam
 * @since 2.2
 */
public final class TmfFileDialogFactory {
    private static @Nullable String[] fOverridePaths = null;

    /**
     * File dialog factory, creates a {@link FileDialog}.
     * <p>
     * Constructs a new instance of this class given only its parent.
     * </p>
     * <p>
     * If the factory is overridden with {@link #setOverrideFiles(String...)},
     * the FileDialog will return the set String when open is called instead of
     * opening a system window
     * </p>
     *
     * @param parent
     *            a shell which will be the parent of the new instance
     * @return the {@link FileDialog}
     *
     * @exception IllegalArgumentException
     *                <ul>
     *                <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
     *                </ul>
     * @exception SWTException
     *                <ul>
     *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
     *                thread that created the parent</li>
     *                <li>ERROR_INVALID_SUBCLASS - if this class is not an
     *                allowed subclass</li>
     *                </ul>
     */
    public static FileDialog create(Shell parent) {
        return create(parent, SWT.APPLICATION_MODAL);
    }

    /**
     * File dialog factory, creates a {@link FileDialog}.
     * <p>
     * Constructs a new instance of this class given its parent and a style
     * value describing its behavior and appearance.
     * </p>
     * <p>
     * The style value is either one of the style constants defined in class
     * <code>SWT</code> which is applicable to instances of this class, or must
     * be built by <em>bitwise OR</em>'ing together (that is, using the
     * <code>int</code> "|" operator) two or more of those <code>SWT</code>
     * style constants. The class description lists the style constants that are
     * applicable to the class. Style bits are also inherited from superclasses.
     * </p>
     * <p>
     * If the factory is overridden with {@link #setOverrideFiles(String[])},
     * the FileDialog will return the set String when open is called instead of
     * opening a system window
     * </p>
     *
     * @param parent
     *            a shell which will be the parent of the new instance
     * @param style
     *            the style of dialog to construct
     * @return the {@link FileDialog}
     *
     * @exception IllegalArgumentException
     *                <ul>
     *                <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
     *                </ul>
     * @exception SWTException
     *                <ul>
     *                <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
     *                thread that created the parent</li>
     *                <li>ERROR_INVALID_SUBCLASS - if this class is not an
     *                allowed subclass</li>
     *                </ul>
     *
     * @see SWT#SAVE
     * @see SWT#OPEN
     * @see SWT#MULTI
     */
    public static FileDialog create(Shell parent, int style) {
        String[] overridePath = fOverridePaths;
        if (overridePath != null) {
            fOverridePaths = null;
            return createNewFileDialog(parent, style, Arrays.asList(overridePath));
        }
        FileDialog fileDialog = new FileDialog(parent, style);
        if ((style & SWT.SAVE) != 0) {
            fileDialog.setOverwrite(true);
        }
        return fileDialog;
    }

    /**
     * Set the override string name that will be returned for the next
     * {@link FileDialog}. Must be called before creating the dialogs.
     *
     * This is a method aimed for testing, This should not be used in product
     * code.
     *
     * @param paths
     *            the paths to override the {@link FileDialog}. They must be
     *            absolute. One or many absolute paths may be entered. When many
     *            paths are entered, it return an input of a multi-select action
     *            if paths is null, it will undo overriding, if paths is a zero
     *            length array, it will behave as if the dialog was cancelled.
     */
    @VisibleForTesting
    @SuppressWarnings("null")
    public static void setOverrideFiles(String... paths) {
        fOverridePaths = paths;
    }

    private static FileDialog createNewFileDialog(Shell parent, int style, List<String> overridePaths) {
        return new FileDialog(parent, style) {
            @Override
            public String open() {
                return !overridePaths.isEmpty() ? overridePaths.get(0) : null;
            }

            @Override
            protected void checkSubclass() {
                /*
                 * do nothing, allow this class to be overridden without
                 * throwing a runtime exception
                 */
            }

            @Override
            public String getFileName() {
                return !overridePaths.isEmpty() ? getFileName(overridePaths.get(0)) : ""; //$NON-NLS-1$
            }

            @Override
            public String[] getFileNames() {
                List<String> outStrings = new ArrayList<>();
                for (String entry : overridePaths) {
                    outStrings.add(getFileName(entry));
                }
                return outStrings.toArray(new String[outStrings.size()]);
            }

            @Override
            public String getFilterPath() {
                return !overridePaths.isEmpty() ? new Path(overridePaths.get(0)).removeLastSegments(1).toString() : ""; //$NON-NLS-1$
            }

            private String getFileName(String path) {
                return new Path(path).lastSegment();
            }
        };
    }
}
