// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


WebInspector.TimelineJSProfileProcessor = { };

/**
 * @param {!WebInspector.TimelineModelImpl} timelineModel
 * @param {!ProfilerAgent.CPUProfile} jsProfile
 */
WebInspector.TimelineJSProfileProcessor.mergeJSProfileIntoTimeline = function(timelineModel, jsProfile)
{
    if (!jsProfile.samples)
        return;
    var jsProfileModel = new WebInspector.CPUProfileDataModel(jsProfile);
    var idleNode = jsProfileModel.idleNode;
    var programNode = jsProfileModel.programNode;
    var gcNode = jsProfileModel.gcNode;

    /**
     * @param {!WebInspector.TimelineModel.Record} record
     */
    function processRecord(record)
    {
        if (record.type() !== WebInspector.TimelineModel.RecordType.FunctionCall &&
            record.type() !== WebInspector.TimelineModel.RecordType.EvaluateScript)
            return;
        var recordStartTime = record.startTime();
        var recordEndTime = record.endTime();
        var originalChildren = record.children().splice(0);
        var childIndex = 0;

        /**
         * @param {number} depth
         * @param {!ProfilerAgent.CPUProfileNode} node
         * @param {number} startTime
         */
        function onOpenFrame(depth, node, startTime)
        {
            if (node === idleNode || node === programNode || node === gcNode)
                return;
            var event = {
                type: "JSFrame",
                data: node,
                startTime: startTime
            };
            putOriginalChildrenUpToTime(startTime);
            record = new WebInspector.TimelineModel.RecordImpl(timelineModel, event, record);
        }

        /**
         * @param {number} depth
         * @param {!ProfilerAgent.CPUProfileNode} node
         * @param {number} startTime
         * @param {number} totalTime
         * @param {number} selfTime
         */
        function onCloseFrame(depth, node, startTime, totalTime, selfTime)
        {
            if (node === idleNode || node === programNode || node === gcNode)
                return;
            record.setEndTime(Math.min(startTime + totalTime, recordEndTime));
            record._selfTime = record.endTime() - record.startTime();
            putOriginalChildrenUpToTime(record.endTime());
            var deoptReason = node.deoptReason;
            if (deoptReason && deoptReason !== "no reason")
                record.addWarning(deoptReason);
            record = record.parent;
        }

        /**
         * @param {number} endTime
         */
        function putOriginalChildrenUpToTime(endTime)
        {
            for (; childIndex < originalChildren.length; ++childIndex)  {
                var child = originalChildren[childIndex];
                var midTime = (child.startTime() + child.endTime()) / 2;
                if (midTime >= endTime)
                    break;
                child.parent = record;
                record.children().push(child);
            }
        }

        jsProfileModel.forEachFrame(onOpenFrame, onCloseFrame, recordStartTime, recordEndTime);
        putOriginalChildrenUpToTime(recordEndTime);
    }

    timelineModel.forAllRecords(processRecord);
}

/**
 * @param {!WebInspector.TracingTimelineModel} timelineModel
 * @param {!ProfilerAgent.CPUProfile} jsProfile
 * @return {!Array.<!WebInspector.TracingModel.Event>}
 */
WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile = function(timelineModel, jsProfile)
{
    if (!jsProfile.samples)
        return [];
    var jsProfileModel = new WebInspector.CPUProfileDataModel(jsProfile);
    var idleNode = jsProfileModel.idleNode;
    var programNode = jsProfileModel.programNode;
    var gcNode = jsProfileModel.gcNode;
    var samples = jsProfileModel.samples;
    var timestamps = jsProfileModel.timestamps;
    var jsEvents = [];
    var mainThread = timelineModel.mainThreadEvents()[0].thread;
    for (var i = 0; i < samples.length; ++i) {
        var node = jsProfileModel.nodeByIndex(i);
        if (node === programNode || node === gcNode || node === idleNode)
            continue;
        var stackTrace = node._stackTraceArray;
        if (!stackTrace) {
            stackTrace = /** @type {!ConsoleAgent.StackTrace} */ (new Array(node.depth + 1));
            node._stackTraceArray = stackTrace;
            for (var j = 0; node.parent; node = node.parent)
                stackTrace[j++] = /** @type {!ConsoleAgent.CallFrame} */ (node);
        }
        var jsEvent = new WebInspector.TracingModel.Event(WebInspector.TracingModel.DevToolsMetadataEventCategory, WebInspector.TracingTimelineModel.RecordType.JSSample,
            WebInspector.TracingModel.Phase.Instant, timestamps[i], mainThread);
        jsEvent.stackTrace = stackTrace;
        jsEvents.push(jsEvent);
    }
    return jsEvents;
}
