/*
 * rdfcommon.c
 * Routines common to the rdf-related parts of the server.
 *
 * Copyright (C) Leni Mayo 2002
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License, version 2, as
 *	published by the Free Software Foundation.
 *
 *	This program 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 General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h> /* for free() */
#include "servercommon.h"
#include "rdfcommon.h"

/* Global variable, a handle to all resources used by the Redland library.
*/
librdf_world *pWorld = NULL;

const char * pszFileScheme = "file:";

GSList * remove_and_free_list_head(GSList *pList)
{
	GSList *pElement;

	pElement = pList;

	pList = g_slist_remove_link(pList, pElement);

	assert(pElement->data != NULL);

	free(pElement->data);

	g_slist_free_1(pElement);

	return pList;
}

void free_list_and_items(GSList *pList)
{
	GSList *pElement;

	for (pElement = pList; pElement != NULL; pElement = pElement->next)
	{
		assert(pElement->data);

		free(pElement->data);
	}

	g_slist_free(pList);
}

const char * node_as_uri_string(librdf_node *pNode)
{
	librdf_uri *pUri;
	const char *pszUriAsString = NULL;
	librdf_node_type t = librdf_node_get_type(pNode);

	assert(t == LIBRDF_NODE_TYPE_RESOURCE || t == LIBRDF_NODE_TYPE_PROPERTY);

	pUri = librdf_node_get_uri(pNode);

	if (pUri)
	{
		pszUriAsString = librdf_uri_as_string(pUri);
	}

	return pszUriAsString;
}

const char * local_part_of_uri(const char *p)
{
	 char *pReturn;

	 assert(p);
	 
	 pReturn = strrchr(p, '#');  

	 if (pReturn != NULL)
	 {
		/*
		 * FIXME: Can someone suggest what is the best way of doing this that is language-independent?
		 */
		pReturn++;
	 }

	 return pReturn;
}

const char * namespaceless_part_of_uri(const char *p)
{
	char *pReturn;

	assert(p);
	 
	pReturn = strrchr(p, ':');  

	if (pReturn != NULL)
	{
		/*
		 * FIXME: Can someone suggest what is the best way of doing this that is language-independent?
		 */
		pReturn++;
	}

	return pReturn;
}

char * new_string_from_base_and_fragment(const char *pszBase, const char *pszFragment)
{
	char *pszReturn;
	
	assert(pszBase && pszFragment);

	pszReturn = malloc(strlen(pszBase) + strlen(pszFragment) + 2);

	strcpy(pszReturn, pszBase);
	strcat(pszReturn, "#");
	strcat(pszReturn, pszFragment);

	return pszReturn;
}

void print_nodes_in_list(FILE *fp, GSList *pList)
{
	GSList *pElement;
	const char *pszNode;

	for (pElement = pList; pElement != NULL; pElement = pElement->next)
	{
		assert(pElement->data);

		pszNode = local_part_of_uri(node_as_uri_string((librdf_node *) pElement->data));

		fprintf(fp, "%s ", pszNode);
	}
}

librdf_node *nodeAtContainerItem(librdf_model *pModel, librdf_node *pContainer, int item)
{
	librdf_node *pNodeReturn = NULL;
	librdf_node *pPredicate;
	char bufPredicate[100];

	snprintf(bufPredicate, sizeof(bufPredicate), "%s#_%d", namespaceFromCode(eXmlRdf), item);

	pPredicate = librdf_new_node_from_uri_string(pWorld, bufPredicate);

	if (pPredicate == NULL)
	{
		/* out of memory */
	}
	else
	{
		pNodeReturn = librdf_model_get_target(pModel, pContainer, pPredicate);

		librdf_free_node(pPredicate);
	}

	return pNodeReturn;
}

/*
 * newUriStringEnsuringScheme()
 * Returns:
 *	- a new string that is:
 *	- a copy of pszInput if pszPath has a URI scheme component, 
 *	- a copy of pszInput with "file:" prepended otherwise
 */
char * newUriStringEnsuringScheme(const char *pszInput)
{
	char *pReturn;

	assert(pszInput);

	if (strchr(pszInput, ':') == NULL)
	{
		/* No scheme, prepend file:
		*/
		pReturn = newUriStringFromFilename(pszInput);
	}
	else
	{
		/*
		 * <rant>
		 * I hate the way 'C' mires you in details of memory management!
		 * it's time-consuming, error-prone, and just shit.
		 * </rant>
		 * leni - Wed Apr 10 11:21:43 EST 2002
		 */

		pReturn = strdup(pszInput);
	}

	return pReturn;
}

const char * stringFromUriRemovingFileScheme(const char *pszUri)
{
	const char *pszReturn;

	if (strncmp(pszUri, pszFileScheme, strlen(pszFileScheme)) == 0)
	{
		pszReturn = filenameFromUri(pszUri);
	}
	else
	{
		pszReturn = pszUri;
	}

	return pszReturn;
}

const char * filenameFromUri(const char *pszUri)
{
	const char *pszReturn = NULL;

	assert(pszUri);

	pszReturn = strchr(pszUri, ':');

	if (pszReturn)
		pszReturn++;

	return pszReturn;
}

char * newUriStringFromFilename(const char *pszFilename)
{
	char *pReturn;

	assert(pszFilename);

	pReturn = malloc(strlen(pszFilename) + strlen(pszFileScheme) + 1);

	if (pReturn)
	{
		strcpy(pReturn, pszFileScheme);
		strcat(pReturn, pszFilename);
	}

	return pReturn;
}

GSList * listFromNodeIterator(librdf_iterator *iterator)
{
	librdf_node *pNode;
	GSList *pListReturn = NULL;

	while ( !librdf_iterator_end(iterator))
	{
		pNode = (librdf_node *) librdf_iterator_get_next(iterator);

		if (pNode)
		{
			pListReturn = g_slist_append(pListReturn, pNode);
		}
	}

	return pListReturn;
}

char * newStringFromNodesInList(GSList *pList)
{
	GSList *pListString = NULL;
	GSList *pElement;
	char *pszReturn;

	for (pElement = pList; pElement != NULL; pElement = pElement->next)
	{
		pListString = g_slist_append(pListString, (char *) local_part_of_uri(node_as_uri_string((librdf_node *) pElement->data)));
	}

	pszReturn = newStringFromStringsInList(pListString);

	g_slist_free(pListString);

	return pszReturn;
}

char *newAbsoluteFromRelativePath(const char *pszPath)
{
	int path_max;
	char *p = NULL;
	char *pszReturn = NULL;
	char *pszResolvedPath;

	/* code fragment as suggested by "man 3 realpath"
	*/
#ifdef PATH_MAX
	path_max = PATH_MAX;
#else
	path_max = pathconf (path, _PC_PATH_MAX);

	if (path_max <= 0)
		path_max = 4096;
#endif

	pszResolvedPath = malloc(path_max);

	if (pszResolvedPath)
		p = realpath(pszPath, pszResolvedPath); 
	
	if (p && pszResolvedPath)
	{
		/* make a (smaller) copy, 4K chunks can add up fast.
		*/
		pszReturn = strdup(pszResolvedPath);
	}

	if (pszResolvedPath)
		free(pszResolvedPath);

	return pszReturn;
}

int removeStatement(librdf_model *pModel, librdf_node *pSubject, librdf_node *pPredicate, librdf_node *pObject)
{
	librdf_statement *pStatement, *pPartialStatement;
	librdf_stream *pStream;
	int ret = TRUE;

	pPartialStatement = librdf_new_statement(pWorld);
		
	if (pSubject)
		librdf_statement_set_subject(pPartialStatement, librdf_new_node_from_node(pSubject));

	if (pPredicate)
		librdf_statement_set_predicate(pPartialStatement, librdf_new_node_from_node(pPredicate));

	if (pObject)
		librdf_statement_set_object(pPartialStatement, librdf_new_node_from_node(pObject));

	pStream = librdf_model_find_statements(pModel, pPartialStatement);

	while (!librdf_stream_end(pStream))
	{
		pStatement = librdf_stream_next(pStream);

#ifdef TAKENOUT
		printf("removing statement: ");
		librdf_statement_print(pStatement, stdout);
		printf("\n");
#endif

		ret = ! librdf_model_remove_statement(pModel, pStatement);

#ifdef TAKENOUT
		printf("Removal was %s\n", ret ? "successful" : "unsuccessful");
#endif

		librdf_free_statement(pStatement);
	}

	librdf_free_stream(pStream);

	librdf_free_statement(pPartialStatement);

	return ret;
}
