/*******************************************************************************
 * Copyright (c) 2015 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:
 *   Patrick Tasse - Initial API and implementation
 *******************************************************************************/

package org.eclipse.tracecompass.internal.tmf.ui.commands;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.Messages;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.ui.viewers.events.columns.TmfEventTableColumn;
import org.eclipse.ui.PlatformUI;

/**
 * This operation copies the text of selected trace events to the clipboard.
 */
public class CopyToClipboardOperation implements IRunnableWithProgress {

    private static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$
    private final ITmfTrace fTrace;
    private final ITmfFilter fFilter;
    private final List<TmfEventTableColumn> fColumns;
    private final long fStartRank;
    private final long fEndRank;

    /**
     * Constructor.
     *
     * @param trace
     *            the trace to copy events from
     * @param filter
     *            the filter to apply to trace events, or null
     * @param columns
     *            the list of event table columns
     * @param start
     *            the start rank of the selection
     * @param end
     *            the end rank of the selection
     */
    public CopyToClipboardOperation(ITmfTrace trace, ITmfFilter filter, List<TmfEventTableColumn> columns, long start, long end) {
        fTrace = trace;
        fFilter = filter;
        fColumns = columns;
        fStartRank = start;
        fEndRank = end;
    }

    @Override
    public void run(IProgressMonitor monitor) {
        final StringBuilder sb = new StringBuilder();
        monitor.beginTask(Messages.CopyToClipboardOperation_TaskName, (int) (fEndRank - fStartRank + 1));

        boolean needTab = false;
        for (TmfEventTableColumn column : fColumns) {
            if (needTab) {
                sb.append('\t');
            }
            sb.append(column.getHeaderName());
            needTab = true;
        }
        sb.append(LINE_SEPARATOR);

        copy(sb, monitor);

        Display.getDefault().syncExec(() -> {
            if (sb.length() == 0) {
                return;
            }
            Clipboard clipboard = new Clipboard(Display.getDefault());
            try {
                clipboard.setContents(new Object[] { sb.toString() },
                        new Transfer[] { TextTransfer.getInstance() });
            } catch (OutOfMemoryError e) {
                sb.setLength(0);
                sb.trimToSize();
                showErrorDialog();
            } finally {
                clipboard.dispose();
            }
        });

        monitor.done();
    }

    private IStatus copy(final StringBuilder sb, final IProgressMonitor monitor) {
        ITmfEventRequest request = new TmfEventRequest(ITmfEvent.class, TmfTimeRange.ETERNITY, fStartRank, (int) (fEndRank - fStartRank + 1), ExecutionType.FOREGROUND) {
            @Override
            public void handleData(ITmfEvent event) {
                super.handleData(event);
                if (monitor.isCanceled()) {
                    cancel();
                    return;
                }
                monitor.worked(1);
                if (fFilter == null || fFilter.matches(event)) {
                    try {
                        boolean needTab = false;
                        for (TmfEventTableColumn column : fColumns) {
                            if (needTab) {
                                sb.append('\t');
                            }
                            sb.append(column.getItemString(event));
                            needTab = true;
                        }
                        sb.append(LINE_SEPARATOR);
                    } catch (OutOfMemoryError e) {
                        sb.setLength(0);
                        sb.trimToSize();
                        showErrorDialog();
                        cancel();
                    }
                }
            }
        };
        fTrace.sendRequest(request);
        try {
            request.waitForCompletion();
        } catch (InterruptedException e) {
            Activator.getDefault().logError("Wait for completion interrupted for copy to clipboard ", e); //$NON-NLS-1$
            Thread.currentThread().interrupt();
        }
        return Status.OK_STATUS;
    }

    private static void showErrorDialog() {
        Display.getDefault().syncExec(() -> {
            Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
            MessageBox confirmOperation = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
            confirmOperation.setText(Messages.CopyToClipboardOperation_OutOfMemoryErrorTitle);
            confirmOperation.setMessage(Messages.CopyToClipboardOperation_OutOfMemoryErrorMessage);
            confirmOperation.open();
        });
    }
}
