"""
Handles the queries.
"""
from attributes import IntegerField, DateTimeField
import redisco
from redisco.containers import SortedSet, Set, List, NonPersistentList
from exceptions import AttributeNotIndexed


# Model Set
class ModelSet(Set):
    def __init__(self, model_class):
        self.model_class = model_class
        self.key = model_class._all_key
        # We access directly _meta as .db is a property and should be
        # access from an instance, not a Class
        self._db = model_class._meta['db'] or redisco.get_client()
        self._filters = {}
        self._exclusions = {}
        self._zfilters = []
        self._ordering = []
        self._limit = None
        self._offset = None
        self._return_values = False

    #################
    # MAGIC METHODS #
    #################

    def __getitem__(self, index):
        """
        Will look in _set to get the id and simply return the instance of
        the model.
        """
        if isinstance(index, slice):
            pipeline = self._db.pipeline()
            for id in self._set[index]:
                pipeline.hgetall(self.model_class.instance_key(id))
            rawdata = pipeline.execute()
            if self._return_values:
                return rawdata
            else:
                instances = []
                for id, rd in zip(self._set[index], rawdata):
                    instances.append(
                        self.model_class.load_from_raw_data(id, rd))
                return instances
        else:
            id = self._set[index]
            if id:
                return self._get_item_with_id(id)
            else:
                raise IndexError

    def __repr__(self):
        if len(self._set) > 30:
            m = self._set[:30]
        else:
            m = self._set
        s = map(lambda id: self._get_item_with_id(id), m)
        return "%s" % s

    def __iter__(self):
        for id in self._set:
            yield self._get_item_with_id(id)

    def __len__(self):
        return len(self._set)

    def __contains__(self, val):
        return val.id in self._set

    ##########################################
    # METHODS THAT RETURN A SET OF INSTANCES #
    ##########################################
    def get_by_id(self, id):
        """
        Returns the object definied by ``id``.

        :param id: the ``id`` of the objects to lookup.