from canaryd.packages import six


class Change(object):
    def __init__(self, plugin, type_, key, data=None):
        self.plugin = plugin
        self.type = type_
        self.key = key

        self.data = data

    def serialise(self):
        data = None

        if self.type == 'updated':
            data = self.plugin.serialise_update(self.data)

        elif self.type == 'added':
            data = self.plugin.serialise_item(self.data)

        return self.type, self.key, data


def make_diffs(plugin, changes):
    return [
        Change(plugin, *change)
        for change in changes
    ]


def get_state_diff(plugin, plugin_state, previous_plugin_state):
    '''
    Diffs two state dicts and returns a list of changes.

    Changes:
        All come as a tuple of ``(type, key, data, message=None)``.
    '''

    changes = []

    # Look through the previous state to find any items that have been removed
    for key, state in six.iteritems(previous_plugin_state):
        if key not in plugin_state:
            changes.append(Change(plugin, 'deleted', key))

    # Loop the new state
    for key, item in six.iteritems(plugin_state):
        previous_item = previous_plugin_state.get(key)

        # Add anything that doesn't exist
        if not previous_item:
            changes.append(Change(plugin, 'added', key, data=item))
            continue

        # Plugins customise diff
        if plugin.is_change(key, previous_item, item) is False:
            continue

        # Create the diff, which is a key -> (old, new) values
        state_diff = dict(
            (k, (previous_item.get(k), v))
            for k, v in six.iteritems(item)
            if v != previous_item.get(k)
        )

        if state_diff:
            changes.append(Change(plugin, 'updated', key, data=state_diff))

    return changes

@@ -239,29 +239,8 @@
 f):%0A
-        data = None%0A%0A
     
@@ -344,31 +344,10 @@
   el
-if self.type == 'added'
+se
 :%0A  
@@ -444,16 +444,116 @@
 , data%0A%0A
+    def __repr__(self):%0A        return '%7B0%7D'.format((self.plugin, self.type, self.key, self.data))%0A%0A
 %0Adef mak
@@ -839,16 +839,24 @@
 e of %60%60(
+plugin, 
 type, ke
@@ -866,17 +866,8 @@
 data
-, message
 =Non
@@ -991,21 +991,29 @@
 or key, 
-sta
+previous_i
 te
+m
  in six.
@@ -1082,16 +1082,150 @@
 _state:%0A
+            state_diff = dict(%0A                (k, (v, None))%0A                for k, v in six.iteritems(previous_item)%0A            )%0A%0A
         
@@ -1272,16 +1272,33 @@
 ed', key
+, data=state_diff
 ))%0A%0A    
@@ -1496,16 +1496,140 @@
 s_item:%0A
+            state_diff = dict(%0A                (k, (None, v))%0A                for k, v in six.iteritems(item)%0A            )%0A
         
@@ -1681,20 +1681,26 @@
 y, data=
-item
+state_diff
 ))%0A     

Change added/deleted changes to have similar data changes structure to updates.