#!/usr/bin/env python

# Copyright (C) 2015 Mark Hahnenberg. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.

import fileinput
import re

TIMING_REGEX = re.compile(
    r'^\[(?P<pid>[0-9]+)\] '
    '(?P<name>[^ ]+) '
    '\(Parent: (?P<parent>[^\)]+)\) '
    '\((?P<collect_type>[^\)]+)\): '
    '(?P<total_time>[0-9]+\.[0-9]+)ms '
    '\(avg. (?P<avg_time>[^,]+), '
    'min. (?P<min_time>[^,]+), '
    'max. (?P<max_time>[^,]+), '
    'count (?P<count>[^\)]+)\)')

class Timing(object):
    def __init__(self, pid, name, parent, collect_type, total_time, avg_time, min_time, max_time, count):
        self.pid = int(pid)
        self.name = str(name)
        self.parent = str(parent)
        self.collect_type = str(collect_type)
        self.total_time = float(total_time)
        self.avg_time = float(avg_time)
        self.min_time = float(min_time)
        self.max_time = float(max_time)
        self.count = int(count)
        self.children = []

    def __unicode__(self):
        return u"%s - %s total: %.2f, avg: %.2f" % (self.name, self.collect_type, self.total_time, self.avg_time)

    def __str__(self):
        return "%s - %s total: %.2f, avg: %.2f" % (self.name, self.collect_type, self.total_time, self.avg_time)

    def __repr__(self):
        return "%s - %s total: %.2f, avg: %.2f" % (self.name, self.collect_type, self.total_time, self.avg_time)


def parse_input():
    timings = []
    for line in fileinput.input():
        result = TIMING_REGEX.match(line)
        if result is None:
            continue
        timings.append(Timing(
            result.group('pid'),
            result.group('name'),
            result.group('parent'),
            result.group('collect_type'),
            result.group('total_time'),
            result.group('avg_time'),
            result.group('min_time'),
            result.group('max_time'),
            result.group('count'),
        ))
    return timings


def print_timing_node(root, timings, tabs):
    for _ in range(tabs):
        print "    ",
    percent_time = 1.0
    if root.parent is not None:
        percent_time = float(root.total_time) / float(root.parent.total_time)
    print "%s - %.2f%%" % (str(root), percent_time * 100.0)
    for child in reversed(sorted(root.children, key=lambda t: t.total_time)):
        if child.parent != root:
            continue
        if child.collect_type != root.collect_type:
            continue
        print_timing_node(child, timings, tabs + 1)
    

def print_timing_tree(timings):
    timings.sort(key=lambda t: t.total_time)
    timings.reverse()
    collection_types = ["All", "Eden", "Full"]
    for collect_type in collection_types:
        for timing in timings:
            if timing.collect_type != collect_type:
                continue
            if timing.parent is not None:
                continue
            print_timing_node(timing, timings, 0)
        print ""


def link_parents(timings):
    for timing in timings:
        if timing.parent == "nullptr":
            timing.parent = None
            continue
        for parent in timings:
            if timing.parent != parent.name:
                continue
            if timing.collect_type != parent.collect_type:
                continue
            timing.parent = parent
            parent.children.append(timing)

def main():
    timings = parse_input()
    link_parents(timings)
    print_timing_tree(timings)

if __name__ == "__main__":
    main()
