/*******************************************************************************
 * Copyright (c) 2014, 2017 École Polytechnique de Montréal 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:
 *   Geneviève Bastien - Initial API and implementation
 *******************************************************************************/

package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.cpuusage;

import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tracecompass.analysis.os.linux.core.signals.TmfCpuSelectedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.ui.viewers.TmfViewer;
import org.eclipse.tracecompass.tmf.ui.viewers.xychart.TmfXYChartViewer;
import org.eclipse.tracecompass.tmf.ui.viewers.xychart.linechart.TmfXYChartSettings;
import org.eclipse.tracecompass.tmf.ui.views.xychart.TmfChartView;

/**
 * CPU usage view. It contains 2 viewers: one tree viewer showing all the
 * threads who were on the CPU in the time range, and one XY chart viewer
 * plotting the total time spent on CPU and the time of the threads selected in
 * the tree viewer.
 *
 * @author Geneviève Bastien
 */
public class CpuUsageView extends TmfChartView {

    /** ID string */
    public static final @NonNull String ID = "org.eclipse.tracecompass.analysis.os.linux.views.cpuusage"; //$NON-NLS-1$

    /** ID of the followed CPU in the map data in {@link TmfTraceContext} */
    public static final @NonNull String CPU_USAGE_FOLLOW_CPU = ID + ".FOLLOW_CPU"; //$NON-NLS-1$

    /*
     * To avoid up and downs CPU usage when process is in and out of CPU frequently,
     * use a smaller resolution to get better averages.
     */
    private static final double RESOLUTION = 0.4;

    /**
     * Constructor
     */
    public CpuUsageView() {
        super(Messages.CpuUsageView_Title);
    }

    @Override
    protected TmfXYChartViewer createChartViewer(Composite parent) {
        TmfXYChartSettings settings = new TmfXYChartSettings(Messages.CpuUsageXYViewer_Title, Messages.CpuUsageXYViewer_TimeXAxis, Messages.CpuUsageXYViewer_CpuYAxis, RESOLUTION);
        return new CpuUsageXYViewer(parent, settings);
    }

    @Override
    public TmfViewer createLeftChildViewer(Composite parent) {
        return new CpuUsageTreeViewer(parent);
    }

    /**
     * Save a data in the data map of {@link TmfTraceContext}
     */
    private static void saveData(@NonNull ITmfTrace trace, @NonNull String key, @NonNull Object data) {
        TmfTraceManager.getInstance().updateTraceContext(trace,
                builder -> builder.setData(key, data));
    }

    private static Object getData(@NonNull ITmfTrace trace, @NonNull String key) {
        TmfTraceContext ctx = TmfTraceManager.getInstance().getTraceContext(trace);
        return ctx.getData(key);
    }

    /**
     * Get CPUs from a trace
     *
     * @param trace
     *            the trace
     * @return the CPUs set
     */
    protected static @NonNull Set<@NonNull Integer> getCpus(@NonNull ITmfTrace trace) {
        Set<@NonNull Integer> data = (Set<@NonNull Integer>) getData(trace, CpuUsageView.CPU_USAGE_FOLLOW_CPU);
        return data != null ? data : Collections.emptySet();
    }

    @Override
    public void setFocus() {
        TmfXYChartViewer xyViewer = getChartViewer();
        if (xyViewer != null) {
            xyViewer.getControl().setFocus();
        }
    }

    /**
     * Signal handler for when a cpu is selected
     *
     * @param signal
     *            the cpu being selected
     * @since 2.0
     */
    @TmfSignalHandler
    public void cpuSelect(TmfCpuSelectedSignal signal) {
        ITmfTrace trace = signal.getTrace();
        TmfXYChartViewer xyViewer = getChartViewer();
        TmfViewer viewer = getLeftChildViewer();
        if (xyViewer instanceof CpuUsageXYViewer && viewer instanceof CpuUsageTreeViewer) {
            Set<Integer> data = (Set<Integer>) getData(trace, CPU_USAGE_FOLLOW_CPU);
            if (data == null) {
                data = new TreeSet<>();
                saveData(trace, CPU_USAGE_FOLLOW_CPU, data);
            }
            int core = signal.getCore();
            if (core >= 0) {
                data.add(core);
            } else {
                data.clear();
            }
            xyViewer.refresh();
            ((CpuUsageXYViewer) xyViewer).setTitle();

            CpuUsageTreeViewer treeViewer = (CpuUsageTreeViewer) viewer;
            treeViewer.updateContent(treeViewer.getWindowStartTime(), treeViewer.getWindowEndTime(), false);
        }
    }
}
