#/bin/env python3
from distutils.log import error
from statistics import mean, median
import sys
from matplotlib import pyplot as p
from mailbox import linesep
import os
import re

from numpy import std

MEASUREMENT_TAG = "MEASUREMENT"
# searches for a measurement with #1 name, #2 tag, #3 time
MEASUREMENT_REGEX = r"^.* " + MEASUREMENT_TAG + r" ([a-zA-Z_:-]*) ([0-9]*)$"

# TODO change this for the actual measurement
# data_basedir = os.environ.get("BFBUILD_QEMU")
data_basedir = os.environ.get("BFBUILD")
data_file = os.path.join(data_basedir, "full_output.log")
out_dir = os.path.join(os.path.dirname(sys.argv[0]), "plots")

def extract_measurement(line):
    match = re.match(MEASUREMENT_REGEX, line)
    if not match:
        print(f"[ERROR] Malformed measurement: '{line}'")
        exit(1)
    return {
        "tag": match.group(1),
        "timestamp": int(match.group(2)),
    }

def extract_measurements(lines):
    filtered = filter(lambda l: MEASUREMENT_TAG in l, lines)
    return list(map(extract_measurement, filtered))

def read_data(file):
    with open(file) as f:
        return f.readlines()
        
def build_dataseries(measurements, start_tag, end_tag):
    datapoints = []
    measurements = list(filter(lambda m: start_tag == m["tag"] or end_tag == m["tag"], measurements))

    started_at = None
    for m in measurements:
        if started_at is None:
            if m["tag"] == start_tag:
                started_at = m["timestamp"]
            elif m["tag"] == end_tag:
                print(f"[ERROR] Got end tag without start {end_tag}")
        else:
            if m["tag"] == end_tag:
                datapoints.append(m["timestamp"] - started_at)
                started_at = None
            elif m["tag"] == start_tag:
                print(f"[ERROR] Invalid sequence, ignoring last start tag {start_tag}")
                started_at = m["timestamp"]
    
    return datapoints


def main():
    raw_log = read_data(data_file)

    measurements = extract_measurements(raw_log)
    measurements.sort(key=lambda m: m["timestamp"])

    dataset = {}

    # dataset["performance"] = build_dataseries(measurements, "aos_performance:start", "aos_performance:done")

    # URPC
    # dataset["client_to_server"] = build_dataseries(measurements, "aos_urpc_nop:start", "aos_urpc_server:start")
    # dataset["server_schedule_task"] = build_dataseries(measurements, "aos_urpc_server:start", "aos_urpc_server:triggered_closure")
    # dataset["server_completed_task"] = build_dataseries(measurements, "aos_urpc_server:triggered_closure", "aos_urpc_server:done")
    # dataset["server_to_client"] = build_dataseries(measurements, "aos_urpc_server:done", "aos_urpc_nop:done")

    # block driver
    dataset["block_driver_read_device"] = build_dataseries(measurements, "block_driver_read_object:start", "block_driver_read_object:memcpy")
    dataset["block_driver_read_memcpy"] = build_dataseries(measurements, "block_driver_read_object:memcpy", "block_driver_read_object:done")
    dataset["block_driver_write_read"] = build_dataseries(measurements, "block_driver_write_object:start", "block_driver_write_object:read")
    dataset["block_driver_write_memcpy"] = build_dataseries(measurements, "block_driver_write_object:read", "block_driver_write_object:memcpy")
    dataset["block_driver_write_device"] = build_dataseries(measurements, "block_driver_write_object:memcpy", "block_driver_write_object:done")

    # calculate stats for each data series
    metrics = {}
    for key in dataset:
        d_mean = mean(dataset[key])
        d_std = std(dataset[key])
        d_median = median(dataset[key])
        metrics[key] = (d_mean, d_std)

    # create output directory
    os.makedirs(out_dir, exist_ok=True)

    # create plots for data series
    for key in dataset:
        d = dataset[key]
        p.clf()
        p.title(key)
        p.xlabel("datapoint index")
        p.ylabel("duration (cycles)")
        p.plot(range(len(d)), d)
        p.savefig(os.path.join(out_dir, f"{key}.jpg"))
    
    p.clf()
    fig, ax = p.subplots(figsize=(12,5))
    p.title("Metrics")
    p.ylabel("median duration (cycles)")
    p.bar(
        metrics.keys(), 
        [x[0] for x in metrics.values()], 
        yerr=[x[1] for x in metrics.values()],
    )
    ax.set_ylim(0)
    p.savefig(os.path.join(out_dir, "metrics.jpg"))

if __name__ == "__main__":
    main()
