from stockpyle._base import BaseStore


class MultiStore(BaseStore):
    
    def __get_stores(self):
        return self.__stores
    
    def __set_stores(self, stores):
        self.__stores = stores
    
    stores = property(__get_stores, __set_stores)
    """the list of stores that this multistore composes.  Data will be written
    through these stores *in-order*"""
    
    def __init__(self, stores=None):
        super(MultiStore, self).__init__()
        if not stores:
            stores = []
        self.__stores = stores
    
    def configure(self, classes, lifetime):
        for store in self.__stores:
            store.configure(classes=classes, lifetime=lifetime)
    
    def put(self, obj):
        
        # write-through
        for store in self.__stores:
            store.put(obj)
    
    def batch_put(self, objs):
        
        # write-through
        for store in self.__stores:
            store.batch_put(objs)
    
    def delete(self, obj):
        
        # delete-through
        for store in self.__stores:
            store.delete(obj)
    
    def get(self, klass, key):
        
        # pull-through
        for i in range(0, len(self.__stores)):
            obj = self.__stores[i].get(klass, key)
            if obj:
                # we found it, pull it through all previous layers
                for previous_store in self.__stores[0:i]:
                    previous_store.put(obj)
                return obj
        
        # never found it
        return None
    
    def batch_get(self, klass, keys):
        
        # pull-through
        final_objects = [None for k in keys]
        for i in range(0, len(self.__stores)):
        
            # get as many hits as possible from this layer
            null_indices = [idx for idx in range(0, len(final_objects)) if final_objects[idx] is None]
            null_keys = [keys[null_idx] for null_idx in null_indices]
            new_objects = self.__stores[i].batch_get(klass, null_keys)
            nonnull_new_objects = []
            for null_idx, new_object in zip(null_indices, new_objects):
                final_objects[null_idx] = new_object
                if new_object:
                    nonnull_new_objects.append(new_object)
            
            # make sure any new objects are persisted in upper layers
            for previous_store in self.__stores[0:i]:
                previous_store.batch_put(nonnull_new_objects)
        
        # return whichever objects we found
        return final_objects
        