import sys
import logging
import signal
import importlib

import gevent
import gevent.hub

from Config import config
from . import Debug

last_error = None

def shutdown(reason="Unknown"):
    logging.info("Shutting down (reason: %s)..." % reason)
    import main
    if "file_server" in dir(main):
        try:
            gevent.spawn(main.file_server.stop)
            if "ui_server" in dir(main):
                gevent.spawn(main.ui_server.stop)
        except Exception as err:
            print("Proper shutdown error: %s" % err)
            sys.exit(0)
    else:
        sys.exit(0)

# Store last error, ignore notify, allow manual error logging
def handleError(*args, **kwargs):
    global last_error
    if not args:  # Manual called
        args = sys.exc_info()
        silent = True
    else:
        silent = False
    if args[0].__name__ != "Notify":
        last_error = args

    if args[0].__name__ == "KeyboardInterrupt":
        shutdown("Keyboard interrupt")
    elif not silent and args[0].__name__ != "Notify":
        logging.exception("Unhandled exception")
        if "greenlet.py" not in args[2].tb_frame.f_code.co_filename:  # Don't display error twice
            sys.__excepthook__(*args, **kwargs)


# Ignore notify errors
def handleErrorNotify(*args, **kwargs):
    err = args[0]
    if err.__name__ == "KeyboardInterrupt":
        shutdown("Keyboard interrupt")
    elif err.__name__ != "Notify":
        logging.error("Unhandled exception: %s" % Debug.formatException(args))
        sys.__excepthook__(*args, **kwargs)


if config.debug:  # Keep last error for /Debug
    sys.excepthook = handleError
else:
    sys.excepthook = handleErrorNotify


# Override default error handler to allow silent killing / custom logging
if "handle_error" in dir(gevent.hub.Hub):
    gevent.hub.Hub._original_handle_error = gevent.hub.Hub.handle_error
else:
    logging.debug("gevent.hub.Hub.handle_error not found using old gevent hooks")
    OriginalGreenlet = gevent.Greenlet
    class ErrorhookedGreenlet(OriginalGreenlet):
        def _report_error(self, exc_info):
            sys.excepthook(exc_info[0], exc_info[1], exc_info[2])

    gevent.Greenlet = gevent.greenlet.Greenlet = ErrorhookedGreenlet
    importlib.reload(gevent)

def handleGreenletError(context, type, value, tb):
    if context.__class__ is tuple and context[0].__class__.__name__ == "ThreadPool":
        # Exceptions in ThreadPool will be handled in the main Thread
        return None

    if isinstance(value, str):
        # Cython can raise errors where the value is a plain string
        # e.g., AttributeError, "_semaphore.Semaphore has no attr", <traceback>
        value = type(value)

    if not issubclass(type, gevent.get_hub().NOT_ERROR):
        sys.excepthook(type, value, tb)

gevent.get_hub().handle_error = handleGreenletError

try:
    signal.signal(signal.SIGTERM, lambda signum, stack_frame: shutdown("SIGTERM"))
except Exception as err:
    logging.debug("Error setting up SIGTERM watcher: %s" % err)


if __name__ == "__main__":
    import time
    from gevent import monkey
    monkey.patch_all(thread=False, ssl=False)
    from . import Debug

    def sleeper(num):
        print("started", num)
        time.sleep(3)
        raise Exception("Error")
        print("stopped", num)
    thread1 = gevent.spawn(sleeper, 1)
    thread2 = gevent.spawn(sleeper, 2)
    time.sleep(1)
    print("killing...")
    thread1.kill(exception=Debug.Notify("Worker stopped"))
    #thread2.throw(Debug.Notify("Throw"))
    print("killed")
    gevent.joinall([thread1,thread2])
