#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#
# Copyright (c) 2026 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import sys
import time
import itertools
from functools import wraps
from multiprocessing import Process, Event

def _spinner_worker(stop_event, message):
    """
    Independent worker function for the spinner process.
    This runs in a separate process with its own GIL.
    """
    frames = itertools.cycle(['|', '/', '-', '\\'])
    try:
        while not stop_event.is_set():
            # \033[K clears the line from the cursor to the end
            sys.stdout.write(f"\r{message} {next(frames)}\033[K")
            sys.stdout.flush()
            time.sleep(0.08)
    finally:
        # Final cleanup: clear the line before exiting the process
        sys.stdout.write("\r\033[K")
        sys.stdout.flush()


def _cleanup_spinner_process(p, stop_event):
    stop_event.set()
    p.join(timeout=0.5)
    if p.is_alive():
        p.terminate()


def progress_spinner(message="Process..."):
    """
    A multiprocess-based spinner decorator to prevent stuttering
    during CPU-intensive tasks.
    Only shows progress when output is to a terminal.
    """

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # Check if output is to a terminal (not redirected to file)
            is_terminal = sys.stdout.isatty()

            # If not terminal, just execute function without spinner
            if not is_terminal:
                return func(*args, **kwargs)

            # Event for inter-process communication
            stop_event = Event()

            # Initialize the spinner process
            # Using a separate process bypasses the GIL limitations
            p = Process(target=_spinner_worker, args=(stop_event, message))
            p.daemon = True
            p.start()

            try:
                # Execute the heavy function in the main process
                return func(*args, **kwargs)
            finally:
                # Signal the process to stop and wait for cleanup
                _cleanup_spinner_process(p, stop_event)
        return wrapper
    return decorator