from bson import DBRef

from .abstract import BaseDataObject, I18nErrorDict


__all__ = ('List', 'Reference')


class List(BaseDataObject, list):

    def __init__(self, container_field, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.container_field = container_field

    def __setitem__(self, key, obj):
        obj = self.container_field.deserialize(obj)
        super().__setitem__(key, obj)
        self._modified = True

    def append(self, obj):
        obj = self.container_field.deserialize(obj)
        ret = super().append(obj)
        self.set_modified()
        return ret

    def pop(self, *args, **kwargs):
        ret = super().pop(*args, **kwargs)
        self.set_modified()
        return ret

    def clear(self, *args, **kwargs):
        ret = super().clear(*args, **kwargs)
        self.set_modified()
        return ret

    def remove(self, *args, **kwargs):
        ret = super().remove(*args, **kwargs)
        self.set_modified()
        return ret

    def reverse(self, *args, **kwargs):
        ret = super().reverse(*args, **kwargs)
        self.set_modified()
        return ret

    def sort(self, *args, **kwargs):
        ret = super().sort(*args, **kwargs)
        self.set_modified()
        return ret

    def extend(self, iterable):
        iterable = [self.container_field.deserialize(obj) for obj in iterable]
        ret = super().extend(iterable)
        self.set_modified()
        return ret

    def __repr__(self):
        return '<object %s.%s(%s)>' % (
            self.__module__, self.__class__.__name__, list(self))

    def is_modified(self):
        if self._modified:
            return True
        if len(self) and isinstance(self[0], BaseDataObject):
            # Recursive handling needed
            return next((True for obj in self if obj.is_modified() is True), False)

    def clear_modified(self):
        self._modified = False
        if len(self) and isinstance(self[0], BaseDataObject):
            # Recursive handling needed
            for obj in self:
                obj.clear_modified()


# TODO: Dict is to much raw: you need to use `set_modified` by hand !
class Dict(BaseDataObject, dict):
    pass


class Reference:

    error_messages = I18nErrorDict(not_found='Reference not found for document {document}.')

    def __init__(self, document_cls, pk):
        self.document_cls = document_cls
        self.pk = pk
        self._document = None

    def fetch(self, no_data=False):
        """
        Retrieve from the database the referenced document

        :param no_data: if True, the caller is only interested in whether or
            not the document is present in database. This means the
            implementation may not retrieve document's data to save bandwidth.
        """
        raise NotImplementedError
    # TODO replace no_data by `exists` function

    def __repr__(self):
        return '<object %s.%s(document=%s, pk=%r)>' % (
            self.__module__, self.__class__.__name__, self.document_cls.__name__, self.pk)

    def __eq__(self, other):
        if isinstance(other, self.document_cls):
            return other.pk == self.pk
        elif isinstance(other, Reference):
            return self.pk == other.pk and self.document_cls == other.document_cls
        elif isinstance(other, DBRef):
            return self.pk == other.id and self.document_cls.collection.name == other.collection
        return NotImplemented

@@ -15,16 +15,59 @@
 rt DBRef
+%0Afrom collections import UserList, UserDict
 %0A%0Afrom .
@@ -179,16 +179,70 @@
 ct, 
-l
+UserL
 ist):%0A%0A
+    __slots__ = ('container_field', '_modified')%0A%0A
     
@@ -329,32 +329,63 @@
 args, **kwargs)%0A
+        self._modified = False%0A
         self.con
@@ -548,32 +548,35 @@
 j)%0A        self.
+set
 _modified = True
@@ -572,15 +572,10 @@
 fied
- = True
+()
 %0A%0A  
@@ -1717,16 +1717,75 @@
 self))%0A%0A
+    def set_modified(self):%0A        self._modified = True%0A%0A
     def 
@@ -1979,38 +1979,12 @@
 urn 
-next((True for obj in self if 
+any(
 obj.
@@ -2001,23 +2001,23 @@
 d() 
-is True), False
+for obj in self
 )%0A%0A 
@@ -2347,23 +2347,345 @@
 ct, 
-dict):%0A    pass
+UserDict):%0A%0A    __slots__ = ('_modified', )%0A%0A    def __init__(self, *args, **kwargs):%0A        self._modified = False%0A        super().__init__(*args, **kwargs)%0A%0A    def is_modified(self):%0A        return self._modified%0A%0A    def set_modified(self):%0A        self._modified = True%0A%0A    def clear_modified(self):%0A        self._modified = False
 %0A%0A%0Ac

Improve List&Dict data objects