# This file defines an interface and client-side API stub
# for referring either to the core Ray API or the same interface
# from the Ray client.
#
# In tandem with __init__.py, we want to expose an API that's
# close to `python/ray/__init__.py` but with more than one implementation.
# The stubs in __init__ should call into a well-defined interface.
# Only the core Ray API implementation should actually `import ray`
# (and thus import all the raylet worker C bindings and such).
# But to make sure that we're matching these calls, we define this API.

from abc import ABC
from abc import abstractmethod


class APIImpl(ABC):
    @abstractmethod
    def get(self, *args, **kwargs):
        pass

    @abstractmethod
    def put(self, *args, **kwargs):
        pass

    @abstractmethod
    def wait(self, *args, **kwargs):
        pass

    @abstractmethod
    def remote(self, *args, **kwargs):
        pass

    @abstractmethod
    def call_remote(self, instance, kind: int, *args, **kwargs):
        pass

    @abstractmethod
    def get_actor_from_object(self, id):
        pass

    @abstractmethod
    def close(self, *args, **kwargs):
        pass


class ClientAPI(APIImpl):
    def __init__(self, worker):
        self.worker = worker

    def get(self, *args, **kwargs):
        return self.worker.get(*args, **kwargs)

    def put(self, *args, **kwargs):
        return self.worker.put(*args, **kwargs)

    def wait(self, *args, **kwargs):
        return self.worker.wait(*args, **kwargs)

    def remote(self, *args, **kwargs):
        return self.worker.remote(*args, **kwargs)

    def call_remote(self, f, kind, *args, **kwargs):
        return self.worker.call_remote(f, kind, *args, **kwargs)

    def get_actor_from_object(self, id):
        raise Exception("Calling get_actor_from_object on the client side")

    def close(self, *args, **kwargs):
        return self.worker.close()

    def __getattr__(self, key: str):
        if not key.startswith("_"):
            raise NotImplementedError(
                "Not available in Ray client: `ray.{}`. This method is only "
                "available within Ray remote functions and is not yet "
                "implemented in the client API.".format(key))
        return self.__getattribute__(key)
