/*******************************************************************************
 * Copyright (c) 2009, 2018 Red Hat, Inc.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Elliott Baron <ebaron@redhat.com> - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.valgrind.cachegrind.model;

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

import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.IFunction;
import org.eclipse.cdt.core.model.IMethod;
import org.eclipse.cdt.core.model.IParent;
import org.eclipse.cdt.core.model.IStructure;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;

public class CachegrindFunction implements ICachegrindElement {
    private CachegrindFile parent;
    private String name;
    private List<CachegrindLine> lines;
    private long[] totals;

    private IAdaptable model;

    private static final String SCOPE_RESOLUTION = "::"; //$NON-NLS-1$

    public CachegrindFunction(CachegrindFile parent, String name) {
        this.parent = parent;
        this.name = name;
        lines = new ArrayList<>();

        IAdaptable pModel = parent.getModel();
        if (pModel instanceof ICElement) {
            ICElement element = (ICElement) pModel;
            try {
                if (element instanceof ITranslationUnit) {
                    // Cachegrind labels parameter types for C++ methods
                    int paramIndex = name.indexOf('(');
                    if (paramIndex >= 0) {
                        name = name.substring(0, paramIndex);
                    }

                    model = findElement(name, (IParent) element);
                    if (model == null) {
                        while (name.contains(SCOPE_RESOLUTION)) {
                            String[] parts = name.split(SCOPE_RESOLUTION, 2);
                            String structureName = parts[0];
                            name = parts[1];

                            List<ICElement> dom = ((IParent) element).getChildrenOfType(ICElement.C_CLASS);
                            dom.addAll(((IParent) element).getChildrenOfType(ICElement.C_STRUCT));
                            dom.addAll(((IParent) element).getChildrenOfType(ICElement.C_UNION));
                            for (int i = 0; i < dom.size(); i++) {
                                ICElement e = dom.get(i);
                                if (e instanceof IStructure && e.getElementName().equals(structureName)) {
                                    element = e;
                                }
                            }
                        }
                        model = findElement(name, (IParent) element);
                    }
                }
            } catch (CoreException e) {
                e.printStackTrace();
            }
        }
    }

    private ICElement findElement(String name, IParent parent)
    throws CModelException {
        ICElement element = null;
        List<ICElement> dom = parent.getChildrenOfType(ICElement.C_FUNCTION);
        dom.addAll(parent.getChildrenOfType(ICElement.C_METHOD));
        for (int i = 0; i < dom.size(); i++) {
            ICElement func = dom.get(i);
            if ((func instanceof IFunction || func instanceof IMethod) && func.getElementName().equals(name)) {
                element = func;
            }
        }
        return element;
    }

    public void addLine(CachegrindLine line) {
        long[] values = line.getValues();
        if (totals == null) {
            totals = new long[values.length];
        }
        for (int i = 0; i < values.length; i++) {
            totals[i] += values[i];
        }
        lines.add(line);
    }

    public String getName() {
        return name;
    }

    @Override
    public IAdaptable getModel() {
        return model;
    }

    public long[] getTotals() {
        return totals;
    }

    public CachegrindLine[] getLines() {
        return lines.toArray(new CachegrindLine[lines.size()]);
    }

    @Override
    public ICachegrindElement[] getChildren() {
        ICachegrindElement[] children = null;
        // if there is only a summary don't return any children
        if (lines.get(0).getLine() > 0) {
            children = getLines();
        }
        return children;
    }

    @Override
    public ICachegrindElement getParent() {
        return parent;
    }

    @Override
    public int compareTo(ICachegrindElement o) {
        int result = 0;
        if (o instanceof CachegrindFunction) {
            result = name.compareTo(((CachegrindFunction) o).getName());
        }
        return result;
    }

}
