# Copyright (C) 2021-2023 Modin authors
#
# SPDX-License-Identifier: Apache-2.0

"""An implementation of ``Backend`` interface using Python Sequential backend."""

import socket

import unidist.core.backends.pyseq.core as py
from unidist.core.backends.pyseq.actor import PySeqActor
from unidist.core.backends.pyseq.remote_function import (
    PySeqRemoteFunction,
)
from unidist.core.base.backend import Backend


class PySeqBackend(Backend):
    """The class that implements the interface in ``Backend`` using Python Sequential backend."""

    @staticmethod
    def make_remote_function(function, num_cpus, num_returns, resources):
        """
        Define ``PySeqRemoteFunction``.

        function : callable
            Function to be ``PySeqRemoteFunction``.
        num_cpus : int
            The number of CPUs to reserve for ``PySeqRemoteFunction``.
        num_returns : int
            The number of ``ObjectRef``-s returned by the function invocation.
        resources : dict
            Custom resources to reserve for the function.

        Returns
        -------
        PySeqRemoteFunction
        """
        return PySeqRemoteFunction(function, num_cpus, num_returns, resources)

    @staticmethod
    def make_actor(cls, num_cpus, resources):
        """
        Define an actor class.

        cls : object
            Class to be an actor class.
        num_cpus : int
            The number of CPUs to reserve for the lifetime of the actor.
        resources : dict
            Custom resources to reserve for the lifetime of the actor.

        Returns
        -------
        PySeqActor
            The actor class type to create.
        list
            The list of arguments for ``PySeqActor`` constructor.
        """
        return PySeqActor, [cls, num_cpus, resources]

    @staticmethod
    def get(data_ids):
        """
        Get an object or a list of objects from object store.

        Parameters
        ----------
        data_ids : unidist.core.backends.common.data_id.DataID or list
            ``DataID`` or a list of ``DataID`` objects to get data from.

        Returns
        -------
        object
            A Python object or a list of Python objects.
        """
        return py.get(data_ids)

    @staticmethod
    def put(data):
        """
        Put `data` into object store.

        Parameters
        ----------
        data : object
            Data to be put.

        Returns
        -------
        unidist.core.backends.common.data_id.DataID
            ``DataID`` matching to data.
        """
        return py.put(data)

    @staticmethod
    def wait(data_ids, num_returns=1):
        """
        Wait until `data_ids` are finished.

        This method returns two lists. The first list consists of
        data IDs that correspond to objects that completed computations.
        The second list corresponds to the rest of the data IDs.

        Parameters
        ----------
        object_refs : unidist.core.backends.common.data_id.DataID or list
            ``DataID`` or list of ``DataID``-s to be waited.
        num_returns : int, default: 1
            The number of ``DataID``-s that should be returned as ready.

        Returns
        -------
        tuple
            List of data IDs that are ready and list of the remaining data IDs.

        Notes
        -----
        Method serves to maintain behavior compatibility between backends. All objects
        completed computation before putting into an object storage for Python Sequential backend.
        """
        return data_ids[:num_returns], data_ids[num_returns:]

    @staticmethod
    def get_ip():
        """
        Get node IP address.

        Returns
        -------
        str
            Node IP address.
        """
        hostname = socket.gethostname()
        return socket.gethostbyname(hostname)

    @staticmethod
    def num_cpus():
        """
        Get the number of CPUs used by the execution backend.

        Returns
        -------
        int
        """
        return 1

    @staticmethod
    def cluster_resources():
        """
        Get resources of the cluster.

        Returns
        -------
        dict
            Dictionary with node info in the form `{"node_ip": {"CPU": x}}`.
        """
        return {PySeqBackend.get_ip(): {"CPU": PySeqBackend.num_cpus()}}

    @staticmethod
    def is_initialized():
        """
        Check if Python Sequential backend has already been initialized.

        Returns
        -------
        bool
            True or False.
        """
        return py.is_initialized()
