/*
 * libopensync - A synchronization framework
 * Copyright (C) 2004-2005  Armin Bauer <armin.bauer@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 "opensync.h"
#include "opensync_internals.h"

/**
 * @defgroup OSyncChangePrivate OpenSync Change Internals
 * @ingroup OSyncPrivate
 * @brief The internals of the OSyncChange
 * 
 */
/*@{*/

/** Increment the reference count of a change 
 * 
 * @param change The change to ref
 **/
void osync_change_ref(OSyncChange *change)
{
	g_assert(change);
	change->refcount++;
}

/** Decrement the reference count of a change 
 * 
 * @param change The change to decref
 **/
void osync_change_decref(OSyncChange *change)
{
	g_assert(change);
	change->refcount--;
	if (change->refcount >= 0)
		osync_change_free(change);
}

/** Get the name of the source object type of a change 
 * 
 * @param change The change
 * @returns The name of the source object type
 **/
const char *osync_change_get_sourceobjtype(OSyncChange *change)
{
	g_assert(change);
	return change->sourceobjtype;
}

/** Get the name of the inital format of a change 
 * 
 * @param change The change
 * @returns The name of the inital format
 **/
OSyncObjFormat *osync_change_get_initial_objformat(OSyncChange *change)
{
	g_assert(change);
	if (change->initial_format)
		return change->initial_format;
	
	if (!change->initial_format)
		return NULL;
	
	osync_assert_msg(change->conv_env, "The conv env of the change must be set by calling member_set or conv_env_set");
	change->initial_format = osync_conv_find_objformat(change->conv_env, change->initial_format_name);
	return change->initial_format;
}

/*@}*/

/**
 * @defgroup OSyncChange OpenSync Change
 * @ingroup OSyncPublic
 * @brief The public API of the OSyncChange
 * 
 */
/*@{*/

/*! @brief Spawns a new change object
 * 
 * @returns Newly allocated change object
 * 
 */
OSyncChange *osync_change_new(void)
{
	osync_trace(TRACE_ENTRY, "%s()", __func__);
	
	OSyncChange *change = g_malloc0(sizeof(OSyncChange));
	change->refcount = 1;
	
	osync_trace(TRACE_EXIT, "%s: %p", __func__, change);
	return change;
}

/*! @brief Frees a change
 * 
 * @param change The change to free
 * 
 */
void osync_change_free(OSyncChange *change)
{
	osync_trace(TRACE_ENTRY, "%s(%p)", __func__, change);
	g_assert(change);
	
	//FIXME cleanly release the change!
	g_free(change);

	osync_trace(TRACE_EXIT, "%s", __func__);
}

/*! @brief Frees the data of a change
 * 
 * This frees the data of a change but does not free
 * the change itself
 * 
 * @param change The change of which to free the data
 * 
 */
void osync_change_free_data(OSyncChange *change)
{
	g_assert(change);
	g_assert(osync_change_get_objformat(change));
	if (!osync_change_get_objformat(change)->destroy_func)
		osync_debug("OSCONV", 1, "Memory leak: can't free data of type %s", osync_change_get_objformat(change)->name);
	else {
		osync_debug("OSCONV", 4, "Freeing data of type %s", osync_change_get_objformat(change)->name);
		osync_change_get_objformat(change)->destroy_func(change->data, change->size);
	}
	change->data = NULL;
	change->size = 0;
	//FIXME Set format to NULL here?
}

/*! @brief Resets a change
 * 
 * Resets the information about changetype, hash, data etc.
 * 
 * @param change The change to reset
 * 
 */
void osync_change_reset(OSyncChange *change)
{
	osync_trace(TRACE_ENTRY, "%s(%p)", __func__, change);
	
	if (change->hash)
		g_free(change->hash);
	change->hash = NULL;
	//FIXME Release data
	change->data = NULL;
	change->size = 0;
	change->has_data = FALSE;
	change->changetype = CHANGE_UNKNOWN;
	//change->sourceobjtype = NULL;
	//change->destobjtype = NULL;
	
	osync_trace(TRACE_EXIT, "%s", __func__);
}

/*! @brief This will save a change into the database
 * 
 * @param change The change to save
 * @param save_format Wether to save the format or not.
 * @param error A pointer to a error struct
 * @returns TRUE if save was successfull, FALSE otherwise
 * 
 */
osync_bool osync_change_save(OSyncChange *change, osync_bool save_format, OSyncError **error)
{
	if (!change->changes_db)
		change->changes_db = change->member->group->changes_db;
	return osync_db_save_change(change, save_format, error);
}

/*! @brief This will delete a change from the database
 * 
 * @param change The change to delete
 * @param error A pointer to a error struct
 * @returns TRUE if deletion was successfull, FALSE otherwise
 * 
 */
osync_bool osync_change_delete(OSyncChange *change, OSyncError **error)
{
	return osync_db_delete_change(change, error);
}

/*! @brief This will load the changes from the database
 * 
 * This opens the change database and returns an array with
 * the changes. The changes have to be freed later. The database has
 * to be closed with a call to osync_changes_close()
 * 
 * @param group The group for which to load the changes
 * @param changes An pointer to an array in which to store the changes
 * @param error A pointer to a error struct
 * @returns TRUE if load was successfull, FALSE otherwise
 * 
 */
osync_bool osync_changes_load(OSyncGroup *group, OSyncChange ***changes, OSyncError **error)
{
	return osync_db_open_changes(group, changes, error);
}

/*! @brief Closes the change database
 * 
 * @param group The group for which to close the database
 * 
 */
void osync_changes_close(OSyncGroup *group)
{
	osync_db_close_changes(group);
}

/*! @brief Gets the member which reported a change
 * 
 * @param change The change
 * @returns The member of the change
 * 
 */
OSyncMember *osync_change_get_member(OSyncChange *change)
{
	g_assert(change);
	return change->member;
}

/*! @brief Sets the member of a change
 * 
 * @param change The change
 * @param member The member of the change
 * 
 */
void osync_change_set_member(OSyncChange *change, OSyncMember *member)
{
	g_assert(change);
	change->member = member;
	change->conv_env = member->group->conv_env;
}

/*! @brief Sets the conversion environment of a change
 * 
 * @param change The change
 * @param env The conversion environment
 * 
 */
void osync_change_set_conv_env(OSyncChange *change, OSyncFormatEnv *env)
{
	g_assert(change);
	change->conv_env = env;
}

/*! @brief Gets the object type of a change
 * 
 * @param change The change
 * @returns The object type
 * 
 */
OSyncObjType *osync_change_get_objtype(OSyncChange *change)
{
	g_assert(change);
	
	if (change->objtype)
		return change->objtype;
	
	if (!change->objtype_name) {
		OSyncObjFormat *format = osync_change_get_objformat(change);
		if (!format)
			return NULL;
		change->objtype = format->objtype;
		return format->objtype;
	}
	
	osync_assert_msg(change->conv_env, "The conv env of the change must be set by calling member_set or conv_env_set");
	change->objtype = osync_conv_find_objtype(change->conv_env, change->objtype_name);
	return change->objtype;
}

/*! @brief Sets the object type of a change
 * 
 * @param change The change
 * @param type The object type
 * 
 */
void osync_change_set_objtype(OSyncChange *change, OSyncObjType *type)
{
	g_assert(change);
	change->objtype = type;
}

/*! @brief Sets the object type of a change from the name
 * 
 * @param change The change
 * @param name The object type name
 * 
 */
void osync_change_set_objtype_string(OSyncChange *change, const char *name)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %s)", __func__, change, name);
	
	g_assert(change);
	if (change->objtype_name)
		g_free(change->objtype_name);
	change->objtype_name = g_strdup(name);
	//Invalidate the previous object type
	change->objtype = NULL;
	
	osync_trace(TRACE_EXIT, "%s", __func__);
}

/*! @brief Gets the object format of a change
 * 
 * @param change The change
 * @returns The object format
 * 
 */
OSyncObjFormat *osync_change_get_objformat(OSyncChange *change)
{
	osync_trace(TRACE_ENTRY, "%s(%p)", __func__, change);
	g_assert(change);
	
	if (change->format) {
		osync_trace(TRACE_EXIT, "%s: %p", __func__, change->format);
		return change->format;
	}
	
	if (!change->format_name) {
		osync_trace(TRACE_EXIT, "%s: No name yet", __func__);
		return NULL;
	}
	
	osync_assert_msg(change->conv_env, "The conv env of the change must be set by calling member_set or conv_env_set");
	change->format = osync_conv_find_objformat(change->conv_env, change->format_name);
	
	osync_trace(TRACE_EXIT, "%s: %p", __func__, change->format);
	return change->format;
}

/*! @brief Sets the object format of a change
 * 
 * @param change The change
 * @param objformat The object format
 * 
 */
void osync_change_set_objformat(OSyncChange *change, OSyncObjFormat *objformat)
{
	g_assert(change);
	change->format = objformat;
	if (objformat)
		change->objtype = objformat->objtype;
	else
		change->objtype = NULL;
}

/*! @brief Sets the object format of a change from the name
 * 
 * @param change The change
 * @param name The object format name
 * 
 */
void osync_change_set_objformat_string(OSyncChange *change, const char *name)
{
	osync_trace(TRACE_ENTRY, "%s(%p, %s)", __func__, change, name);
	
	g_assert(change);
	if (change->format_name)
		g_free(change->format_name);
	change->format_name = g_strdup(name);
	//Invalidate the previous format
	change->format = NULL;
	
	osync_trace(TRACE_EXIT, "%s", __func__);
}

/*! @brief Gets the changetype of a change
 * 
 * @param change The change
 * @returns The changetype
 * 
 */
OSyncChangeType osync_change_get_changetype(OSyncChange *change)
{
	if (!change)
		return CHANGE_UNKNOWN;
	
	return change->changetype;
}

/*! @brief Sets the changetype of a change
 * 
 * @param change The change
 * @param type The changetype to set
 * 
 */
void osync_change_set_changetype(OSyncChange *change, OSyncChangeType type)
{
	g_assert(change);
	change->changetype = type;
}

/*! @brief Sets the hash of a change that is used to decide wether a change is new, modifed etc
 * 
 * @param change The change
 * @param hash The hash to set
 * 
 */
void osync_change_set_hash(OSyncChange *change, const char *hash)
{
	g_assert(change);
	if (change->hash)
		g_free(change->hash);
	change->hash = g_strdup(hash);
}

/*! @brief Gets the hash of a change
 * 
 * @param change The change
 * @returns The hash
 * 
 */
const char *osync_change_get_hash(OSyncChange *change)
{
	g_assert(change);
	return change->hash;
}

/*! @brief Sets the uid of a change
 * 
 * @param change The change
 * @param uid The uid to set
 * 
 */
void osync_change_set_uid(OSyncChange *change, const char *uid)
{
	g_assert(change);
	if (change->uid)
		g_free(change->uid);
	change->uid = g_strdup(uid);
}

/*! @brief Gets the uid of a change
 * 
 * @param change The change
 * @returns The uid
 * 
 */
const char *osync_change_get_uid(OSyncChange *change)
{
	g_assert(change);
	return change->uid;
}

/*! @brief Sets the data of a change
 * 
 * @param change The change
 * @param data The data to set
 * @param size The size of the data to set
 * @param has_data Set this to TRUE of this already is the complete data
 * 
 */
void osync_change_set_data(OSyncChange *change, char *data, int size, osync_bool has_data)
{
	change->data = data;
	change->size = size;
	change->has_data = has_data;
}

/*! @brief Returns wether the complete data already has been set
 * 
 * @param change The change
 * @returns TRUE if the change has the complete data set
 * 
 */
osync_bool osync_change_has_data(OSyncChange *change)
{
	g_assert(change);
	return change->has_data;
}

/*! @brief Gets the data of a change
 * 
 * @param change The change
 * @returns The data
 * 
 */
char *osync_change_get_data(OSyncChange *change)
{
	g_assert(change);
	return change->data;
}

/*! @brief Gets the size of the data of a change
 * 
 * @param change The change
 * @returns The size of the data
 * 
 */
int osync_change_get_datasize(OSyncChange *change)
{
	g_assert(change);
	return change->size;
}

/*! @brief Gets the mappingid of a change
 * 
 * @param change The change
 * @returns The mappingid of the data
 * 
 */
long long int osync_change_get_mappingid(OSyncChange *change)
{
	g_assert(change);
	return change->mappingid;
}

/*! @brief Sets the mappingid of a change
 * 
 * @param change The change
 * @param mappingid The mappingid to set
 * 
 */
void osync_change_set_mappingid(OSyncChange *change, long long int mappingid)
{
	g_assert(change);
	change->mappingid = mappingid;
}

/*! @brief Gets data that can be used privately by the engine
 * 
 * @param change The change
 * @returns The data of the engine
 * 
 */
void *osync_change_get_engine_data(OSyncChange *change)
{
	g_assert(change);
	return change->engine_data;
}

/*! @brief Sets the data of the engine
 * 
 * @param change The change
 * @param engine_data The data
 * 
 */
void osync_change_set_engine_data(OSyncChange *change, void *engine_data)
{
	g_assert(change);
	change->engine_data = engine_data;
}

/*! @brief Gets the id of the change which is always unique
 * 
 * @param change The change
 * @returns The id
 * 
 */
long long int osync_change_get_id(OSyncChange *change)
{
	g_assert(change);
	return change->id;
}

/*! @brief Updated one change from another change
 * 
 * This function can be used to "merge" the information from 2
 * changes into one. The uid, hash, data of the target change
 * are overwriten by those of the source change if they are not set already
 * on the target. The data of the source change is copied.
 * 
 * @param source The source change
 * @param target The target change
 * 
 */
void osync_change_update(OSyncChange *source, OSyncChange *target)
{
	osync_trace(TRACE_ENTRY, "osync_change_update(%p, %p)", source, target);
	//FIXME free stuff
	g_assert(source);
	g_assert(target);
	if (!target->uid)
		target->uid = g_strdup(source->uid);
	target->hash = g_strdup(source->hash);
	
	OSyncError *error = NULL;
	if (!osync_change_copy_data(source, target, &error)) {
		osync_trace(TRACE_INTERNAL, "unable to copy change: %s", osync_error_print(&error));
		osync_error_free(&error);
	}
	
	target->has_data = source->has_data;
	target->changetype = source->changetype;
	if (source->format)
		target->format = osync_change_get_objformat(source);
	if (source->objtype) {
		target->objtype = osync_change_get_objtype(source);
		target->sourceobjtype = g_strdup(osync_change_get_objtype(source)->name);
	}
	
	target->changes_db = source->changes_db;
	
	osync_trace(TRACE_EXIT, "osync_change_update");
}

/*@}*/


syntax highlighted by Code2HTML, v. 0.9.1