from __future__ import annotations
import warnings
from typing import Tuple, Set, Dict, List, NamedTuple, Optional, TYPE_CHECKING
import krpc.limits
import krpc.schema
from krpc.schema import KRPC_pb2
from krpc.types import TypeBase, ClassBase, WrappedClass, DocEnum, StaticMethod
from krpc.event import Event
if TYPE_CHECKING:
    from krpc.services import Client
from krpc.services import serviceb as _service_serviceb


# The classes, enumerations, structures and exceptions put on the service in
# __init__, declared for type checkers
if TYPE_CHECKING:

    class _ServiceADefinitions:
        pass
else:
    _ServiceADefinitions = object


class ServiceA(_ServiceADefinitions):
    """
    A service whose procedures use another service's enumeration.
    """

    def __init__(self, client: Client) -> None:
        self._client = client
        # The classes, enumerations, structures and exceptions the service defines are
        # put on the service here, so that reaching one is an ordinary attribute lookup
        # and nothing has to intercept the lookups that do not name one - which is every
        # call, as a call goes through a procedure or a property. A class is bound to the
        # client it is reached through, so that its static methods use that client.
        self.__dict__.update(self._enumerations)
        self.__dict__.update(self._structs)
        self.__dict__.update(self._exceptions)
        for name, class_type in self._classes.items():
            self.__dict__[name] = WrappedClass(client, class_type)

    # Hidden from type checkers, so that they report a name the service does not define
    if not TYPE_CHECKING:

        def __getattr__(self, name):
            # Reached only once the ordinary lookup has failed, which for a service means
            # either a name it does not define, or one it does whose procedure or property
            # raised an AttributeError of its own. Python discards that error before calling
            # this, so it cannot be reported here; what can be done is to not report a member
            # that does exist as missing, which would point at the wrong thing entirely.
            cls = type(self)
            if hasattr(cls, name):
                raise AttributeError(
                    "'%s' object attribute '%s' raised AttributeError"
                    % (cls.__name__, name)
                )
            raise AttributeError(
                "'%s' object has no attribute '%s'" % (cls.__name__, name)
            )

    _classes = {
    }
    _enumerations = {
    }
    _structs = {
    }
    _exceptions = {
    }

    def frob(self, mode: _service_serviceb.Mode = _service_serviceb.Mode.fast) -> None:
        """
        Defaults to a member of another service's enumeration.
        """
        return self._client._invoke(
            "ServiceA",
            "Frob",
            [mode],
            [self._client._types.enumeration_type("ServiceB", "Mode")],
            None
        )

    def _return_type_frob(self) -> TypeBase:
        return None

    def _build_call_frob(self, mode: _service_serviceb.Mode = _service_serviceb.Mode.fast) -> KRPC_pb2.ProcedureCall:
        return self._client._build_call(
            "ServiceA",
            "Frob",
            [mode],
            [self._client._types.enumeration_type("ServiceB", "Mode")],
            None
        )

    def frob_all(self, modes: List[_service_serviceb.Mode] = [_service_serviceb.Mode.fast]) -> None:
        """
        Defaults to a collection containing a member of another service's enumeration.
        """
        return self._client._invoke(
            "ServiceA",
            "FrobAll",
            [modes],
            [self._client._types.list_type(self._client._types.enumeration_type("ServiceB", "Mode"))],
            None
        )

    def _return_type_frob_all(self) -> TypeBase:
        return None

    def _build_call_frob_all(self, modes: List[_service_serviceb.Mode] = [_service_serviceb.Mode.fast]) -> KRPC_pb2.ProcedureCall:
        return self._client._build_call(
            "ServiceA",
            "FrobAll",
            [modes],
            [self._client._types.list_type(self._client._types.enumeration_type("ServiceB", "Mode"))],
            None
        )
