#!/usr/bin/python

import re
import argparse

from collections import defaultdict

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('rootingHazards', nargs='?', default='rootingHazards.txt')
parser.add_argument('gcFunctions', nargs='?', default='gcFunctions.txt')
parser.add_argument('hazards', nargs='?', default='hazards.txt')
parser.add_argument('extra', nargs='?', default='unnecessary.txt')
parser.add_argument('refs', nargs='?', default='refs.txt')
args = parser.parse_args()

num_hazards = 0
num_refs = 0
try:
    with open(args.rootingHazards) as rootingHazards, \
        open(args.hazards, 'w') as hazards, \
        open(args.extra, 'w') as extra, \
        open(args.refs, 'w') as refs:
        current_gcFunction = None

        # Map from a GC function name to the list of hazards resulting from
        # that GC function
        hazardousGCFunctions = defaultdict(list)

        # List of tuples (gcFunction, index of hazard) used to maintain the
        # ordering of the hazards
        hazardOrder = []

        for line in rootingHazards:
            m = re.match(r'^Time: (.*)', line)
            mm = re.match(r'^Run on:', line)
            if m or mm:
                print >>hazards, line
                print >>extra, line
                print >>refs, line
                continue

            m = re.match(r'^Function.*has unnecessary root', line)
            if m:
                print >>extra, line
                continue

            m = re.match(r'^Function.*takes unsafe address of unrooted', line)
            if m:
                num_refs += 1
                print >>refs, line
                continue

            m = re.match(r"^Function.*has unrooted.*of type.*live across GC call ('?)(.*?)('?) at \S+:\d+$", line)
            if m:
                # Function names are surrounded by single quotes. Field calls
                # are unquoted.
                current_gcFunction = m.group(2)
                hazardousGCFunctions[current_gcFunction].append(line)
                hazardOrder.append((current_gcFunction, len(hazardousGCFunctions[current_gcFunction]) - 1))
                num_hazards += 1
                continue

            if current_gcFunction:
                if not line.strip():
                    # Blank line => end of this hazard
                    current_gcFunction = None
                else:
                    hazardousGCFunctions[current_gcFunction][-1] += line

        with open(args.gcFunctions) as gcFunctions:
            gcExplanations = {}  # gcFunction => stack showing why it can GC

            current_func = None
            explanation = None
            for line in gcFunctions:
                m = re.match(r'^GC Function: (.*)', line)
                if m:
                    if current_func:
                        gcExplanations[current_func] = explanation
                    current_func = None
                    if m.group(1) in hazardousGCFunctions:
                        current_func = m.group(1)
                        explanation = line
                elif current_func:
                    explanation += line
            if current_func:
                gcExplanations[current_func] = explanation

        for gcFunction, index in hazardOrder:
            gcHazards = hazardousGCFunctions[gcFunction]

            if gcFunction in gcExplanations:
                print >>hazards, (gcHazards[index] + gcExplanations[gcFunction])
            else:
                print >>hazards, gcHazards[index]

except IOError as e:
    print 'Failed: %s' % str(e)

print("Wrote %s" % args.hazards)
print("Wrote %s" % args.extra)
print("Wrote %s" % args.refs)
print("Found %d hazards and %d unsafe references" % (num_hazards, num_refs))
