/*******************************************************************************
 * Copyright (c) 2009, 2018 STMicroelectronics and others.
 * 
 * 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:
 *   Xavier Raynaud <xavier.raynaud@st.com> - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.gprof.parser;

import java.io.DataInput;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.core.IAddressFactory;
import org.eclipse.cdt.core.IBinaryParser.IBinaryObject;
import org.eclipse.cdt.core.IBinaryParser.ISymbol;
import org.eclipse.linuxtools.internal.gprof.symbolManager.CallGraphArc;
import org.eclipse.linuxtools.internal.gprof.symbolManager.CallGraphNode;
import org.eclipse.linuxtools.internal.gprof.view.histogram.HistRoot;


/**
 * This class in on charge of parsing the call graph section
 * of gmon files
 * @author Xavier Raynaud <xavier.raynaud@st.com>
 */
public class CallGraphDecoder {

    protected final GmonDecoder decoder;


    private final Map<ISymbol, CallGraphNode> nodes = new HashMap<>();

    /**
     * Constructor
     * @param decoder the Gmon decoder
     */
    public CallGraphDecoder(GmonDecoder decoder) {
        this.decoder = decoder;
    }

    /**
     * Decode call-graph record from gmon file.
     * @param stream
     * @throws IOException
     */
    public void decodeCallGraphRecord(DataInput stream, boolean bsdFormat) throws IOException {
        long from_pc = readAddress(stream);
        long self_pc = readAddress(stream);
        int count    = bsdFormat?(int)readAddress(stream):stream.readInt();
        IBinaryObject program = decoder.getProgram();
        IAddressFactory addressFactory = program.getAddressFactory();
        IAddress parentAddress = addressFactory.createAddress(Long.toString(from_pc));
        ISymbol  parentSymbol  = program.getSymbol(parentAddress);
        IAddress childAddress  = addressFactory.createAddress(Long.toString(self_pc));
        ISymbol  childSymbol   = program.getSymbol(childAddress);
        if (childSymbol == null || parentSymbol == null) {
            return;
        }
        addCallArc(parentSymbol, parentAddress, childSymbol, count);
    }


    protected long readAddress(DataInput stream) throws IOException {
        return stream.readInt() & 0xFFFFFFFFL;
    }


    private void addCallArc(ISymbol parent, IAddress parentAddress, ISymbol child, int count) {
        CallGraphNode parentNode = nodes.get(parent);
        CallGraphNode childNode  = nodes.get(child);
        if (parentNode == null) {
            parentNode = new CallGraphNode(parent);
            nodes.put(parent, parentNode);
        }
        if (childNode == null) {
            childNode = new CallGraphNode(child);
            nodes.put(child, childNode);
        }
        CallGraphArc arc = parentNode.getOutputArc(childNode);
        if (arc == null) {
            arc = new CallGraphArc(parentNode, parentAddress, childNode, count, decoder.getProgram(), decoder.getProject());
            parentNode.getChildren().add(arc);
            childNode.getParents().add(arc);
        } else {
            arc.setCount(arc.getCount() + count);
        }
    }

    void populate(HistRoot rootNode) {
        for (CallGraphNode callGraphNode : nodes.values()) {
            rootNode.addCallGraphNode(callGraphNode);
        }
    }


}
