/*
 * 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"

#include "opensync_error_internals.h"

/**
 * @defgroup OSyncErrorPrivateAPI OpenSync Error Internals
 * @ingroup OSyncPrivate
 * @brief The public API of opensync
 * 
 * This gives you an insight in the public API of opensync.
 * 
 */
/*@{*/

/*! @brief Translate a error type into something human readable
 * 
 * @param type The error type to look up
 * @returns The name of the error type
 * 
 */
static const char *osync_error_name_from_type(OSyncErrorType type)
{
	switch (type) {
		case OSYNC_NO_ERROR:
			return "NoError";
		case OSYNC_ERROR_GENERIC:
			return "UnknownError";
		case OSYNC_ERROR_IO_ERROR:
			return "IOError";
		case OSYNC_ERROR_NOT_SUPPORTED:
			return "NotSupported";
		case OSYNC_ERROR_TIMEOUT:
			return "Timeout";
		case OSYNC_ERROR_DISCONNECTED:
			return "Disconnected";
		case OSYNC_ERROR_FILE_NOT_FOUND:
			return "FileNotFound";
		default:
			return "UnspecifiedError";
	}
}

/*! @brief Sets a error from a va_list
 * 
 * @param error A pointer to a error struct
 * @param type The type to set
 * @param format The message
 * @param args The arguments to the message
 * 
 */
void osync_error_set_vargs(OSyncError **error, OSyncErrorType type, const char *format, va_list args)
{
	osync_return_if_fail(error);
	osync_return_if_fail(osync_error_is_set(error) == FALSE);
	osync_return_if_fail(format);

	*error = g_malloc0(sizeof(OSyncError));
	(*error)->message = g_strdup_vprintf(format, args);
	(*error)->type = type;
	(*error)->ref_count = 1;
	
	return;
}

/*@}*/

/**
 * @defgroup OSyncErrorAPI OpenSync Errors
 * @ingroup OSyncPublic
 * @brief OpenSync's error reporting facilities
 * 
 */
/*@{*/


/*! @brief This will return a string describing the type of the error
 * 
 * @param error A pointer to a error struct
 * @returns The description, NULL on error
 * 
 */
const char *osync_error_get_name(OSyncError **error)
{
	osync_return_val_if_fail(error != NULL, NULL);
	if (!*error)
		return osync_error_name_from_type(OSYNC_NO_ERROR);
	return osync_error_name_from_type((*error)->type);
}

void osync_error_ref(OSyncError **error)
{
	if (!osync_error_is_set(error))
		return;
	
	g_atomic_int_inc(&(*error)->ref_count);
}

void osync_error_unref(OSyncError **error)
{
	if (!osync_error_is_set(error))
		return;
		
	if (g_atomic_int_dec_and_test(&(*error)->ref_count)) {
		if ((*error)->message)
			g_free ((*error)->message);
		
		if ((*error)->child)
			osync_error_unref(&((*error)->child));
		
		g_free(*error);
	}
	
	*error = NULL;
}

/*! @brief Checks if the error is set
 * 
 * @param error A pointer to a error struct to check
 * @returns TRUE if the error is set, FALSE otherwise
 * 
 */
osync_bool osync_error_is_set (OSyncError **error)
{
	if (!error)
		return FALSE;
		
	if (*error == NULL)
		return FALSE;
	
	if ((*error)->type)
		return TRUE;
		
	return FALSE;
}

/*! @brief Returns the type of the error
 * 
 * @param error The error
 * @returns The type of the error or OSYNC_NO_ERROR if no error
 * 
 */
OSyncErrorType osync_error_get_type(OSyncError **error)
{
	if (!osync_error_is_set(error))
		return OSYNC_NO_ERROR;
	return (*error)->type;
}

/*! @brief Returns the message of the error
 * 
 * @param error The error to print
 * @returns The message of the error or NULL if no error
 * 
 */
const char *osync_error_print(OSyncError **error)
{
	if (!osync_error_is_set(error))
		return NULL;
	return (*error)->message;
}

char *osync_error_print_stack(OSyncError **error)
{
	if (!osync_error_is_set(error))
		return NULL;
		
	char *submessage = NULL;
	if ((*error)->child)
		submessage = osync_error_print_stack(&((*error)->child));
	
	char *message = NULL;
	if (submessage) {
		message = g_strdup_printf("NEXT ERROR: \"%s\"; %s", (*error)->message, submessage);
		g_free(submessage);
	} else
		message = g_strdup_printf("ROOT CAUSE: \"%s\"", (*error)->message);
	
	return message;
}

/*! @brief Duplicates the error into the target
 * 
 * 
 * @param target The target error to update
 * @param source The source error which to duplicate
 * 
 */
void osync_error_set_from_error(OSyncError **target, OSyncError **source)
{
	if (!target || osync_error_is_set(target))
		return;
	
	if (!osync_error_is_set(source)) {
		*target = NULL;
		return;
	}
	
	*target = *source;
	osync_error_ref(target);
}

/*! @brief Sets the error
 * 
 * You can use this function to set the error to the given type and message
 * 
 * @param error A pointer to a error struct to set
 * @param type The Error type to set
 * @param format The message
 * 
 */
void osync_error_set(OSyncError **error, OSyncErrorType type, const char *format, ...)
{
	va_list args;
	va_start(args, format);
	osync_error_set_vargs(error, type, format, args);
	va_end (args);
}

void osync_error_stack(OSyncError **parent, OSyncError **child)
{
	if (!parent || !*parent)
		return;
	
	if (!child || !*child)
		return;
	
	if ((*parent)->child)
		osync_error_unref(&((*parent)->child));
	
	(*parent)->child = *child;
	osync_error_ref(child);
}

OSyncError *osync_error_get_child(OSyncError **parent)
{
	if (!parent || !*parent)
		return NULL;
	
	return (*parent)->child;
}

/*! @brief Sets the type of an error
 * 
 * @param error A pointer to a error struct to set
 * @param type The Error type to set
 * 
 */
void osync_error_set_type(OSyncError **error, OSyncErrorType type)
{
	if (!error)
		return;
	
	(*error)->type = type;
	return;
}

/*@}*/
