/* sml_data_sync_session.c
 *
 * Copyright (C) 2009 Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 */

#include "sml_data_sync_session_private.h"
#include "sml_data_sync_data_store_session_internals.h"
#include "data_sync_devinf.h"

#include "sml_data_sync_internals.h"
#include "../sml_support.h"
#include "../sml_error_internals.h"

#include "../sml_manager.h"

G_DEFINE_TYPE (SmlDataSyncSession, sml_data_sync_session, G_TYPE_OBJECT)

enum
{
	PROP_0,
	PROP_LOCAL,
	PROP_REMOTE
};

static void
sml_data_sync_session_get_property (GObject    *object,
                                    guint       property_id,
                                    GValue     *value,
                                    GParamSpec *pspec)
{
	switch (property_id) {
	case PROP_LOCAL:
		g_value_set_object (value, SML_DATA_SYNC_SESSION (object)->priv->local);
		break;
	case PROP_REMOTE:
		g_value_set_object (value, SML_DATA_SYNC_SESSION (object)->priv->remote);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_data_sync_session_set_property (GObject      *object,
                                    guint         property_id,
                                    const GValue *value,
                                    GParamSpec   *pspec)
{
	switch (property_id) {
	case PROP_LOCAL:
        	if (SML_DATA_SYNC_SESSION (object)->priv->local)
        		g_object_unref (SML_DATA_SYNC_SESSION (object)->priv->local);
		SML_DATA_SYNC_SESSION (object)->priv->local = SML_LOCATION (value);
		g_object_ref(SML_LOCATION (value));
		break;
	case PROP_REMOTE:
        	if (SML_DATA_SYNC_SESSION (object)->priv->remote)
        		g_object_unref (SML_DATA_SYNC_SESSION (object)->priv->remote);
		SML_DATA_SYNC_SESSION (object)->priv->remote = SML_LOCATION (value);
		g_object_ref(SML_LOCATION (value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_data_sync_session_free_ds_session(gpointer key)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, key);
	SmlDsSession *session = key;
	smlDsSessionUnref(session);
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void
sml_data_sync_session_free_data_store(gpointer key)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, key);
	SmlDataSyncDataStore *datastore = key;
	g_object_unref(datastore);
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void
sml_data_sync_session_free_data_store_session(gpointer key)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, key);
	SmlDataSyncDataStoreSession *session = key;
	g_object_unref(session);
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void
sml_data_sync_session_finalize (GObject *object)
{
	SmlDataSyncSession *self = SML_DATA_SYNC_SESSION (object);

	/* cleanup sessions */
	g_hash_table_remove_all(self->priv->hash_dsdss2dsdss);
	g_hash_table_remove_all(self->priv->hash_ds2dsdss);
	g_hash_table_remove_all(self->priv->hash_dsds2dsdss);
	g_hash_table_unref(self->priv->hash_dsdss2dsdss);
	g_hash_table_unref(self->priv->hash_ds2dsdss);
	g_hash_table_unref(self->priv->hash_dsds2dsdss);
	self->priv->hash_dsdss2dsdss = NULL;
	self->priv->hash_ds2dsdss = NULL;
	self->priv->hash_dsds2dsdss = NULL;

	/* cleanup remote DevInf */
	if (self->priv->remote_dev_inf) {
		g_object_unref(self->priv->remote_dev_inf);
		self->priv->remote_dev_inf = NULL;
	}

	/* cleanup locations */
	if (self->priv->remote) {
		g_object_unref(self->priv->remote);
		self->priv->remote = NULL;
	}
	if (self->priv->local) {
		g_object_unref(self->priv->local);
		self->priv->local = NULL;
	}

	/* cleanup data sync object */
	if (self->priv->data_sync) {
		g_object_unref(self->priv->data_sync);
		self->priv->data_sync = NULL;
	}

	G_OBJECT_CLASS (sml_data_sync_session_parent_class)->finalize (object);
}

static void
sml_data_sync_session_class_init (SmlDataSyncSessionClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	g_type_class_add_private (klass, sizeof (SmlDataSyncSessionPrivate));

	object_class->get_property = sml_data_sync_session_get_property;
	object_class->set_property = sml_data_sync_session_set_property;
	object_class->finalize     = sml_data_sync_session_finalize;

	/**
	 * SmlDataSyncSession:local:
	 *
	 * The local location property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_LOCAL,
	                                 g_param_spec_object ("local",
	                                                      "local",
	                                                      "local location",
	                                                      G_TYPE_OBJECT,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncSession:remote:
	 *
	 * The remote location property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_REMOTE,
	                                 g_param_spec_object ("remote",
	                                                      "remote",
	                                                      "remote location",
	                                                      G_TYPE_OBJECT,
	                                                      G_PARAM_READWRITE));

}

static void
sml_data_sync_session_init (SmlDataSyncSession *self)
{
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
	                                          SML_TYPE_DATA_SYNC_SESSION,
	                                          SmlDataSyncSessionPrivate);
}

/**
 * sml_data_sync_session_new:
 *
 * Creates a new instance of #SmlDataSyncSession.
 *
 * Return value: the newly created #SmlDataSyncSession instance
 */
SmlDataSyncSession*
sml_data_sync_session_new (SmlSession *session,
                           SmlDataSync *data_sync,
                           GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%p, %p, %p)", __func__, session, data_sync, error);
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (session != NULL, NULL, error, SML_ERROR_GENERIC, "There must be a SmlSession object.");
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC (data_sync), NULL, error, SML_ERROR_GENERIC, "There must be a SmlDataSync object.");

	SmlDataSyncSession *self = g_object_new (SML_TYPE_DATA_SYNC_SESSION, NULL);

	self->priv->session = session;
	self->priv->data_sync = data_sync;
	self->priv->actual_package = SML_PACKAGE_0;
	self->priv->only_localtime = sml_data_sync_get_only_localtime(data_sync);

	if (!sml_data_sync_configure_session(data_sync, session, error))
	{
		g_object_unref(self);
		smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
		return NULL;
	}

	self->priv->hash_ds2dsdss = g_hash_table_new_full(g_direct_hash,
	                                                  g_direct_equal,
	                                                  sml_data_sync_session_free_ds_session,
	                                                  sml_data_sync_session_free_data_store_session);
	self->priv->hash_dsds2dsdss = g_hash_table_new_full(g_direct_hash,
	                                                    g_direct_equal,
	                                                    sml_data_sync_session_free_data_store,
	                                                    sml_data_sync_session_free_data_store_session);
	self->priv->hash_dsdss2dsdss = g_hash_table_new_full(g_direct_hash,
	                                                     g_direct_equal,
	                                                     sml_data_sync_session_free_data_store_session,
	                                                     sml_data_sync_session_free_data_store_session);
	smlTrace(TRACE_EXIT, "%s - %p", __func__, self);
	return self;
}

SmlSession*
sml_data_sync_session_get_session (SmlDataSyncSession *self)
{
	return self->priv->session;
}

SmlDataSync*
sml_data_sync_session_get_data_sync (SmlDataSyncSession *self)
{
	return self->priv->data_sync;
}

SmlDataSyncDataStoreSession*
sml_data_sync_session_get_data_store_session (SmlDataSyncSession *self,
                                              SmlDataSyncDataStore *datastore,
                                              GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%p, %p, %p)", __func__, self, datastore, error);
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_SESSION (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncSession object.");
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_DATA_STORE (datastore), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncDataStore object.");

	smlTrace(TRACE_INTERNAL, "%s: %s", __func__, sml_data_sync_data_store_get_local_uri(datastore));
	SmlDataSyncDataStoreSession *data_store_session = g_hash_table_lookup(self->priv->hash_dsds2dsdss, datastore);
	if (!data_store_session) {
		data_store_session = sml_data_sync_data_store_session_new(datastore, self, error);
		self->priv->list_data_store_sessions = g_list_append(self->priv->list_data_store_sessions, data_store_session);
		g_hash_table_insert(self->priv->hash_dsds2dsdss, datastore, data_store_session);
		g_hash_table_insert(self->priv->hash_dsdss2dsdss, data_store_session, data_store_session);
		g_object_ref(data_store_session);
		g_object_ref(data_store_session);
		g_object_ref(data_store_session);
		g_object_ref(datastore);
		smlTrace(TRACE_INTERNAL, "%s: data store sessions => %d", __func__, g_list_length(self->priv->list_data_store_sessions));
	}
	smlTrace(TRACE_EXIT, "%s - %p", __func__, data_store_session);
	return data_store_session;
}

/**
 * sml_data_sync_session_get_local:
 * @self: A #SmlDataSyncSession
 *
 * Gets the local property.
 *
 * Return value: 
 */
SmlLocation*
sml_data_sync_session_get_local (SmlDataSyncSession *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_SESSION (self), NULL);

	if (!self->priv->local &&
	    smlSessionGetSource(self->priv->session) &&
	    smlSessionGetEstablished(self->priv->session))
	{
		sml_data_sync_session_set_local(self, smlSessionGetSource(self->priv->session));
	}

	return self->priv->local;
}

/**
 * sml_data_sync_session_set_local:
 * @self: A #SmlDataSyncSession
 * @local:
 *
 * Sets the local property.
 */
void
sml_data_sync_session_set_local (SmlDataSyncSession *self,
                                 SmlLocation* local)
{
	g_return_if_fail (SML_IS_DATA_SYNC_SESSION (self));
	g_return_if_fail (SML_IS_LOCATION (local));

	if (self->priv->local)
		g_object_unref (self->priv->local);
	self->priv->local = g_object_ref (local);
}

/**
 * sml_data_sync_session_get_remote:
 * @self: A #SmlDataSyncSession
 *
 * Gets the remote property.
 *
 * Return value: 
 */
SmlLocation*
sml_data_sync_session_get_remote (SmlDataSyncSession *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_SESSION (self), NULL);

	if (!self->priv->remote &&
	    smlSessionGetTarget(self->priv->session) &&
	    smlSessionGetEstablished(self->priv->session))
	{
		sml_data_sync_session_set_remote(self, smlSessionGetTarget(self->priv->session));
	}

	return self->priv->remote;
}

/**
 * sml_data_sync_session_set_remote:
 * @self: A #SmlDataSyncSession
 * @remote:
 *
 * Sets the remote property.
 */
void
sml_data_sync_session_set_remote (SmlDataSyncSession *self,
                                  SmlLocation* remote)
{
	g_return_if_fail (SML_IS_DATA_SYNC_SESSION (self));
	g_return_if_fail (SML_IS_LOCATION (remote));

	if (self->priv->remote)
		g_object_unref (self->priv->remote);
	self->priv->remote = g_object_ref (remote);
}

/**
 * sml_data_sync_session_send_changes:
 * @self: A #SmlDataSyncSession
 *
 * 
 */
gboolean
sml_data_sync_session_send_changes (SmlDataSyncSession *self,
                                    GError **error)
{
	/* This means that all alerts were received
	 * and all changes were added.
	 * So let us flush the session. */
	smlTrace(TRACE_ENTRY, "%s", __func__);
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_SESSION (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncSession object.");

	/* Verify that the library is in the correct state */

	if (self->priv->state >= SML_DATA_SYNC_STATE_SENT_CHANGES)
	{
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			"The function %s is called to late or more than once.", __func__);
		goto error;
	} else {
		self->priv->state = SML_DATA_SYNC_STATE_SENT_CHANGES;
	}
	if (sml_data_sync_get_session_type (self->priv->data_sync) == SML_SESSION_TYPE_SERVER &&
	    self->priv->actual_package != SML_PACKAGE_4)
	{
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			"This is not the server's sync package (%d).",
			self->priv->actual_package);
		goto error;
	}
	if (sml_data_sync_get_session_type (self->priv->data_sync) == SML_SESSION_TYPE_CLIENT &&
	    self->priv->actual_package != SML_PACKAGE_3)
	{
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			"This is not the client's sync package (%d).",
			self->priv->actual_package);
		goto error;
	}

	/* iterate over all datastores */

	GList *o = self->priv->list_data_store_sessions;
	for (; o; o = o->next) { 
		SmlDataSyncDataStoreSession *datastore = o->data;
		smlAssert(datastore);

		if (!sml_data_sync_data_store_session_send_changes(datastore, error))
			goto error;

	}

	/* Send the next package */

	if (!smlSessionFlush(self->priv->session, TRUE, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
	return FALSE;
}

/**
 * sml_data_sync_session_send_map:
 * @self: A #SmlDataSyncSession
 *
 * 
 */
gboolean
sml_data_sync_session_send_mappings (SmlDataSyncSession *self,
                                     GError **error)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_SESSION (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncSession object.");

	/* Verify that the library is in the correct state */

	if (sml_data_sync_get_session_type(self->priv->data_sync) == SML_SESSION_TYPE_SERVER)
	{
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "An OMA DS server never sends a map.");
		goto error;
	}
	if (sml_data_sync_get_session_type(self->priv->data_sync) == SML_SESSION_TYPE_CLIENT &&
	    self->priv->actual_package != SML_PACKAGE_5)
	{
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing server's sync package.");
		goto error;
	}

	/* iterate over all datastores */

	GList *o = self->priv->list_data_store_sessions;
	for (; o; o = o->next) { 
		SmlDataSyncDataStoreSession *datastore = o->data;
		smlAssert(datastore);

		if (!sml_data_sync_data_store_session_send_mappings(datastore, error))
			goto error;

	}

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
	return FALSE;
}


/**
 * sml_data_sync_session_abort:
 * @self: A #SmlDataSyncSession
 *
 * 
 */
gboolean
sml_data_sync_session_abort (SmlDataSyncSession *self,
                             GError **error)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_SESSION (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncSession object.");
	sml_return_val_error_if_fail (self->priv->session, FALSE, error, SML_ERROR_GENERIC, "There must be a SmlSession object.");

	///* determine the state of the session */
	//if (!dsObject->session->sending && !dsObject->session->waiting)
	//{
	//	/* send abort command */
	//	smlTrace(TRACE_INTERNAL, "%s: A careful abort is possible.", __func__);
	//	/* FIXME: Send a default abort command. */
	//	/* FIXME: Is it necessary or recommended to abort every datastore. */
	//	goto WORKAROUND;
	//} else {
	//	/* stop transport */
//WORKAROUND:
	smlTrace(TRACE_INTERNAL, "%s: A hard abort is required.", __func__);

	if (SML_DATA_SYNC_STATE_CONNECTED <= self->priv->state &&
	    self->priv->state < SML_DATA_SYNC_STATE_DISCONNECT_IN_PROGRESS)
	{
		self->priv->state = SML_DATA_SYNC_STATE_DISCONNECT_IN_PROGRESS;

		if (!sml_data_sync_disconnect_session (self->priv->data_sync, self->priv->session, error))
			goto error;
	}
	//}

	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
	return FALSE;
}

/**
 * sml_data_sync_session_check:
 * @self: A #SmlDataSyncSession
 *
 * 
 */
gboolean
sml_data_sync_session_check (SmlDataSyncSession *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_SESSION (self), FALSE);

	GList *o = self->priv->list_data_store_sessions;
	for (; o; o = o->next) {
		SmlDataSyncDataStoreSession *session = o->data;
		if (sml_data_sync_data_store_session_check(session))
			return TRUE;
	}
	return FALSE;
}

/**
 * sml_data_sync_session_dispatch:
 * @self: A #SmlDataSyncSession
 *
 * 
 */
gboolean
sml_data_sync_session_dispatch (SmlDataSyncSession *self)
{
	smlTrace(TRACE_ENTRY, "%s (%p)", __func__, self);
	g_return_val_if_fail (SML_IS_DATA_SYNC_SESSION (self), FALSE);

	GList *o = self->priv->list_data_store_sessions;
	for (; o; o = o->next)
	{
		smlTrace(TRACE_INTERNAL, "%s: data store session %p", __func__, o->data);
		SmlDataSyncDataStoreSession *session = o->data;
		if (sml_data_sync_data_store_session_check(session) &&
		    !sml_data_sync_data_store_session_dispatch(session))
		{
			smlTrace(TRACE_EXIT, "%s - FALSE", __func__);
			return FALSE;
		}
	}
	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
}

/**
 * sml_data_sync_session_send_event:
 * @self: A #SmlDataSync
 * @session: A #SmlDataSyncSession
 * @type: A #SmlDataSyncEventType
 * @error: A #GError
 * 
 */
void
sml_data_sync_session_send_event (SmlDataSyncSession *self,
                                  SmlDataSync *data_sync,
                                  SmlDataSyncEventType type,
                                  const GError *error)
{
	/* this function works synchronous today */
	smlAssert(self || data_sync);
	if (!data_sync)
		data_sync = self->priv->data_sync;
	SmlDataSyncSessionEventCallback callback = sml_data_sync_get_event_callback(data_sync);
	void *userdata = sml_data_sync_get_event_userdata(data_sync);

	smlAssert(callback);
	callback(self, data_sync, type, userdata, error);
}

/* *************************************** */
/* *****     Management Callback     ***** */
/* *************************************** */

void
sml_data_sync_session_event_callback (SmlDataSyncSession *self,
                                      SmlManagerEventType type,
                                      const GError *error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, self, type, error);

	GList *o = NULL;
	GError *locerror = NULL;

	/* I (bellmich) still looking for the perfect time
	 * when to set the remote peers identity.
	 */
	if (!self->priv->local &&
	    smlSessionGetSource(self->priv->session) &&
	    smlSessionGetEstablished(self->priv->session))
	{
		sml_data_sync_session_set_local(self, smlSessionGetSource(self->priv->session));
	}
	if (!self->priv->remote &&
	    smlSessionGetTarget(self->priv->session) &&
	    smlSessionGetEstablished(self->priv->session))
	{
		sml_data_sync_session_set_remote(self, smlSessionGetTarget(self->priv->session));
	}

	switch (type) {
		case SML_MANAGER_SESSION_FLUSH:
			smlTrace(TRACE_INTERNAL, "%s: ignored event %d ", __func__, type);
			self->priv->state = SML_DATA_SYNC_STATE_CONNECTED;
			break;
		case SML_MANAGER_SESSION_ESTABLISHED:
			smlTrace(TRACE_INTERNAL, "%s: session established", __func__);
			self->priv->state = SML_DATA_SYNC_STATE_SESSION_READY;
			sml_data_sync_session_send_event(self, NULL, SML_DATA_SYNC_SESSION_EVENT_CONNECT, NULL);
			break;
		case SML_MANAGER_SESSION_NEW:
			smlTrace(TRACE_INTERNAL, "%s: Just received a new session with ID %d.",
				__func__, smlSessionGetSessionID(self->priv->session));
			break;
		case SML_MANAGER_SESSION_FINAL:
			smlTrace(TRACE_INTERNAL, "%s: Session %d reported final.",
				__func__, smlSessionGetSessionID(self->priv->session));

			/* determine which package was received */

			if (sml_data_sync_get_session_type(self->priv->data_sync) == SML_SESSION_TYPE_CLIENT)
			{
				/* only devinf receiving is not supported */
				if (self->priv->actual_package < SML_PACKAGE_1)
					self->priv->actual_package = SML_PACKAGE_1;
				else
					self->priv->actual_package += 2;
			} else { // SML_SESSION_TYPE_SERVER
				/* only devinf receiving is not supported */
				if (self->priv->actual_package < SML_PACKAGE_2)
					self->priv->actual_package = SML_PACKAGE_2;
				else
					self->priv->actual_package += 2;
			}
			smlTrace(TRACE_INTERNAL, "%s: package == %d", __func__, self->priv->actual_package);

			/* start callbacks etc. according to state */

			switch(self->priv->actual_package)
			{
				case SML_PACKAGE_1: /* SAN received by client */
					/* This is the best position to check for
					 * the availability of the remote device
					 * information. If it is not present then
					 * the own device information is send and
					 * the remote ones is requested.
					 */
					if (!sml_data_sync_dev_inf_manage_session(self, TRUE, &locerror))
						goto error;
       					if (!smlSessionFlush(self->priv->session, TRUE, &locerror))
						goto error;
					break;
				case SML_PACKAGE_2: /* alerts received by server */
					/* This is the best position to check for
					 * the availability of the remote device
					 * information. If it is not present then
					 * the own device information is send and
					 * the remote ones is requested.
					 */
					if (!sml_data_sync_dev_inf_manage_session(self, TRUE, &locerror))
						goto error;
					
       					if (!smlSessionFlush(self->priv->session, TRUE, &locerror))
						goto error;

					sml_data_sync_session_send_event(self, NULL, SML_DATA_SYNC_SESSION_EVENT_GOT_ALL_ALERTS, NULL);
					break;
				case SML_PACKAGE_3: /* alerts received by client */
					sml_data_sync_session_send_event(self, NULL, SML_DATA_SYNC_SESSION_EVENT_GOT_ALL_ALERTS, NULL);
					break;
				case SML_PACKAGE_4: /* syncs received by server */
					sml_data_sync_session_send_event(self, NULL, SML_DATA_SYNC_SESSION_EVENT_GOT_ALL_CHANGES, NULL);
					break;
				case SML_PACKAGE_5: /* syncs received by client */
					if (!sml_data_sync_session_send_mappings(self, &locerror))
						goto error;
       					if (!smlSessionFlush(self->priv->session, TRUE, &locerror))
						goto error;
					sml_data_sync_session_send_event(self, NULL, SML_DATA_SYNC_SESSION_EVENT_GOT_ALL_CHANGES, NULL);
					break;
				case SML_PACKAGE_6: /* map received by server */
       					if (!smlSessionFlush(self->priv->session, TRUE, &locerror))
						goto error;
					break;
				case SML_PACKAGE_END: /* end received by client */
					/* everything done */
					/* auto disconnect by library */
					break;
				default:
					g_set_error(&locerror, SML_ERROR, SML_ERROR_NOT_IMPLEMENTED,
						"The package %d is actually not supported.",
						self->priv->actual_package);
					goto error;
			}
			break;
		case SML_MANAGER_SESSION_END:
			smlTrace(TRACE_INTERNAL, "%s: Session %d has ended",
				__func__, smlSessionGetSessionID(self->priv->session));

			/* cache new anchors */

			o = self->priv->list_data_store_sessions;
			for (;o; o = o->next)
			{
				SmlDataSyncDataStoreSession *dsdss = o->data;
				if (!sml_data_sync_data_store_session_dump_anchors(dsdss, &locerror))
					goto error;
			}

			/* initiate disconnect */

			if (!sml_data_sync_disconnect_session (self->priv->data_sync, self->priv->session, &locerror))
				goto error;

			sml_data_sync_session_send_event(self, NULL, SML_DATA_SYNC_SESSION_EVENT_FINISHED, NULL);
			break;
		case SML_MANAGER_SESSION_ERROR:
			if (self->priv->session)
				smlTrace(TRACE_INTERNAL,
					"%s: There was an error in the session %d: %s",
					__func__,
					smlSessionGetSessionID(self->priv->session),
					error->message);
			else
				smlTrace(TRACE_INTERNAL,
					"There was a general error in the manager. %s",
					error->message);

			goto error;
			break;
		case SML_MANAGER_SESSION_WARNING:
			g_warning("WARNING: %s\n", error->message);
			break;
		default:
			g_warning("%s: Unknown event received: %d.", __func__, type);
			break;
	}
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
error:
	if (error && locerror) {
		/* The locerror is only a resulting error from the original error. */
		g_error_free(locerror);
		locerror = NULL;
	}
	if (locerror)
		error = locerror;
	sml_data_sync_session_send_event(self, NULL, SML_DATA_SYNC_SESSION_EVENT_ERROR, error);
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, error->message);
	if (locerror)
		g_error_free(locerror);
}
