/* sml_data_sync_data_store.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_data_store_private.h"
#include "sml_data_sync_defines.h"
#include "../sml_error_internals.h"

G_DEFINE_TYPE (SmlDataSyncDataStore, sml_data_sync_data_store, G_TYPE_OBJECT)

enum
{
	PROP_0,
	PROP_DATA_SYNC,
	PROP_REMOTE,
	PROP_LOCAL,
	PROP_CONTENT_TYPE
};

static void
sml_data_sync_data_store_get_property (GObject    *object,
                                       guint       property_id,
                                       GValue     *value,
                                       GParamSpec *pspec)
{
	switch (property_id) {
	case PROP_DATA_SYNC:
		g_value_set_object (value, SML_DATA_SYNC_DATA_STORE (object)->priv->data_sync);
		break;
	case PROP_REMOTE:
		g_value_set_object (value, SML_DATA_SYNC_DATA_STORE (object)->priv->remote);
		break;
	case PROP_LOCAL:
		g_value_set_object (value, SML_DATA_SYNC_DATA_STORE (object)->priv->local);
		break;
	case PROP_CONTENT_TYPE:
		g_value_set_string (value, SML_DATA_SYNC_DATA_STORE (object)->priv->content_type);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_data_sync_data_store_set_property (GObject      *object,
                                       guint         property_id,
                                       const GValue *value,
                                       GParamSpec   *pspec)
{
	switch (property_id) {
	case PROP_DATA_SYNC:
		SML_DATA_SYNC_DATA_STORE (object)->priv->data_sync = SML_DATA_SYNC (value);
		break;
	case PROP_REMOTE:
        	if (SML_DATA_SYNC_DATA_STORE (object)->priv->remote)
        		g_object_unref (SML_DATA_SYNC_DATA_STORE (object)->priv->remote);
		SML_DATA_SYNC_DATA_STORE (object)->priv->remote = SML_LOCATION (value);
		g_object_ref(SML_LOCATION (value));
		break;
	case PROP_LOCAL:
        	if (SML_DATA_SYNC_DATA_STORE (object)->priv->local)
        		g_object_unref (SML_DATA_SYNC_DATA_STORE (object)->priv->local);
		SML_DATA_SYNC_DATA_STORE (object)->priv->local = SML_LOCATION (value);
		g_object_ref(SML_LOCATION (value));
		break;
	case PROP_CONTENT_TYPE:
		g_free (SML_DATA_SYNC_DATA_STORE (object)->priv->content_type);
		SML_DATA_SYNC_DATA_STORE (object)->priv->content_type = g_strdup (g_value_get_string (value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_data_sync_data_store_finalize (GObject *object)
{
	SmlDataSyncDataStore *self = (SmlDataSyncDataStore *) object;

	if (self->priv->data_sync) {
		self->priv->data_sync = NULL;
	}

	if (self->priv->data_store_server) {
		g_object_unref(self->priv->data_store_server);
		self->priv->data_store_server = NULL;
	}

	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;
	}

	if (self->priv->content_type) {
		g_free(self->priv->content_type);
		self->priv->content_type = NULL;
	}

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

static void
sml_data_sync_data_store_class_init (SmlDataSyncDataStoreClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	g_type_class_add_private (klass, sizeof (SmlDataSyncDataStorePrivate));

	object_class->get_property = sml_data_sync_data_store_get_property;
	object_class->set_property = sml_data_sync_data_store_set_property;
	object_class->finalize     = sml_data_sync_data_store_finalize;

	/**
	 * SmlDataSyncDataStore:data_sync:
	 *
	 * The data_sync location property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_REMOTE,
	                                 g_param_spec_object ("data_sync",
	                                                      "SmlDataSync object",
	                                                      "SmlDataSync object",
	                                                      G_TYPE_OBJECT,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncDataStore:remote:
	 *
	 * The remote location property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_REMOTE,
	                                 g_param_spec_object ("remote",
	                                                      "remote location",
	                                                      "remote location",
	                                                      G_TYPE_OBJECT,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncDataStore:local:
	 *
	 * The local location property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_LOCAL,
	                                 g_param_spec_object ("local",
	                                                      "local location",
	                                                      "local location",
	                                                      G_TYPE_OBJECT,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDataSyncDataStore:content_type:
	 *
	 * The content-type property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_CONTENT_TYPE,
	                                 g_param_spec_string ("content_type",
	                                                      "content-type",
	                                                      "content-type",
	                                                      NULL,
	                                                      G_PARAM_READWRITE));

}

static void
sml_data_sync_data_store_init (SmlDataSyncDataStore *self)
{
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
	                                          SML_TYPE_DATA_SYNC_DATA_STORE,
	                                          SmlDataSyncDataStorePrivate);
}

/**
 * sml_data_sync_data_store_new:
 *
 * Creates a new instance of #SmlDataSyncDataStore.
 *
 * Return value: the newly created #SmlDataSyncDataStore instance
 */
SmlDataSyncDataStore*
sml_data_sync_data_store_new (void)
{
	return g_object_new (SML_TYPE_DATA_SYNC_DATA_STORE, NULL);
}

/**
 * sml_data_sync_data_store_get_data_sync:
 * @self: A #SmlDataSyncDataStore
 *
 * Gets the data sync property.
 *
 * Return value: 
 */
SmlDataSync*
sml_data_sync_data_store_get_data_sync (SmlDataSyncDataStore *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self), NULL);
	return self->priv->data_sync;
}

/**
 * sml_data_sync_data_store_set_data_sync:
 * @self: A #SmlDataSyncDataStore
 * @remote:
 *
 * Sets the data sync property.
 */
void
sml_data_sync_data_store_set_data_sync (SmlDataSyncDataStore *self,
                                        SmlDataSync* ds)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));

	/* The data sync can be NULL if this function is called from
	 * the finalize function of the data sync object.
	 * This is done to avoid invalid references.
	 */

	self->priv->data_sync = ds;
}

/**
 * sml_data_sync_data_store_get_remote:
 * @self: A #SmlDataSyncDataStore
 *
 * Gets the remote location property.
 *
 * Return value: 
 */
SmlLocation*
sml_data_sync_data_store_get_remote (SmlDataSyncDataStore *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self), NULL);
	return self->priv->remote;
}

/**
 * sml_data_sync_data_store_set_remote:
 * @self: A #SmlDataSyncDataStore
 * @remote:
 *
 * Sets the remote location property.
 */
void
sml_data_sync_data_store_set_remote (SmlDataSyncDataStore *self,
                                     SmlLocation* remote)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (G_IS_OBJECT (remote));

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

/**
 * sml_data_sync_data_store_get_local:
 * @self: A #SmlDataSyncDataStore
 *
 * Gets the local location property.
 *
 * Return value: 
 */
SmlLocation*
sml_data_sync_data_store_get_local (SmlDataSyncDataStore *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self), NULL);
	return self->priv->local;
}

/**
 * sml_data_sync_data_store_set_local:
 * @self: A #SmlDataSyncDataStore
 * @local:
 *
 * Sets the local location property.
 */
void
sml_data_sync_data_store_set_local (SmlDataSyncDataStore *self,
                                    SmlLocation* local)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (G_IS_OBJECT (local));

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

/**
 * sml_data_sync_data_store_get_content_type:
 * @self: A #SmlDataSyncDataStore
 *
 * Gets the content-type property.
 *
 * Return value: 
 */
G_CONST_RETURN gchar*
sml_data_sync_data_store_get_content_type (SmlDataSyncDataStore *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self), NULL);
	return self->priv->content_type;
}

/**
 * sml_data_sync_data_store_set_content_type:
 * @self: A #SmlDataSyncDataStore
 * @content_type:
 *
 * Sets the content-type property.
 */
void
sml_data_sync_data_store_set_content_type (SmlDataSyncDataStore *self,
                                           const gchar* content_type)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_free (self->priv->content_type);
	self->priv->content_type = g_strdup (content_type);
}

/**
 * sml_data_sync_data_store_set_remote_uri:
 * @self: A #SmlDataSyncDataStore
 *
 * 
 */
void
sml_data_sync_data_store_set_remote_uri (SmlDataSyncDataStore *self,
                                         const gchar *uri)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	if (!self->priv->remote)
		self->priv->remote = sml_location_new();
	sml_location_set_uri(self->priv->remote, uri);
}

/**
 * sml_data_sync_data_store_get_local_uri:
 * @self: A #SmlDataSyncDataStore
 *
 * 
 */
G_CONST_RETURN gchar*
sml_data_sync_data_store_get_local_uri (SmlDataSyncDataStore *self)
{
	g_return_val_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self), NULL);
	if (!self->priv->local)
		return NULL;
	return sml_location_get_uri(self->priv->local);
}

/**
 * sml_data_sync_data_store_set_local_uri:
 * @self: A #SmlDataSyncDataStore
 *
 * 
 */
void
sml_data_sync_data_store_set_local_uri (SmlDataSyncDataStore *self,
                                        const gchar *uri)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	if (!self->priv->local)
		self->priv->local = sml_location_new();
	sml_location_set_uri(self->priv->local, uri);
}

/**
 * sml_data_sync_data_store_initialize:
 * @self: A #SmlDataSyncDataStore
 *
 * 
 */
gboolean
sml_data_sync_data_store_initialize (SmlDataSyncDataStore *self,
                                     GError **error)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self), FALSE, error, SML_ERROR_GENERIC, "There must be a SmlDataSyncDataStore object.");
	sml_return_val_error_if_fail (self->priv->data_sync, FALSE, error, SML_ERROR_GENERIC, "The data sync object is missing.");
	// sml_return_val_error_if_fail (self->priv->remote, FALSE, error, SML_ERROR_GENERIC, "The remote location is missing.");
	sml_return_val_error_if_fail (self->priv->local, FALSE, error, SML_ERROR_GENERIC, "The local location is missing.");
	sml_return_val_error_if_fail (self->priv->content_type, FALSE, error, SML_ERROR_GENERIC, "The content-type is missing.");

	gchar *lcCT = NULL;
	gchar *lcSource = NULL;
	SmlTransportType tsp_type = sml_data_sync_get_transport_type(self->priv->data_sync);

	/* especially Samsung devices need the datastores during connect */

	if (tsp_type == SML_TRANSPORT_OBEX_CLIENT)
	{
		smlTrace(TRACE_INTERNAL, "%s: initializing content types for Samsung", __func__);
		lcCT = g_ascii_strdown(self->priv->content_type,
		                       strlen(self->priv->content_type));
		lcSource = g_utf8_strdown(sml_location_get_uri(self->priv->local),
		                          strlen(sml_location_get_uri(self->priv->local)));
		if (strstr(lcCT, "vcard") &&
		    !sml_data_sync_set_option(
		        self->priv->data_sync,
		        SML_TRANSPORT_CONFIG_DATASTORE,
		        SML_TRANSPORT_CONFIG_DATASTORE_CONTACT,
		        error))
		{
			goto error;
		}
		if (strstr(lcCT, "calendar") &&
		    ( strstr(lcSource, "cal") ||
		      strstr(lcSource, "event")
		    )&&
		    !sml_data_sync_set_option(
		        self->priv->data_sync,
		        SML_TRANSPORT_CONFIG_DATASTORE,
		        SML_TRANSPORT_CONFIG_DATASTORE_EVENT,
		        error))
		{
			goto error;
		}
		if (strstr(lcCT, "calendar") &&
		    strstr(lcSource, "todo") &&
		    !sml_data_sync_set_option(
		        self->priv->data_sync,
		        SML_TRANSPORT_CONFIG_DATASTORE,
		        SML_TRANSPORT_CONFIG_DATASTORE_TODO,
		        error))
		{
			goto error;
		}
		if (strstr(lcCT, "text/plain") &&
		    !sml_data_sync_set_option(
		        self->priv->data_sync,
		        SML_TRANSPORT_CONFIG_DATASTORE,
		        SML_TRANSPORT_CONFIG_DATASTORE_NOTE,
		        error))
		{
			goto error;
		}
		smlSafeCFree(&lcCT);
		smlSafeCFree(&lcSource);
	}

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
error:
	if (lcCT)
		smlSafeCFree(&lcCT);
	if (lcSource)
		smlSafeCFree(&lcSource);
	smlTrace(TRACE_EXIT, "%s - FALSE", __func__);
	return FALSE;
}

/**
 * sml_data_sync_data_store_register_handle_get_alert_type_callback:
 * @self: A #SmlDataSyncDataStore
 * @callback: A #SmlDataSyncSessionGetAlertTypeCallback
 * @userdata: A #gpointer
 * 
 */
void
sml_data_sync_data_store_register_get_alert_type_callback (SmlDataSyncDataStore *self,
                                                           SmlDataSyncDataStoreSessionGetAlertTypeCallback callback,
                                                           void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (callback);

	self->priv->get_alert_type_callback = callback;
	self->priv->get_alert_type_userdata = userdata;
}

/**
 * sml_data_sync_data_store_register_change_callback:
 * @self: A #SmlDataSyncDataStore
 * @callback: A #SmlDataSyncSessionChangeCallback
 * @userdata: A #gpointer
 * 
 */
void
sml_data_sync_data_store_register_change_callback (SmlDataSyncDataStore *self,
                                                   SmlDataSyncDataStoreSessionChangeCallback callback,
                                                   void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (callback);

	self->priv->change_callback = callback;
	self->priv->change_userdata = userdata;
}

/**
 * sml_data_sync_data_store_register_change_status_callback:
 * @self: A #SmlDataSyncDataStore
 * @callback: A #SmlDataSyncSessionChangeStatusCallback
 * @userdata: A #gpointer
 * 
 */
void
sml_data_sync_data_store_register_change_status_callback (SmlDataSyncDataStore *self,
                                                          SmlDataSyncDataStoreSessionChangeStatusCallback callback,
                                                          void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (callback);

	self->priv->change_status_callback = callback;
	self->priv->change_status_userdata = userdata;
}

/**
 * sml_data_sync_data_store_register_mapping_callback:
 * @self: A #SmlDataSyncDataStore
 * @callback: A #SmlDataSyncSessionMappingCallback
 * @userdata: A #gpointer
 * 
 */
void
sml_data_sync_data_store_register_mapping_callback (SmlDataSyncDataStore *self,
                                                    SmlDataSyncDataStoreSessionMappingCallback callback,
                                                    void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (callback);

	self->priv->mapping_callback = callback;
	self->priv->mapping_userdata = userdata;
}

/**
 * sml_data_sync_data_store_register_mapping_status_callback:
 * @self: A #SmlDataSyncDataStore
 * @callback: A #SmlDataSyncSessionMappingStatusCallback
 * @userdata: A #gpointer
 * 
 */
void
sml_data_sync_data_store_register_mapping_status_callback (SmlDataSyncDataStore *self,
                                                           SmlDataSyncDataStoreSessionMappingStatusCallback callback,
                                                           void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (callback);

	self->priv->mapping_status_callback = callback;
	self->priv->mapping_status_userdata = userdata;
}

/**
 * sml_data_sync_data_store_register_get_anchor_callback:
 * @self: A #SmlDataSyncDataStore
 * @callback: A #SmlDataSyncSessionGetAnchorCallback
 * @userdata: A #gpointer
 * 
 */
void
sml_data_sync_data_store_register_get_anchor_callback (SmlDataSyncDataStore *self,
                                                       SmlDataSyncDataStoreSessionGetAnchorCallback callback,
                                                       void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (callback);

	self->priv->get_anchor_callback = callback;
	self->priv->get_anchor_userdata = userdata;
}

/**
 * sml_data_sync_data_store_register_set_anchor_callback:
 * @self: A #SmlDataSyncDataStore
 * @callback: A #SmlDataSyncSessionSetAnchorCallback
 * @userdata: A #gpointer
 * 
 */
void
sml_data_sync_data_store_register_set_anchor_callback (SmlDataSyncDataStore *self,
                                                       SmlDataSyncDataStoreSessionSetAnchorCallback callback,
                                                       void *userdata)
{
	g_return_if_fail (SML_IS_DATA_SYNC_DATA_STORE (self));
	g_return_if_fail (callback);

	self->priv->set_anchor_callback = callback;
	self->priv->set_anchor_userdata = userdata;
}

SmlDataSyncDataStoreSessionChangeCallback
sml_data_sync_data_store_get_change_callback (SmlDataSyncDataStore *self)
{
	return self->priv->change_callback;
}

void*
sml_data_sync_data_store_get_change_userdata (SmlDataSyncDataStore *self)
{
	return self->priv->change_userdata;
}

SmlDataSyncDataStoreSessionChangeStatusCallback
sml_data_sync_data_store_get_change_status_callback (SmlDataSyncDataStore *self)
{
	return self->priv->change_status_callback;
}

void*
sml_data_sync_data_store_get_change_status_userdata (SmlDataSyncDataStore *self)
{
	return self->priv->change_status_userdata;
}

SmlDataSyncDataStoreSessionMappingCallback
sml_data_sync_data_store_get_mapping_callback (SmlDataSyncDataStore *self)
{
	return self->priv->mapping_callback;
}

void*
sml_data_sync_data_store_get_mapping_userdata (SmlDataSyncDataStore *self)
{
	return self->priv->mapping_userdata;
}

SmlDataSyncDataStoreSessionMappingStatusCallback
sml_data_sync_data_store_get_mapping_status_callback (SmlDataSyncDataStore *self)
{
	return self->priv->mapping_status_callback;
}

void*
sml_data_sync_data_store_get_mapping_status_userdata (SmlDataSyncDataStore *self)
{
	return self->priv->mapping_status_userdata;
}

SmlDataSyncDataStoreSessionSetAnchorCallback
sml_data_sync_data_store_get_set_anchor_callback (SmlDataSyncDataStore *self)
{
	return self->priv->set_anchor_callback;
}

void*
sml_data_sync_data_store_get_set_anchor_userdata (SmlDataSyncDataStore *self)
{
	return self->priv->set_anchor_userdata;
}

