# Copyright 2018-2021 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

__all__ = (
    "AbstractChildWatcher",
    "DefaultEventLoopPolicy",
)

import asyncio as _real_asyncio
from asyncio import events
from asyncio.unix_events import AbstractChildWatcher

import fcntl
import os

from portage.util._eventloop.global_event_loop import (
    global_event_loop as _global_event_loop,
)


if hasattr(os, "set_blocking"):

    def _set_nonblocking(fd):
        os.set_blocking(fd, False)

else:

    def _set_nonblocking(fd):
        flags = fcntl.fcntl(fd, fcntl.F_GETFL)
        flags = flags | os.O_NONBLOCK
        fcntl.fcntl(fd, fcntl.F_SETFL, flags)


class _PortageEventLoopPolicy(events.AbstractEventLoopPolicy):
    """
    Implementation of asyncio.AbstractEventLoopPolicy based on portage's
    internal event loop. This supports running event loops in forks,
    which is not supported by the default asyncio event loop policy,
    see https://bugs.python.org/issue22087.
    """

    def get_event_loop(self):
        """
        Get the event loop for the current context.

        Returns an event loop object implementing the AbstractEventLoop
        interface.

        @rtype: asyncio.AbstractEventLoop (or compatible)
        @return: the current event loop policy
        """
        return _global_event_loop()._asyncio_wrapper

    def get_child_watcher(self):
        """Get the watcher for child processes."""
        return _global_event_loop()._asyncio_child_watcher


class _AsyncioEventLoopPolicy(_PortageEventLoopPolicy):
    """
    A subclass of _PortageEventLoopPolicy which raises
    NotImplementedError if it is set as the real asyncio event loop
    policy, since this class is intended to *wrap* the real asyncio
    event loop policy.
    """

    def _check_recursion(self):
        if _real_asyncio.get_event_loop_policy() is self:
            raise NotImplementedError("this class is only a wrapper")

    def get_event_loop(self):
        self._check_recursion()
        return super().get_event_loop()

    def get_child_watcher(self):
        self._check_recursion()
        return super().get_child_watcher()


DefaultEventLoopPolicy = _AsyncioEventLoopPolicy
