import sys
import threading
import time
from typing import Callable


class Progress:
    delay: float = 0.1

    @staticmethod
    def spinning_cursor():
        while True:
            yield from "|/-\\"

    def __init__(self, *, message=""):
        self.message = message
        self.spinner_generator = self.spinning_cursor()

    def spinner_iteration(self):
        message = self.message
        sys.stdout.write(next(self.spinner_generator) + " " + message)
        sys.stdout.flush()
        time.sleep(self.delay)
        # clear the spinner and message
        sys.stdout.write(
            "\b" * (len(message) + 2)
            + " " * (len(message) + 2)
            + "\b" * (len(message) + 2)
        )
        sys.stdout.flush()

    def spinner_task(self):
        while self.message:
            message = self.message
            sys.stdout.write(next(self.spinner_generator) + " " + message)
            sys.stdout.flush()
            time.sleep(self.delay)
            # clear the spinner and message
            sys.stdout.write(
                "\b" * (len(message) + 2)
                + " " * (len(message) + 2)
                + "\b" * (len(message) + 2)
            )
            sys.stdout.flush()

    def __enter__(self) -> Callable[[str], None]:
        self.thread = threading.Thread(target=self.spinner_task)
        self.thread.start()

        def set_message(message):
            self.message = message
            if not message:
                self.thread.join()

        return set_message

    def __exit__(self, exception, value, tb):
        self.message = ""
        try:
            self.thread.join()
        finally:
            del self.thread
        if exception is not None:
            return False
