#!/usr/bin/python

import csv
import os
import os.path

def changed_significantly(old, new):
    if old == new: return False
    diff = abs(new-old)
    if diff < 0.3: return False
    if old == 0 or new == 0: return True
    return diff/old > 0.03

def format_diff(old, new):
    if new == old: return "no change"
    if old == 0: return "+%.2f (from 0.00)" % new
    if new == 0: return "-%.2f (down to 0.00)" % old
    diff = new - old
    perc = diff / old
    return "%.2f (%+.2f, %+.2f%%)" % (new, diff, perc * 100)

class Stats(object):
    def __init__(self, utime=0, stime=0):
        self.utime = utime
        self.stime = stime

    def add(self, utime, stime):
        self.utime += utime
        self.stime += stime

    def diff(self, name, prev, print_always=False):
        """
        Print differences from a previous run
        """
        if changed_significantly(prev.utime, self.utime) or print_always:
            if changed_significantly(prev.stime, self.stime) or print_always:
                print "%s: user: %s sys: %s" % (
                    name,
                    format_diff(prev.utime, self.utime),
                    format_diff(prev.stime, self.stime))
            else:
                print "%s: user: %s" % (
                    name,
                    format_diff(prev.utime, self.utime))
        elif changed_significantly(prev.stime, self.stime):
            print "%s: sys: %s" % (
                name,
                format_diff(prev.stime, self.stime))

class Suite(object):
    def __init__(self, name):
        self.name = name
        self.stats = Stats()
        self.sub = {}

    def add_result(self, names, utime, stime):
        self.stats.add(utime, stime)
        for n in names + [".".join(names)]:
            if n in self.sub:
                self.sub[n].add(utime, stime)
            else:
                self.sub[n] = Stats(utime, stime)

    def diff(self, prev):
        """
        Print differences from a previous run
        """
        self.stats.diff(self.name, prev.stats, print_always=True)
        names = set(self.sub.iterkeys())
        names.update(prev.sub.iterkeys())
        for n in sorted(names):
            if n not in prev.sub:
                print "%s.%s: new test appeared" % (self.name, n)
            elif n not in self.sub:
                print "%s.%s: old test disappeared" % (self.name, n)
            else:
                self.sub[n].diff("%s.%s" % (self.name, n), prev.sub[n])


class Suites(object):
    """
    All results of a benchmark run
    """
    def __init__(self, name):
        self.name = name
        self.suites = {}

    def add_result(self, suite_name, names, utime, stime):
        if suite_name not in self.suites:
            suite = Suite(suite_name)
            self.suites[suite_name] = suite
        else:
            suite = self.suites[suite_name]
        suite.add_result(names, utime, stime)

    def diff(self, prev):
        """
        Print differences from a previous run
        """
        all_keys = set(self.suites.iterkeys())
        all_keys.update(prev.suites.iterkeys())
        for k in sorted(all_keys):
            if k not in prev.suites:
                print "%s: new suite appeared" % k
            elif k not in self.suites:
                print "%s: old suite disappeared" % k
            else:
                self.suites[k].diff(prev.suites[k])


def last_two_files():
    # Find all .csv files
    files = [fn for fn in os.listdir("src/bench") if fn.endswith(".csv")]
    files.sort(key=lambda fn:int(fn.split("-")[0]))
    return files[-2:]

def read_file(fn):
    suites = Suites(fn)
    pathname = os.path.join("src/bench", fn)
    with open(pathname) as fd:
        reader = csv.reader(fd)
        for idx, row in enumerate(reader):
            # Skip titles
            if idx == 0: continue
            sname, name, utime, stime = row
            # Mangle suite names differently than found in CSV: take only the
            # toplevel suite name and use all other names as if they were
            # categories
            snames = sname.split(".")
            suite_name = snames.pop(0)
            snames.append(name)
            suites.add_result(suite_name, snames, float(utime), float(stime))
    return suites

fn_old, fn_new = last_two_files()
suites_old = read_file(fn_old)
suites_new = read_file(fn_new)

suites_new.diff(suites_old)
