/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2008-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 Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include "data_sync_loop.h"
#include "sml_data_sync_private.h"

#include "sml_data_sync_session_internals.h"

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

static gboolean sml_data_sync_loop_prepare(GSource *source, gint *timeout_)
{
	smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, source, timeout_);
        *timeout_ = 50;
        return FALSE;
}

static void sml_data_sync_loop_check_session (gpointer key, gpointer value, void *userdata)
{
	SmlDataSyncSession *session = key;
	/* value is the same like the key */
	gboolean *result = userdata;
	if (sml_data_sync_session_check(session))
		*result = TRUE;
}

static gboolean sml_data_sync_loop_check(GSource *source)
{
	SmlDataSync *self = *((SmlDataSync **)(source + 1));

	/* Is there a waiting event? */

	if (smlManagerCheck(self->priv->manager))
		return TRUE;

	/* Every data store session of every data sync session must be checked. */

	gboolean result = FALSE;

	g_hash_table_foreach(self->priv->data_sync_sessions, sml_data_sync_loop_check_session, &result);

	return result;
}

static void sml_data_sync_loop_check_dispatch (gpointer key, gpointer value, void *userdata)
{
	smlTrace(TRACE_INTERNAL, "%s(%p)", __func__, key);
	SmlDataSyncSession *session = key;
	/* value is the same like the key */
	sml_data_sync_session_dispatch(session);
}

static gboolean sml_data_sync_loop_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
	smlTrace(TRACE_INTERNAL, "%s(%p, %p, %p)", __func__, source, callback, user_data);
	SmlDataSync *self = user_data;

	/* Every data store session of every data sync session must be checked. */

	g_hash_table_foreach(self->priv->data_sync_sessions, sml_data_sync_loop_check_dispatch, NULL);

	smlManagerDispatch(self->priv->manager);

	return TRUE;
}

gboolean sml_data_sync_loop_start (SmlDataSync *self,
                                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	CHECK_ERROR_REF

	/* prepare function */

	self->priv->functions = smlTryMalloc0(sizeof(GSourceFuncs), error);
	if (!self->priv->functions)
		goto error;

	self->priv->functions->prepare  = sml_data_sync_loop_prepare;
	self->priv->functions->check    = sml_data_sync_loop_check;
	self->priv->functions->dispatch = sml_data_sync_loop_dispatch;
	self->priv->functions->finalize = NULL;

	/* prepare context and thread */

	self->priv->context = g_main_context_new();
	if (!self->priv->context)
		goto error;
	self->priv->thread = smlThreadNew(self->priv->context, error);
	if (!self->priv->thread)
		goto error;
	smlThreadStart(self->priv->thread);

	/* prepare source for thread's main loop */

	self->priv->source = g_source_new(self->priv->functions, sizeof(GSource) + sizeof(SmlDataSync *));
	SmlDataSync **ptr = (SmlDataSync **)(self->priv->source + 1);
	*ptr = self;
	g_source_set_callback(self->priv->source, NULL, self, NULL);
	g_source_attach(self->priv->source, self->priv->context);
	g_main_context_ref(self->priv->context);
	
	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
	return FALSE;
}

void sml_data_sync_loop_stop (SmlDataSync *self)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);

	/* stop thread */
	smlThreadStop(self->priv->thread);
	smlThreadFree(self->priv->thread);
	self->priv->thread = NULL;

	/* detach source */
	g_source_unref(self->priv->source);
	self->priv->source = NULL;

	/* free context */
	g_main_context_unref(self->priv->context);
	self->priv->context = NULL;

	/* free functions */
	smlSafeFree((gpointer *)&(self->priv->functions));
	self->priv->functions = NULL;

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
}

