void osync_status_update_change(OSyncEngine *engine, OSyncChange *change, OSyncMember *member, OSyncMapping *mapping, OSyncChangeEvent type, OSyncError *error)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %i, %p)", __func__, engine, change, member, mapping, type, error);
	
	if (engine->changestat_callback) {
		OSyncChangeUpdate *update = g_malloc0(sizeof(OSyncChangeUpdate));
		if (!update)
			return;
		
		update->type = type;
		
		update->change = change;
		osync_change_ref(change);
		
		update->member = member;
		osync_member_ref(member);
		
		update->error = error;
		osync_error_ref(&error);
		
		engine->changestat_callback(update, engine->changestat_userdata);
		
		osync_status_free_change_update(update);
	} else
		osync_trace(TRACE_INTERNAL, "Status Update Ignored");
		
	osync_trace(TRACE_EXIT, "%s", __func__);
}
osync_bool osync_obj_engine_receive_change(OSyncObjEngine *objengine, OSyncClientProxy *proxy, OSyncChange *change, OSyncError **error)
{
  OSyncSinkEngine *sinkengine = NULL;
  GList *s = NULL, *e = NULL;
	
  osync_assert(objengine);
	
  osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, objengine, proxy, change, error);
	
  /* Find the sinkengine for the proxy */
  for (s = objengine->sink_engines; s; s = s->next) {
    sinkengine = s->data;
    if (sinkengine->proxy == proxy)
      break;
    sinkengine = NULL;
  }
	
  if (!sinkengine) {
    osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to find sinkengine");
    osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
    return FALSE;
  }
	
  /* We now have to see if the change matches one of the already existing mappings */
  for (e = sinkengine->entries; e; e = e->next) {
    OSyncMappingEntryEngine *mapping_engine = e->data;
		
    if (osync_entry_engine_matches(mapping_engine, change)) {
      osync_entry_engine_update(mapping_engine, change);
			
      osync_status_update_change(sinkengine->engine->parent, change, osync_client_proxy_get_member(proxy), mapping_engine->mapping_engine->mapping, OSYNC_CHANGE_EVENT_READ, NULL);
			
      osync_trace(TRACE_EXIT, "%s: Updated", __func__);
      return TRUE;
    }
  }
	
  osync_status_update_change(sinkengine->engine->parent, change, osync_client_proxy_get_member(proxy), NULL, OSYNC_CHANGE_EVENT_READ, NULL);
			
  /* If we couldnt find a match entry, we will append it the unmapped changes
   * and take care of it later */
  sinkengine->unmapped = g_list_append(sinkengine->unmapped, change);
  osync_change_ref(change);
	
  osync_trace(TRACE_EXIT, "%s: Unmapped", __func__);
  return TRUE;
}
//typedef void (* OSyncContextChangeFn) (OSyncChange *, void *);
static void _osyncplugin_ctx_change_callback(OSyncChange *change, void *user_data)
{
	Command *cmd = (Command *) user_data;		
	OSyncObjTypeSink *sink = cmd->sink;

	printf("GETCHANGES:\t%s\t%s\t%s", 
	       _osyncplugin_changetype_str(change), 
	       osync_objtype_sink_get_name(sink),	
	       osync_change_get_uid(change));

	if (osync_change_get_objformat(change))
		printf("\t%s", osync_objformat_get_name(osync_change_get_objformat(change)));

	printf("\n");

	osync_change_ref(change);
	changesList = g_list_append(changesList, change);
}
/** @brief Solves the conflict by duplicating the conflicting entries
 * 
 * @param engine The engine
 * @param dupe_mapping The conflicting mapping to duplicate
 * 
 */
osync_bool osync_mapping_engine_duplicate(OSyncMappingEngine *existingMapping, OSyncError **error)
{
  int elevation = 0;
  OSyncObjEngine *objengine = NULL;
  GList *entries = NULL, *e = NULL, *mappings = NULL;

  osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, existingMapping, error);
  g_assert(existingMapping);
	
  objengine = existingMapping->parent;
	
  /* Remove all deleted items first and copy the changes to a list */
  e = existingMapping->entries;
  for (; e; e = e->next) {
    OSyncMappingEntryEngine *entry = e->data;
    if (entry->change) {
      if (osync_change_get_changetype(entry->change) == OSYNC_CHANGE_TYPE_MODIFIED || osync_change_get_changetype(entry->change) == OSYNC_CHANGE_TYPE_ADDED) {
        osync_trace(TRACE_INTERNAL, "Appending entry %s, changetype %i from member %lli", osync_change_get_uid(entry->change), osync_change_get_changetype(entry->change), osync_member_get_id(osync_client_proxy_get_member(entry->sink_engine->proxy)));
		
        entries = g_list_append(entries, entry);
      } else {
        osync_trace(TRACE_INTERNAL, "Removing entry %s, changetype %i from member %lli", osync_change_get_uid(entry->change), osync_change_get_changetype(entry->change), osync_member_get_id(osync_client_proxy_get_member(entry->sink_engine->proxy)));
        osync_entry_engine_update(entry, NULL);
      }
    } else {
      osync_trace(TRACE_INTERNAL, "member %lli does not have a entry", osync_member_get_id(osync_client_proxy_get_member(entry->sink_engine->proxy)));
    }
  }
	
  /* Create a list with mappings. In the beginning, only the exisiting mapping is in the list */
  mappings = g_list_append(NULL, existingMapping);
  osync_mapping_engine_ref(existingMapping);
	
  while (entries) {
    OSyncMappingEntryEngine *existingEntry = entries->data;
		
    /* Now lets see which mapping is the correct one for the entry */
    GList *m = NULL;
    OSyncMappingEngine *mapping = NULL;
    OSyncChange *existingChange = NULL;
    osync_bool dirty = FALSE;
    OSyncMappingEntryEngine *newEntry = NULL;

    elevation = 0;
    for (m = mappings; m; m = m->next) {
      GList *e = NULL;
      OSyncChange *change = NULL;
      OSyncMappingEntryEngine *entry = NULL;
      mapping = m->data;
			
      /* Get the first change of the mapping to test. Compare the given change with this change.
       * If they are not the same, we have found a new mapping */
      for (e = mapping->entries; e; e = e->next) {
        entry = e->data;
        change = entry->change;
        if (change)
          break;
      }
			
      if (!change || osync_change_compare(existingEntry->change, change) == OSYNC_CONV_DATA_SAME){
        existingChange = existingEntry->change;
        osync_change_ref(existingChange);
        osync_assert(osync_change_get_uid(existingChange));
        break;
      }


      mapping = NULL;
      elevation++;
		
      existingChange = osync_change_clone(existingEntry->change, error);
      osync_assert(osync_change_get_uid(existingChange));
    }
		
		
    if (!mapping) {
      /* Unable to find a mapping. We have to create a new one */
      mapping = _osync_obj_engine_create_mapping_engine(objengine, error);
      if (!mapping)
        goto error;
      mappings = g_list_append(mappings, mapping);
      objengine->mapping_engines = g_list_append(objengine->mapping_engines, mapping);
      osync_mapping_engine_ref(mapping);
    }
		
    /* update the uid and the content to suit the new level */
    if (!_osync_change_elevate(existingChange, elevation, &dirty, error))
      goto error;

    /* Lets add the entry to the mapping */
    newEntry = osync_mapping_engine_get_entry(mapping, existingEntry->sink_engine);
    osync_assert(newEntry);
    osync_entry_engine_update(newEntry, existingChange);
    osync_mapping_entry_set_uid(newEntry->entry, osync_change_get_uid(existingChange));
    osync_change_unref(existingChange);
		
    /* Set the last entry as the master */
    osync_mapping_engine_set_master(mapping, newEntry);
		
    /* Update the dirty status. If the duplicate function said
     * that the returned item needs to be written, we will set
     * this information here */
    newEntry->dirty = dirty;
		
    entries = g_list_remove(entries, existingEntry);
  }
	
	
  while (mappings) {
    OSyncMappingEngine *mapping = mappings->data;
    osync_mapping_engine_unref(mapping);
    mappings = g_list_remove(mappings, mapping);
  }
	
  objengine->conflicts = g_list_remove(objengine->conflicts, existingMapping);
  osync_status_update_mapping(objengine->parent, existingMapping, OSYNC_MAPPING_EVENT_SOLVED, NULL);
	
  if (osync_engine_check_get_changes(objengine->parent) && osync_bitcount(objengine->sink_errors | objengine->sink_get_changes) == g_list_length(objengine->sink_engines)) {
    if (!osync_obj_engine_command(objengine, OSYNC_ENGINE_COMMAND_WRITE, error))
      goto error;
  } else
    osync_trace(TRACE_INTERNAL, "Not triggering write. didnt receive all reads yet");

  osync_trace(TRACE_EXIT, "%s", __func__);
  return TRUE;

 error:
  while (mappings) {
    OSyncMappingEngine *mapping = mappings->data;
    osync_mapping_engine_unref(mapping);
    mappings = g_list_remove(mappings, mapping);
  }
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
osync_bool osync_mapping_engine_multiply(OSyncMappingEngine *engine, OSyncError **error)
{
  GList *e = NULL;
  osync_assert(engine);
  osync_assert(engine->mapping);
	
  osync_trace(TRACE_ENTRY, "%s(%p(%lli), %p)", __func__, engine, osync_mapping_get_id(engine->mapping), error);
		
  if (engine->synced) {
    osync_trace(TRACE_EXIT, "%s: No need to multiply. Already synced", __func__);
    return TRUE;
  }

  if (!engine->master) {
    osync_error_set(error, OSYNC_ERROR_GENERIC, "No master set");
    goto error;
  }

  for (e = engine->entries; e; e = e->next) {
    OSyncChange *existChange = NULL, *masterChange = NULL;
    OSyncData *masterData = NULL, *newData = NULL;
    OSyncMappingEntryEngine *entry_engine = e->data;
    OSyncChangeType existChangeType = 0, newChangeType = 0;
    if (entry_engine == engine->master)
      continue;
		
    osync_trace(TRACE_INTERNAL, "Propagating change %s to %p from %p", osync_mapping_entry_get_uid(entry_engine->entry), entry_engine, engine->master);
		
    /* Input is:
     * masterChange -> change that solved the mapping
     * masterData -> data of masterChange
     * existChange -> change that will be overwritten (if any) */
		
    existChange = entry_engine->change;
    masterChange = osync_entry_engine_get_change(engine->master);
    masterData = osync_change_get_data(masterChange);
		
    /* Clone the masterData. This has to be done since the data
     * might get changed (converted) and we dont want to touch the 
     * original data */
    newData = osync_data_clone(masterData, error);
    if (!newData)
      goto error;
		
    if (!existChange) {
      existChange = osync_change_new(error);
      if (!existChange)
        goto error;
			
      osync_change_set_changetype(existChange, OSYNC_CHANGE_TYPE_UNKNOWN);
    } else {
      /* Ref the change so that we can unref it later */
      osync_change_ref(existChange);
    }
		
    /* Save the changetypes, so that we can calculate the correct changetype later */
    existChangeType = osync_change_get_changetype(existChange);
    newChangeType = osync_change_get_changetype(masterChange);
		
    osync_trace(TRACE_INTERNAL, "Orig change type: %i New change type: %i", existChangeType, newChangeType);

    /* Now update the entry with the change */
    osync_entry_engine_update(entry_engine, existChange);
		
    /* We have to use the uid of the entry, so that the member
     * can correctly identify the entry 
     *
     * prahal: added a check if the entry has a uid to send the existing uid
     * to the plugins in case we have a slow-sync (both are added and have a uid) 
     * This to avoid creating duplicates by sending the plugins a different uid 
     * with the same or merged data 
     *
     * dgollub: enhanced the check if the entry has a uid to send the existing uid
     * to the plugins in case we have a slow-sync - both have changetype ADDED - and
     * for odd plugins/protocolls which mark new entries as MODIFIED all the time.
     * Changetype MODIFIED of new entries has atleast the IrMC plugin and likely some
     * SE SyncML implementation...
     * 
     * ^^irmc hacks in the irmc plugin ;-)
     *
     * dgollub: Set masterChange UID for existChange if entry_engine->entry doesn't have 
     * mapping uid, even if the newChangeType is UNKOWN. Bug: #571  
     *
     * prahal : rely on the fact that there are no mapping nor the entry existed to detect new change
     *	    Also avoid changing the id of the change if one existed and there where no mapping :
     *	    this way we send the id known to the member in case of a "modify". Fixing syncml plugin
     *	    freezing the phone and mozilla sync receiving an id it cannot do anything with for modify
     *	    in slow sync (no existing mapping).
     *
     */
    if ((!osync_mapping_entry_get_uid(entry_engine->entry) && !osync_change_get_uid(existChange))  ) 
      osync_change_set_uid(existChange, osync_change_get_uid(masterChange));
    else if(osync_mapping_entry_get_uid(entry_engine->entry)) 
      osync_change_set_uid(existChange, osync_mapping_entry_get_uid(entry_engine->entry));

    osync_change_set_data(existChange, newData);
    osync_change_set_changetype(existChange, osync_change_get_changetype(masterChange));
		
    /* We also have to update the changetype of the new change */
    if (newChangeType == OSYNC_CHANGE_TYPE_ADDED && (existChangeType != OSYNC_CHANGE_TYPE_DELETED && existChangeType != OSYNC_CHANGE_TYPE_UNKNOWN)) {
      osync_trace(TRACE_INTERNAL, "Updating change type to MODIFIED");
      osync_change_set_changetype(existChange, OSYNC_CHANGE_TYPE_MODIFIED);
      /* Only adapt the change to ADDED if the existing Change got deleted. Don't update it to ADDED if existChangeType is UNKOWN.
         The exitChangeType is at least also UNKOWN if the file-sync has only one modified entry. */
    } else if (newChangeType == OSYNC_CHANGE_TYPE_MODIFIED && (existChangeType == OSYNC_CHANGE_TYPE_DELETED)) {
      osync_trace(TRACE_INTERNAL, "Updating change type to ADDED");
      osync_change_set_changetype(existChange, OSYNC_CHANGE_TYPE_ADDED);
    }
		
    osync_change_unref(existChange);
    /* Also unref newData. Otherwise this cannot be freed when it is written. */
    osync_data_unref(newData);
			
    osync_entry_engine_set_dirty(entry_engine, TRUE);
  }
	
  osync_trace(TRACE_EXIT, "%s", __func__);
  return TRUE;

 error:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}