/*******************************************************************************
 * Copyright (c) 2011, 2017 Ericsson and others
 *
 * 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:
 *   Mathieu Denis <mathieu.denis@polymtl.ca> - Generalized version based on LTTng
 *   Bernd Hufmann - Updated to use trace reference in TmfEvent and streaming
 *   Mathieu Denis - New request added to update the statistics from the selected time range
 *   Mathieu Denis - Generalization of the view to instantiate a viewer specific to a trace type
 *
 *******************************************************************************/

package org.eclipse.tracecompass.internal.tmf.ui.views.statistics;

import java.io.OutputStream;

import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.action.Action;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.internal.tmf.ui.commands.ExportToTsvAction;
import org.eclipse.tracecompass.internal.tmf.ui.viewers.statistics.TmfStatisticsViewer;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.ui.viewers.ITmfViewer;
import org.eclipse.tracecompass.tmf.ui.views.ITmfAllowMultiple;
import org.eclipse.tracecompass.tmf.ui.views.TmfView;
import org.eclipse.tracecompass.tmf.ui.widgets.tabsview.TmfViewerFolder;

/**
 * The generic Statistics View displays statistics for any kind of traces.
 *
 * It is implemented according to the MVC pattern. - The model is a
 * TmfStatisticsTreeNode built by the State Manager. - The view is built with a
 * TreeViewer. - The controller that keeps model and view synchronized is an
 * observer of the model.
 *
 * @author Mathieu Denis
 */
public class TmfStatisticsViewImpl extends TmfView implements ITmfAllowMultiple {

    /**
     * The view name.
     */
    public static final String TMF_STATISTICS_VIEW = "StatisticsView"; //$NON-NLS-1$

    /**
     * The viewer that builds the columns to show the statistics.
     */
    protected final TmfViewerFolder fStatsViewers;

    /**
     * Stores a reference to the selected trace.
     */
    private ITmfTrace fTrace;

    private final Action fExportAction = new ExportToTsvAction() {
        @Override
        protected void exportToTsv(@Nullable OutputStream stream) {
            ITmfViewer viewer = fStatsViewers.getViewer();
            if (viewer instanceof TmfStatisticsViewer) {
                ((TmfStatisticsViewer) viewer).exportToTsv(stream);
            }
        }

        @Override
        protected @Nullable Shell getShell() {
            return getViewSite().getShell();
        }

    };


    /**
     * Constructor of a statistics view.
     *
     * @param viewName The name to give to the view.
     */
    public TmfStatisticsViewImpl(String viewName) {
        super(viewName);
        /*
         * Create a fake parent for initialization purpose, than set the parent
         * as soon as createPartControl is called.
         */
        Composite temporaryParent = new Shell();
        fStatsViewers = new TmfViewerFolder(temporaryParent);
    }

    /**
     * Default constructor.
     */
    public TmfStatisticsViewImpl() {
        this(TMF_STATISTICS_VIEW);
    }

    @Override
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);
        fStatsViewers.setParent(parent);
        createStatisticsViewers();

        ITmfTrace trace = TmfTraceManager.getInstance().getActiveTrace();
        if (trace != null) {
            traceSelected(new TmfTraceSelectedSignal(this, trace));
        }
        getViewSite().getActionBars().getMenuManager().add(fExportAction);
    }

    @Override
    public void dispose() {
        super.dispose();
        fStatsViewers.dispose();
    }

    /**
     * Handler called when an trace is opened.
     *
     * @param signal
     *            Contains the information about the selection.
     */
    @TmfSignalHandler
    public void traceOpened(TmfTraceOpenedSignal signal) {
        /*
         * Dispose the current viewer and adapt the new one to the trace
         * type of the trace opened
         */
        fStatsViewers.clear();
        // Update the current trace
        fTrace = signal.getTrace();
        createStatisticsViewers();
        fStatsViewers.layout();
    }

    /**
     * Handler called when an trace is selected. Checks if the trace
     * has changed and requests the selected trace if it has not yet been
     * cached.
     *
     * @param signal
     *            Contains the information about the selection.
     */
    @TmfSignalHandler
    public void traceSelected(TmfTraceSelectedSignal signal) {
        // Does not reload the same trace if already opened
        if (signal.getTrace() != fTrace) {
            /*
             * Dispose the current viewer and adapt the new one to the trace
             * type of the trace selected
             */
            fStatsViewers.clear();
            // Update the current trace
            fTrace = signal.getTrace();
            createStatisticsViewers();
            fStatsViewers.layout();

            TmfTraceRangeUpdatedSignal updateSignal = new TmfTraceRangeUpdatedSignal(this, fTrace, fTrace.getTimeRange());

            for (ITmfViewer viewer : fStatsViewers.getViewers()) {
                TmfStatisticsViewer statsViewer = (TmfStatisticsViewer) viewer;
                statsViewer.sendPartialRequestOnNextUpdate();
                statsViewer.traceRangeUpdated(updateSignal);
            }
        } else {
            /*
             * If the same trace is reselected, sends a notification to
             * the viewers to make sure they reload correctly their partial
             * event count.
             */
            for (ITmfViewer viewer : fStatsViewers.getViewers()) {
                TmfStatisticsViewer statsViewer = (TmfStatisticsViewer) viewer;
                // Will update the partial event count if needed.
                statsViewer.sendPartialRequestOnNextUpdate();
            }
        }
    }

    /**
     * @param signal the incoming signal
     */
    @TmfSignalHandler
    public void traceClosed(TmfTraceClosedSignal signal) {
        if (signal.getTrace() != fTrace) {
            return;
        }

        // Clear the internal data
        fTrace = null;

        // Clear the UI widgets
        fStatsViewers.clear();  // Also cancels ongoing requests
        createStatisticsViewers();
        fStatsViewers.layout();
    }

    @Override
    public void setFocus() {
        fStatsViewers.setFocus();
    }

    /**
     * Creates the statistics viewers for all traces in an experiment and
     * populates a viewer folder. Each viewer is placed in a different tab and
     * the first one is selected automatically.
     *
     * It uses the extension point that defines the statistics viewer to build
     * from the trace type. If no viewer is defined, another tab won't be
     * created, since the global viewer already contains all the basic
     * statistics. If there is no trace selected, a global statistics viewer will
     * still be created.
     */
    protected void createStatisticsViewers() {
        // Default style for the tabs that will be created
        int defaultStyle = SWT.NONE;

        // The folder composite that will contain the tabs
        Composite folder = fStatsViewers.getParentFolder();

        // Instantiation of the global viewer
        if (fTrace != null) {
            // Shows the name of the trace in the global tab
            TmfStatisticsViewer globalViewer = new TmfStatisticsViewer(folder, Messages.TmfStatisticsView_GlobalTabName + " - " + fTrace.getName(), fTrace); //$NON-NLS-1$
            fStatsViewers.addTab(globalViewer, Messages.TmfStatisticsView_GlobalTabName, defaultStyle);

        } else {
            // There is no trace selected. Shows an empty global tab
            TmfStatisticsViewer globalViewer = new TmfStatisticsViewer(folder, Messages.TmfStatisticsView_GlobalTabName, fTrace);
            fStatsViewers.addTab(globalViewer, Messages.TmfStatisticsView_GlobalTabName, defaultStyle);
        }
        // Makes the global viewer visible
        fStatsViewers.setSelection(0);
    }
}
