import functools

__all__ = ["LazyData"]


class LazyData(object):
    """Lazily load data from function and kwargs

    The idea is that the experimental data does not have
    to be loaded before the user requests it. Furthermore,
    this reduces the memory footprint (not all data are
    loaded).
    """
    def __init__(self):
        self.loaders = {}

    def __deepcopy__(self, memo):
        # Make sure deepcopy does not copy anything.
        # We might have `io`s, etc.
        return self

    def __contains__(self, key):
        return key in self.loaders

    # cache a few curves so everything is more responsive
    @functools.lru_cache(maxsize=100)
    def __getitem__(self, key):
        if key in self:
            func, kwargs = self.loaders[key]
            return func(**kwargs)

    def __iter__(self):
        for column in self.loaders:
            yield column

    def keys(self):
        return self.loaders.keys()

    def set_lazy_loader(self, column, func, kwargs):
        """Add a lazy loader

        Parameters
        ----------
        column: str
            Column for which to register the loader
        func: callable
            Function to call to get the data
        kwargs:
            Keyword arguments to ``func``
        """
        self.loaders[column] = (func, kwargs)
