"""
Helper class to track length of time taken for each frame and draw a graph when application exits.
Also able to add events at arbitrary times across the graph.
"""
import time
import matplotlib.pyplot as plt
import statistics


class FrametimePlotter:
    EVENT_POINT_Y = -0.05
    EVENT_MSG_Y = -0.045

    def __init__(self):
        self.times = []
        self.events = []
        self.start = time.perf_counter()

    def add_event(self, event_msg):
        self.events.append((len(self.times), event_msg))

    def end_frame(self, time_delta):
        self.times.append(time_delta)

    def _show_stats(self):
        end = time.perf_counter()
        print("Min   : {:.5f}".format(min(self.times)))
        print("Max   : {:.5f}".format(max(self.times)))
        print("Avg   : {:.5f}".format(statistics.mean(self.times)))
        print("Median: {:.5f}".format(statistics.median(self.times)))
        try:
            print("Mode  : {:.5f}".format(statistics.mode(self.times)))
        except statistics.StatisticsError as e:
            print("Mode  : {}".format(e))
        print("StdDev: {:.5f}".format(statistics.stdev(self.times)))
        frame_count = len(self.times)
        elapsed_time = end - self.start
        print("Frame count: {}".format(frame_count))
        print("Elapsed time: {:.5f}".format(elapsed_time))
        print("FPS: {:.5f}".format(frame_count / elapsed_time))

    def show(self):
        if len(self.times) <= 1:
            return
        self._show_stats()
        frame_idxs = range(0, len(self.times))
        event_idxs = [e[0] for e in self.events]
        event_point_y = [self.EVENT_POINT_Y] * len(self.events)
        plt.figure("Frame durations", figsize=(8, 6))
        plt.plot(frame_idxs, self.times, event_idxs, event_point_y, "k|")
        plt.xlabel("frames")
        plt.ylabel("frame duration")
        plt.ylim(self.EVENT_POINT_Y - 0.005, 0.5)
        plt.tight_layout()
        for frame_idx, msg in self.events:
            plt.text(frame_idx, self.EVENT_MSG_Y, msg, horizontalalignment="center", verticalalignment="bottom",
                     size="smaller", rotation="vertical")
        plt.show()
