from typing import Optional
from typing import Tuple
import time


def progress_data(start_time: Optional[float], iteration: Optional[int], total_iterations: Optional[int]) \
        -> Tuple[Optional[Tuple[int, int, int, int]], Optional[float]]:
    """
    Calculate elapsed time and progress.

    :param start_time:       Used for the elapsed time calculation. "Now" time point is :func:`time.perf_counter()`.
    :param iteration:        Used for progress calculation together with `total_iterations`. As this is meant as
                             and "iteration index", 1 is added before percentage calculation.
    :param total_iterations: Used for progress calculation together with `iteration`.

    :return: Tuple(elapsed time - hours; elapsed time - minutes; elapsed time - seconds; elapsed time - milliseconds)
             if start time was provided, else `None`; iteration percentage, if `iteration` and `total_iterations`
             were provided, else `None`)
    """
    if start_time is not None:
        elapsed_time = time.perf_counter() - start_time
        elapsed_time_ms = 1000.0 * (elapsed_time - int(elapsed_time))
        elapsed_time_h, elapsed_time_m = divmod(elapsed_time, 60 * 60)
        elapsed_time_m, elapsed_time_s = divmod(elapsed_time_m, 60)
        elapsed_time = (int(elapsed_time_h), int(elapsed_time_m), int(elapsed_time_s), int(elapsed_time_ms))
    else:
        elapsed_time = None

    if (iteration is not None) and (total_iterations is not None):
        # Iterations are mostly indexes ranging from [0, total_iterations).
        progress = 100.0 * (iteration + 1) / total_iterations
    else:
        progress = None

    return elapsed_time, progress