from __future__ import annotations

import time

from pytools import DependentDictionary, Reference


class StopWatch:
    def __init__(self) -> None:
        self.Elapsed: float = 0
        self.LastStart: float | None = None

    def start(self) -> StopWatch:
        assert self.LastStart is None

        self.LastStart = time.time()
        return self

    def stop(self) -> StopWatch:
        assert self.LastStart is not None

        self.Elapsed += time.time() - self.LastStart
        self.LastStart = None
        return self

    def elapsed(self) -> float:
        if self.LastStart:
            return time.time() - self.LastStart + self.Elapsed
        return self.Elapsed


class Job:
    def __init__(self, name: str) -> None:
        self.Name: str = name
        self.StopWatch: StopWatch = StopWatch().start()

        if self.is_visible():
            print(f"{name}...")

    def done(self) -> None:
        elapsed = self.StopWatch.elapsed()

        JOB_TIMES[self.Name] += elapsed
        if self.is_visible():
            print(" " * (len(self.Name) + 2), elapsed, "seconds")

    def is_visible(self) -> bool:
        if PRINT_JOBS.get():
            return self.Name not in HIDDEN_JOBS
        return self.Name in VISIBLE_JOBS


class EtaEstimator:
    def __init__(self, total_steps: int) -> None:
        self.stopwatch: StopWatch = StopWatch().start()
        self.total_steps: int = total_steps
        assert total_steps > 0

    def estimate(self, done: int) -> float | None:
        fraction_done = done / self.total_steps
        time_spent = self.stopwatch.elapsed()

        if fraction_done > 1.0e-5:
            return time_spent / fraction_done - time_spent
        return None


def print_job_summary() -> None:
    for key, value in JOB_TIMES.iteritems():
        print(key, " " * (50 - len(key)), value)


HIDDEN_JOBS: list[str] = []
VISIBLE_JOBS: list[str] = []
JOB_TIMES = DependentDictionary[str, float](lambda d, x: 0.0)
PRINT_JOBS = Reference(True)
