/*******************************************************************************
 * Copyright (c) 2016, 2018 EfficiOS Inc., Alexandre Montplaisir
 *
 * 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.project.model;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.swt.graphics.Image;
import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModuleHelper;
import org.eclipse.tracecompass.tmf.core.analysis.TmfAnalysisManager;
import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType;
import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;

/**
 * Project model element for the "Views" node.
 *
 * For now it contains the list of the standard analyses, with their outputs
 * (views) under each. For experiments all analyses from children traces are
 * aggregated and shown under the "Views" node.
 *
 * The plan is to eventually only show the views under this node, since the
 * user cannot really interact with the analyses themselves.
 *
 * @author Alexandre Montplaisir
 * @since 2.0
 */
public class TmfViewsElement extends TmfProjectModelElement {

    /**
     * Element of the resource path
     */
    public static final String PATH_ELEMENT = ".views"; //$NON-NLS-1$

    private static final String ELEMENT_NAME = Messages.TmfViewsElement_Name;

    /**
     * Constructor
     *
     * @param resource
     *            The resource to be associated with this element
     * @param parent
     *            The parent element
     */
    protected TmfViewsElement(IResource resource, TmfCommonProjectElement parent) {
        super(ELEMENT_NAME, resource, parent);
    }

    // ------------------------------------------------------------------------
    // TmfProjectModelElement
    // ------------------------------------------------------------------------

    @Override
    public TmfCommonProjectElement getParent() {
        /* Type enforced at constructor */
        return (TmfCommonProjectElement) super.getParent();
    }

    @Override
    protected synchronized void refreshChildren() {
        /* Refreshes the analysis under this trace */
        Map<String, TmfAnalysisElement> childrenMap = new HashMap<>();
        for (TmfAnalysisElement analysis : getParent().getAvailableAnalysis()) {
            childrenMap.put(analysis.getAnalysisId(), analysis);
        }

        TraceTypeHelper helper = TmfTraceType.getTraceType(getParent().getTraceType());

        Class<@NonNull ? extends ITmfTrace> traceClass = null;

        if (helper != null) {
            traceClass = helper.getTraceClass();
        }

        /* Remove all analysis and return */
        if (traceClass == null) {
            for (TmfAnalysisElement analysis : childrenMap.values()) {
                removeChild(analysis);
            }
            return;
        }

        IPath nodePath = getResource().getFullPath();

        TmfCommonProjectElement parent = getParent();

        if (parent instanceof TmfTraceElement) {
            /* Add all new analysis modules or refresh outputs of existing ones */
            for (IAnalysisModuleHelper module : TmfAnalysisManager.getAnalysisModules(traceClass).values()) {

                /* If the analysis is not a child of the trace, create it */
                TmfAnalysisElement analysis = childrenMap.remove(module.getId());
                if (analysis == null) {
                    IFolder analysisRes = ResourcesPlugin.getWorkspace().getRoot().getFolder(nodePath.append(module.getId()));
                    analysis = new TmfAnalysisElement(module.getName(), analysisRes, this, module);
                    addChild(analysis);
                }
                analysis.refreshChildren();
            }

            /* Remove analysis that are not children of this trace anymore */
            for (TmfAnalysisElement analysis : childrenMap.values()) {
                removeChild(analysis);
            }
        } else if (parent != null) {
            /* In experiment case collect trace analyses in the aggregate analyses element */
            Map<String, TmfAggregateAnalysisElement> analysisMap = new HashMap<>();

            /* Add all new analysis modules or refresh outputs of existing ones */
            for (IAnalysisModuleHelper module : TmfAnalysisManager.getAnalysisModules(traceClass).values()) {

                /* If the analysis is not a child of the trace, create it */
                TmfAnalysisElement analysis = childrenMap.remove(module.getId());
                TmfAggregateAnalysisElement aggregateAnalysisElement = null;
                if (analysis == null) {
                    IFolder analysisRes = ResourcesPlugin.getWorkspace().getRoot().getFolder(nodePath.append(module.getId()));
                    analysis = new TmfAnalysisElement(module.getName(), analysisRes, this, module);
                    aggregateAnalysisElement = new TmfAggregateAnalysisElement(parent, analysis);
                    addChild(aggregateAnalysisElement);
                } else {
                    if (analysis instanceof TmfAggregateAnalysisElement) {
                        aggregateAnalysisElement = (TmfAggregateAnalysisElement) analysis;
                    } else {
                        aggregateAnalysisElement = new TmfAggregateAnalysisElement(parent, analysis);
                    }
                    removeChild(analysis);
                    addChild(aggregateAnalysisElement);
                }
                analysisMap.put(analysis.getAnalysisId(), aggregateAnalysisElement);
            }

            /* Now add all available trace analyses */
            for (TmfAnalysisElement analysis : getParent().getAvailableChildrenAnalyses()) {
                /* If the analysis is not a child of the trace, create it */
                TmfAnalysisElement a = childrenMap.remove(analysis.getAnalysisId());

                TmfAggregateAnalysisElement childAnalysis = null;

                if (a instanceof TmfAggregateAnalysisElement) {
                    childAnalysis = (TmfAggregateAnalysisElement) a;
                } else {
                    childAnalysis = analysisMap.get(analysis.getAnalysisId());
                }

                if (childAnalysis == null) {
                    childAnalysis = new TmfAggregateAnalysisElement(parent, analysis);
                    addChild(childAnalysis);
                } else {
                    childAnalysis.addAnalyses(analysis);
                }
                analysisMap.put(analysis.getAnalysisId(), childAnalysis);
            }

            /* Remove analysis that are not children of this trace anymore */
            for (TmfAnalysisElement analysis : childrenMap.values()) {
                removeChild(analysis);
            }
        }
    }

    @Override
    public Image getIcon() {
        return TmfProjectModelIcons.VIEWS_ICON;
    }

    /**
     * Remove children analysis from aggregated traces
     *
     * @param analysisElements
     *              list of analysis elements to remove
     *
     * @since 3.0
     */
    public void removeChildrenAnalysis(List<@NonNull TmfAnalysisElement> analysisElements) {
        for (TmfAnalysisElement tmfAnalysisElement : analysisElements) {
            TmfAggregateAnalysisElement aggrElement = getAggregateAnalysisElement(tmfAnalysisElement);
            if (aggrElement != null) {
                aggrElement.removeAnalyses(tmfAnalysisElement);
                if (aggrElement.isEmpty()) {
                    removeChild(aggrElement);
                }
            }
        }
    }

    private TmfAggregateAnalysisElement getAggregateAnalysisElement(TmfAnalysisElement element) {
        return getChildren().stream()
                .filter(TmfAggregateAnalysisElement.class::isInstance)
                .map(elem -> ((TmfAggregateAnalysisElement) elem))
                .filter(elem -> elem.getAnalysisHelper().getId().equals(element.getAnalysisHelper().getId()))
                .findFirst().orElse(null);
    }
}
