/*
 * persist.c
 * Turn Nodes and Edges into an xml/rdf graph.
 * Longer-term, this'll become the interface to persistence for the daemon.
 *
 * 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 <stdlib.h> /* for free()     */
#include <string.h> /* for strlen()   */
#include <time.h>   /* for strftime() */
#include <errno.h>
#include <glib.h>

#include "config.h"  /* generated by autoconf configure */
#include "servercommon.h"
#include "persist.h"

static void print_graph(FILE *fp, Nodes *pNodes, Edges *pEdges, const char *pszGraphId);
static void print_nodes(FILE *fp, Nodes *pNodes);
static void print_edges(FILE *fp, Edges *pEdges);
static void print_li(FILE *fp, const char *pszFragment);
static void print_file_add(FILE *fp, const char *pszFileAdd);
static char * uriFragmentFromEdge(char buf[], int buflen, const Edge *pEdge);
static char *getTime8601(char buf[], int buflen);

void print_as_xml_rdf_graph(FILE *fp, Nodes *pNodes, Edges *pEdges,
		const char *pszGraphId, const char *pszFileAdd)
{
	print_rdf_header(fp);

	print_graph(fp, pNodes, pEdges, pszGraphId);

	print_nodes(fp, pNodes);

	print_edges(fp, pEdges);

	print_file_add(fp, pszFileAdd);

	print_rdf_trailer(fp);
}

void print_graph(FILE *fp, Nodes *pNodes, Edges *pEdges, const char *pszGraphId)
{
	GSList *pElement;
	GList *pEdgeItem;
	Node *pNode;
	Edge *pEdge;
	char buf[2 * SERVICE_NAME_SIZE + 100];
	const char *pszRdfId = "graph1";

	if (pszGraphId && strlen(pszGraphId) > 0)
	{
		pszRdfId = pszGraphId;
	}

	fprintf(fp, "\n");
	fprintf(fp, "<graph rdf:ID=\"%s\" graph:directed=\"#true\" serel:version=\"%s\" serel:created=\"%s\" >\n",
		pszRdfId,
		VERSION,
		getTime8601(buf, sizeof(buf))
		);
	fprintf(fp, "  <nodes>\n");
	fprintf(fp, "    <rdf:Bag>\n");

	for (pElement = nodesGet(pNodes); pElement != NULL; pElement = pElement->next)
	{
		pNode = pElement->data;
		
		print_li(fp, serviceIdAsString(pNode->id));
	}

	fprintf(fp, "    </rdf:Bag>\n");
	fprintf(fp, "  </nodes>\n");
	fprintf(fp, "\n");
	fprintf(fp, "  <edges>\n");
	fprintf(fp, "    <rdf:Bag>\n");

	for (pEdgeItem = edgesGet(pEdges); pEdgeItem != NULL; pEdgeItem = pEdgeItem->next)
	{
		pEdge = pEdgeItem->data;

		print_li(fp, uriFragmentFromEdge(buf, sizeof(buf), pEdge));
	}

	fprintf(fp, "    </rdf:Bag>\n");
	fprintf(fp, "  </edges>\n");
	fprintf(fp, "</graph>\n");
	fprintf(fp, "\n");
}

void print_nodes(FILE *fp, Nodes *pNodes)
{
	GSList *pElement;
	Node *pNode;

	for (pElement = nodesGet(pNodes); pElement != NULL; pElement = pElement->next)
	{
		pNode = pElement->data;
		
		fprintf(fp, "<node rdf:ID=\"%s\" graph:weight=\"%4.2f\" />\n",
			serviceIdAsString(pNode->id),
			((float) pNode->stopwatch)/1000.0);
	}

	fprintf(fp, "\n");
}

void print_edges(FILE *fp, Edges *pEdges)
{
	GList *pEdgeItem;
	Edge *pEdge;
	char buf[2 * SERVICE_NAME_SIZE + 100];

	for (pEdgeItem = edgesGet(pEdges); pEdgeItem != NULL; pEdgeItem = pEdgeItem->next)
	{
		pEdge = pEdgeItem->data;
		
		fprintf(fp, "<edge rdf:ID=\"%s\" >\n", uriFragmentFromEdge(buf, sizeof(buf), pEdge));
		fprintf(fp, "  <source rdf:resource=\"#%s\"/>\n", serviceIdAsString(pEdge->id[eEdgeProvider]));
		fprintf(fp, "  <target rdf:resource=\"#%s\"/>\n", serviceIdAsString(pEdge->id[eEdgeNeeder]));
		fprintf(fp, "</edge>\n");
	}

	fprintf(fp, "\n");
}

void print_file_add(FILE *fp, const char *pszFileAdd)
{
	if (pszFileAdd && strlen(pszFileAdd) > 0)
	{
		FILE *fpAdd;

		fpAdd = fopen(pszFileAdd, "r");

		if (fpAdd)
		{
			int cBytes;
			char buf[1000];

			assert(sizeof(char) == 1);

			cBytes = fread(buf, 1, sizeof(buf) - 1, fpAdd);

			while (cBytes > 0)
			{
				buf[cBytes] = '\0';

				fprintf(fp, "%s", buf);

				cBytes = fread(buf, 1, sizeof(buf) - 1, fpAdd);
			}

			fprintf(fp, "\n");

			fclose(fpAdd);
		}
		else
		{
			fprintf(fp, "<!-- Error: Unable to open \"%s\" for reading: %s -->\n", pszFileAdd, strerror(errno));
			fprintf(fp, "\n");
		}
	}
}

void print_li(FILE *fp, const char *pszFragment)
{
	fprintf(fp, "      <rdf:li rdf:resource=\"#%s\" />\n", pszFragment);
}

char * uriFragmentFromEdge(char buf[], int buflen, const Edge *pEdge)
{
	return edgeFragmentFromPair(buf, buflen,
		serviceIdAsString(pEdge->id[eEdgeProvider]),
		serviceIdAsString(pEdge->id[eEdgeNeeder]));
}

static char *getTime8601(char buf[], int buflen)
{
	time_t t = time(NULL);
	struct tm *pTm = gmtime(&t);
    strftime(buf, buflen, "%G-%m-%d %T UTC", pTm);

	return buf;
}
