#!/usr/bin/env python
'''
@file polling_thread_base.py

@brief A base class for repeat action thread

Copyright (C) Atmosic 2024
'''
import threading
import traceback
from time import sleep

from logging import getLogger

logger = getLogger(__name__)
POLLING_INTERVAL_S = 0.005

class PollingThreadBase(threading.Thread):
    is_any_thread_exception = False
    is_need_all_polling_thread_stopped = False

    def __init__(self,
                 group=None,
                 target=None,
                 name=None,
                 args=(),
                 kwargs=None,
                 *,
                 daemon=True):
        threading.Thread.__init__(
            self,
            group=group,
            target=target,
            name=name,
            args=args,
            kwargs=kwargs,
            daemon=daemon,
        )
        self._is_thread_not_stop = True

    def run(self):
        try:
            while self._is_thread_not_stop and \
                not PollingThreadBase.is_need_all_polling_thread_stopped:
                self.polling()
                sleep(POLLING_INTERVAL_S)
        except BaseException as e:
            logger.error(f"{self.__class__.__name__}: run failed: {e}",
                         exc_info=True)
            PollingThreadBase.is_any_thread_exception = True
        finally:
            logger.debug(f"{self.__class__.__name__} has been closed")
            if self._target:
                del self._target, self._args, self._kwargs

    def trigger_stop(self):
        self._is_thread_not_stop = False

    def start(self):
        try:
            super().start()
        except RuntimeError:
            # Thread cannot be started twice;
            #  recreate a fresh runner thread instance
            PollingThreadBase.__init__(self)
            super().start()

    def stop(self):
        try:
            self.trigger_stop()
            self.join()
            self.final()
            logger.debug(f"{self.__class__.__name__} has been closed")
        except Exception:
            err_log = \
                f"\n===== Exception {self.__class__.__name__}.stop =======\n"
            for s in traceback.format_exc().split("\n"):
                if s == "":
                    break
                err_log += s + "\n"
            err_log += \
                f"===== Exception {self.__class__.__name__}.stop END ==="
            logger.error(err_log)

    def polling(self):
        if self._target:
            self._target(*self._args, **self._kwargs)

    def final(self):
        pass
