import logging
import sys
import warnings

__all__ = ["use", "get_backend"]

_log = logging.getLogger(__name__)


def _use_backend(name):
    # internal: import the libseabreeze cython wrapper -> cseabreeze
    try:
        if name == "cseabreeze":
            import seabreeze.cseabreeze as sbb
        elif name == "pyseabreeze":
            import seabreeze.pyseabreeze as sbb
        else:
            raise ValueError("unknown backend '{}'".format(name))
    except ImportError as err:
        _log.warning(
            "seabreeze can't load '{}' backend - error: '{}'".format(name, repr(err)),
            exc_info=True,
        )
        return None
    else:
        return sbb


# internal config dict used exclusively in this submodule
_SeaBreezeConfig = {
    "requested_backend": "cseabreeze",  # default is cseabreeze
    "available_backends": ("cseabreeze", "pyseabreeze"),
    "allow_fallback": False,
    "_api_kwargs": {},  # for pytests
}


def use(backend, force=True, **_kwargs):
    """
    select the backend used for communicating with the spectrometer

    Parameters
    ----------
    backend : str

    force : bool, optional, default: True
        raises an ImportError when ``seabreeze.get_backend()`` is called
        and the requested backend can not be imported. force=True should
        be used in user code to ensure that the correct backend is being
        loaded.
    """
    if backend not in _SeaBreezeConfig["available_backends"]:
        raise ValueError(
            "backend not in: {}".format(
                ", ".join(_SeaBreezeConfig["available_backends"])
            )
        )
    if 'seabreeze.spectrometers' in sys.modules:
        warnings.warn(
            "seabreeze.use has to be called before importing seabreeze.spectrometers",
        )

    _SeaBreezeConfig["requested_backend"] = backend
    _SeaBreezeConfig["allow_fallback"] = not force
    _SeaBreezeConfig["_api_kwargs"] = _kwargs.pop("_api_kwargs", {})
    if _kwargs:
        raise TypeError("unknown keyword arguments")


def get_backend():
    """
    return the requested backend or a fallback. configuration is done
    via ``seabreeze.use()``

    Returns
    -------
    backend:
        a backend interface for communicating with the spectrometers
    """
    requested = _SeaBreezeConfig["requested_backend"]
    fallback = _SeaBreezeConfig["allow_fallback"]
    backends = _SeaBreezeConfig["available_backends"]

    backend = _use_backend(requested)  # trying to import requested backend
    if backend is None and fallback:

        warnings.warn(
            "seabreeze failed to load user requested '{}' backend but will try fallback backends".format(
                requested
            ),
            category=ImportWarning,
        )

        fallback_backends = [b for b in backends if b != requested]
        for fallback_backend in fallback_backends:
            backend = _use_backend(fallback_backend)
            if backend is not None:
                break
        else:
            raise ImportError(
                "Could not import backend. Tried: {}".format(", ".join(backends))
            )

    if backend is None:
        raise ImportError("Could not import backend. Requested: {}".format(requested))

    # provide for testing purposes
    backend._api_kwargs = _SeaBreezeConfig["_api_kwargs"]

    return backend
