/* -*- Mode: c; c-basic-offset: 2 -*-
 *
 * raptor_serialize_dot.c - Serialize RDF graph to GraphViz DOT format
 *
 * Copyright (C) 2004-2007, David Beckett http://www.dajobe.org/
 * Copyright (C) 2004-2005, University of Bristol, UK http://www.bristol.ac.uk/
 * 
 * This package is Free Software and part of Redland http://librdf.org/
 * 
 * It is licensed under the following three licenses as alternatives:
 *   1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
 *   2. GNU General Public License (GPL) V2 or any newer version
 *   3. Apache License, V2.0 or any newer version
 * 
 * You may not use this file except in compliance with at least one of
 * the above three licenses.
 * 
 * See LICENSE.html or LICENSE.txt at the top of this package for the
 * complete terms and further detail along with the license texts for
 * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
 * 
 */

#ifdef HAVE_CONFIG_H
#include <raptor_config.h>
#endif

#ifdef WIN32
#include <win32_raptor_config.h>
#endif


#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

/* Raptor includes */
#include "raptor.h"
#include "raptor_internal.h"

/*
 * Raptor dot serializer object
 */
typedef struct {
  raptor_namespace_stack *nstack;
  raptor_sequence *namespaces;

  raptor_sequence *resources;
  raptor_sequence *literals;
  raptor_sequence *bnodes;
} raptor_dot_context;

typedef struct {
  raptor_identifier_type type;  /* node type */
  union {

    struct {
      raptor_uri *uri;
    } resource;

    struct {
      unsigned char *string;
      raptor_uri *datatype;
      unsigned char *language;
    } literal;

    struct {
      unsigned char *string;
    } blank;
    
  } value;
} raptor_dot_serializer_node;


/* Free a node */
static void
raptor_dot_serializer_free_node(raptor_dot_serializer_node *node)
{
  if(!node)
    return;
  
  switch (node->type) {
      case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
      case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
        raptor_free_uri(node->value.resource.uri);
        break;
          
      case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
        RAPTOR_FREE(blank, node->value.blank.string);
        break;
          
      case RAPTOR_IDENTIFIER_TYPE_LITERAL:
      case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:
        RAPTOR_FREE(literal, node->value.literal.string);

        if(node->value.literal.datatype)
          raptor_free_uri(node->value.literal.datatype);

        if(node->value.literal.language)
          RAPTOR_FREE(language, node->value.literal.language);

        break;
          
      case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
      case RAPTOR_IDENTIFIER_TYPE_UNKNOWN: 
      default:
        /* Nothing to do */
        break;
  }

  RAPTOR_FREE(raptor_node, node);
}


/*
 * raptor_dot_serializer_node_matches:
 * @node: #raptor_node to compare
 * @node_type: Raptor identifier type
 * @node_data: For node_type RAPTOR_IDENTIFIER_TYPE_ORDINAL, int* to the
 *             ordinal.
 * @datatype: Literal datatype or NULL
 * @language: Literal language or NULL
 *
 * Return value: non-zero if @node matches the node described by the rest of
 *   the parameters.
 */
static int
raptor_dot_serializer_node_matches(raptor_dot_serializer_node* node,
                                   raptor_identifier_type node_type,
				   const void* node_data, raptor_uri* datatype,
				   const unsigned char* language)
{
  int rv = 0;
  
  if(node->type != node_type)
    return 0;

  switch (node->type) {
      case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
      case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
        rv = raptor_uri_equals(node->value.resource.uri,
                               (raptor_uri*)node_data);
        break;
          
      case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
        rv = !strcmp((const char*)node->value.blank.string,
                     (const char*)node_data);
        break;
          
      case RAPTOR_IDENTIFIER_TYPE_LITERAL:
      case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:

        if((char*)node->value.literal.string != NULL &&
           (char*)node_data != NULL) {

          /* string */
          rv = (strcmp((char*)node->value.literal.string,
                       (char*)node_data) == 0);

          /* language */
          if((char*)node->value.literal.language != NULL &&
             (char*)language != NULL)
            rv &= (strcmp((char*)node->value.literal.language,
                          (char*)language) == 0);
          else if((char*)node->value.literal.language != NULL ||
                  (char*)language != NULL)
            rv= 0;

          /* datatype */
          if(node->value.literal.datatype != NULL && datatype != NULL)
            rv &= (raptor_uri_equals(node->value.literal.datatype,datatype) !=0);
          else if(node->value.literal.datatype != NULL || datatype != NULL)
            rv = 0;
          
        } else {
          RAPTOR_FATAL1("string must be non-NULL for literal or xml literal\n");
          rv = 0;
        }
        
        break;
          
      case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
      case RAPTOR_IDENTIFIER_TYPE_UNKNOWN: 
      default:
        /* Nothing to do */
        break;
  }

  return rv;
}


/*
 * raptor_dot_serializer_new_node implementation:
 * @node_type: Raptor identifier type
 * @node_data: For node_type RAPTOR_IDENTIFIER_TYPE_ORDINAL, int* to the
 *             ordinal.
 * @datatype: Literal datatype or NULL
 * @language: Literal language or NULL
 *
 * Parts of this is taken from redland librdf_node.h and librdf_node.c
 *
 * Return value: a new raptor_dot_serializer_node
 *
 **/
static raptor_dot_serializer_node *
raptor_dot_serializer_new_node(raptor_identifier_type node_type,
                               const void* node_data,
			       raptor_uri* datatype,
                               const unsigned char *language)
{
  unsigned char *string;
  raptor_dot_serializer_node* node;
  
  if(node_type == RAPTOR_IDENTIFIER_TYPE_UNKNOWN)
    return NULL;

  node = (raptor_dot_serializer_node *)RAPTOR_CALLOC(raptor_dot_serializer_node, 1, sizeof(raptor_dot_serializer_node));

  if(node) {
    node->type = node_type;
    
    switch (node_type) {
      case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
        node->type = RAPTOR_IDENTIFIER_TYPE_RESOURCE;
        /* intentional fall through */

      case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
        node->value.resource.uri = raptor_uri_copy((raptor_uri*)node_data);
        break;
        
      case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
        string=(unsigned char*)RAPTOR_MALLOC(blank,
                                             strlen((char*)node_data)+1);
        strcpy((char*)string, (const char*) node_data);
        node->value.blank.string = string;
        break;
        
      case RAPTOR_IDENTIFIER_TYPE_LITERAL:
      case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:
        string = (unsigned char*)RAPTOR_MALLOC(literal,
                                               strlen((char*)node_data)+1);
        strcpy((char*)string, (const char*)node_data);
        node->value.literal.string = string;
        
        if(datatype)
          node->value.literal.datatype = raptor_uri_copy(datatype);
        
        if(language) {
          unsigned char *lang;
          lang = (unsigned char*)RAPTOR_MALLOC(language,
                                               strlen((const char*)language)+1);
          strcpy((char*)lang, (const char*)language);
          node->value.literal.language = lang;
        }
        break;
        
      case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
      case RAPTOR_IDENTIFIER_TYPE_UNKNOWN: 
      default:
        RAPTOR_FREE(raptor_dot_serializer_node, node);
    }
    
  }

  return node;
}


/* add a namespace */
static int
raptor_dot_serializer_declare_namespace_from_namespace(raptor_serializer* serializer,
						       raptor_namespace *nspace)
{
  raptor_dot_context * context = (raptor_dot_context *)serializer->context;
  int i;

  for( i = 0 ; i < raptor_sequence_size(context->namespaces) ; i++ ) {
    raptor_namespace * ns;
    ns = (raptor_namespace *)raptor_sequence_get_at(context->namespaces, i);

    /* If prefix is already declared, ignore it */
    if((!ns->prefix && !nspace->prefix) ||
       (ns->prefix && nspace->prefix &&
        !strcmp((const char*)ns->prefix, (const char*)nspace->prefix)) ||
       (ns->uri && nspace->uri &&
        raptor_uri_equals(ns->uri, nspace->uri)) )
      return 1;
  }

  nspace = raptor_new_namespace_from_uri(context->nstack, nspace->prefix,
					 nspace->uri, 0);

  if(!nspace)
    return 1;
  
  raptor_sequence_push(context->namespaces, nspace);

  return 0;
}


/* add a namespace */
static int
raptor_dot_serializer_declare_namespace(raptor_serializer* serializer,
					raptor_uri* uri,
					const unsigned char *prefix)
{
  raptor_dot_context * context = (raptor_dot_context *)serializer->context;
  raptor_namespace *ns;
  int rc;

  ns = raptor_new_namespace_from_uri(context->nstack, prefix, uri, 0);
  rc = raptor_dot_serializer_declare_namespace_from_namespace(serializer, ns);

  raptor_free_namespace(ns);

  return rc;
}


/* create a new serializer */
static int
raptor_dot_serializer_init(raptor_serializer *serializer, const char *name)
{
  raptor_dot_context * context = (raptor_dot_context *)serializer->context;
  const raptor_uri_handler *uri_handler;
  void* uri_context;

  raptor_uri_get_handler(&uri_handler, &uri_context);

  /* Setup namespace handling */
  context->nstack =
    raptor_new_namespaces(uri_handler, uri_context,
			  (raptor_simple_message_handler)raptor_serializer_simple_error,
			  serializer, 1);
  context->namespaces=raptor_new_sequence((raptor_sequence_free_handler *)raptor_free_namespace, NULL);

  /* We keep a list of nodes to avoid duplication (which isn't
   * critical in graphviz, but why bloat the file?)
   */
  context->resources =
    raptor_new_sequence((raptor_sequence_free_handler *)raptor_dot_serializer_free_node, NULL);
  context->literals =
    raptor_new_sequence((raptor_sequence_free_handler *)raptor_dot_serializer_free_node, NULL);
  context->bnodes =
    raptor_new_sequence((raptor_sequence_free_handler *)raptor_dot_serializer_free_node, NULL);

  return 0;
}


/**
 * raptor_dot_iostream_write_string:
 * @iostr: #raptor_iostream to write to
 * @string: UTF-8 string to write
 * @len: length of UTF-8 string
 * or \0 for no escaping.
 *
 * Write an UTF-8 string, escaped for graphviz.
 *
 * Return value: non-0 on failure.
 **/
static int
raptor_dot_iostream_write_string(raptor_iostream *iostr,
				 const unsigned char *string)
{
  unsigned char c;

  for( ; (c = *string) ; string++ ) {
    if( (c == '\\') || (c == '"') || (c == '|') ||
        (c == '{')  || (c == '}') ) {
      raptor_iostream_write_byte(iostr, '\\');
      raptor_iostream_write_byte(iostr, c);
    } else if( c == '\n' ) {
      raptor_iostream_write_byte(iostr, '\\');
      raptor_iostream_write_byte(iostr, 'n');
    } else
      raptor_iostream_write_byte(iostr, c);
  }

  return 0;
}


static void
raptor_dot_serializer_write_node_type(raptor_serializer * serializer,
				      raptor_identifier_type type)
{
  switch(type) {
    case RAPTOR_IDENTIFIER_TYPE_LITERAL:
    case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:
      raptor_iostream_write_byte(serializer->iostream, 'L');
      break;

    case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
      raptor_iostream_write_byte(serializer->iostream, 'B');
      break;

    case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
    case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
      raptor_iostream_write_byte(serializer->iostream, 'R');
      break;

    case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
    case RAPTOR_IDENTIFIER_TYPE_UNKNOWN:
      raptor_iostream_write_byte(serializer->iostream, '?');
      break;
  }
}


static void
raptor_dot_serializer_write_uri(raptor_serializer* serializer,
				raptor_uri* uri)
{
  raptor_dot_context* context = (raptor_dot_context*)serializer->context;
  unsigned char* full = raptor_uri_as_string(uri);
  int i;

  for( i = 0 ; i < raptor_sequence_size(context->namespaces) ; i++ ) {
    raptor_namespace* ns =
      (raptor_namespace*)raptor_sequence_get_at(context->namespaces, i);
    const unsigned char* ns_uri_string;
    size_t ns_uri_string_len;
    ns_uri_string=raptor_uri_as_counted_string(ns->uri, &ns_uri_string_len);

    if(!strncmp((char*)full, (char*)ns_uri_string, ns_uri_string_len) ) {
      const unsigned char* prefix = raptor_namespace_get_prefix(ns);
      
      if(prefix) {	
        raptor_iostream_write_string(serializer->iostream, prefix);
        raptor_iostream_write_byte(serializer->iostream, ':');
      }

      raptor_iostream_write_string(serializer->iostream,
                                   full + ns_uri_string_len);

      return;
    }
  }

  raptor_iostream_write_string(serializer->iostream, full);
}


static void
raptor_dot_serializer_write_node(raptor_serializer * serializer,
				 const void* term,
				 raptor_identifier_type type,
				 raptor_uri* literal_datatype,
				 const unsigned char * literal_language) {
  switch(type) {
    case RAPTOR_IDENTIFIER_TYPE_LITERAL:
    case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:
      raptor_dot_iostream_write_string(serializer->iostream, (const unsigned char*)term);
      if(literal_language && type == RAPTOR_IDENTIFIER_TYPE_LITERAL) {
        raptor_iostream_write_byte(serializer->iostream, '|');
        raptor_iostream_write_string(serializer->iostream, "Language: ");
        raptor_iostream_write_string(serializer->iostream, literal_language);
      }
      if(type == RAPTOR_IDENTIFIER_TYPE_XML_LITERAL) {
        raptor_iostream_write_byte(serializer->iostream, '|');
        raptor_iostream_write_string(serializer->iostream, "Datatype: ");
        raptor_iostream_write_string(serializer->iostream, raptor_xml_literal_datatype_uri_string);
      } else if(literal_datatype) {
        raptor_iostream_write_byte(serializer->iostream, '|');
        raptor_iostream_write_string(serializer->iostream, "Datatype: ");
	raptor_dot_serializer_write_uri(serializer, (raptor_uri*)literal_datatype);
      }

      break;
      
    case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
      raptor_iostream_write_counted_string(serializer->iostream, "_:", 2);
      raptor_iostream_write_string(serializer->iostream, term);
      break;
  
    case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
    case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
      raptor_dot_serializer_write_uri(serializer, (raptor_uri*)term);
      break;
      
    case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
    case RAPTOR_IDENTIFIER_TYPE_UNKNOWN:
    default:
      RAPTOR_FATAL2("Unknown type %d", type);
  }
}


/* Check the list to see if the node is a duplicate. If not, add it
 * to the list.
 */
static void
raptor_dot_serializer_assert_node(raptor_serializer* serializer,
                                  raptor_identifier_type node_type,
                                  const void* node_data,
                                  raptor_uri* datatype,
                                  const unsigned char* language)
{
  raptor_dot_context* context = (raptor_dot_context*)serializer->context;
  raptor_sequence* seq = NULL;
  int i;

  /* Which list are we searching? */
  switch(node_type) {
    case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
    case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
      seq = context->resources;
      break;

    case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
      seq = context->bnodes;
      break;

    case RAPTOR_IDENTIFIER_TYPE_LITERAL:
    case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:
      seq = context->literals;
      break;

    case RAPTOR_IDENTIFIER_TYPE_UNKNOWN:
    case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
      break;
  }

  for( i = 0 ; i < raptor_sequence_size(seq) ; i++ ) {
    raptor_dot_serializer_node* node =
      (raptor_dot_serializer_node*)raptor_sequence_get_at(seq, i);

    if(raptor_dot_serializer_node_matches(node, node_type, node_data,
                                          datatype, language) )
      return;
  }

  raptor_sequence_push(seq,
		       raptor_dot_serializer_new_node(node_type, node_data,
						      datatype, language));
}


/* start a serialize */
static int
raptor_dot_serializer_start(raptor_serializer* serializer)
{
  raptor_iostream_write_string(serializer->iostream, (const unsigned char *)
			       "digraph {\n\trankdir=LR;\n\tcharset=\"utf-8\";\n\n");

  return 0;
}


static int
raptor_dot_serializer_write_colors(raptor_serializer* serializer,
				   raptor_identifier_type type)
{
  switch(type) {
    case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
      if(serializer->feature_resource_border) {
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", color=");
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)serializer->feature_resource_border);
      }
      else
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", color=blue");

      if(serializer->feature_resource_fill) {
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", style=filled, fillcolor=");
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)
				     serializer->feature_resource_fill);
      }

      break;

    case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
      if(serializer->feature_bnode_border) {
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", color=");
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)serializer->feature_bnode_border);
      }
      else
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", color=green");

      if(serializer->feature_bnode_fill) {
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", style=filled, fillcolor=");
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)serializer->feature_bnode_fill);
      }

      break;

    case RAPTOR_IDENTIFIER_TYPE_LITERAL:
      if(serializer->feature_literal_border) {
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", color=");
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)serializer->feature_literal_border);
      }

      if(serializer->feature_literal_fill) {
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)", style=filled, fillcolor=");
	raptor_iostream_write_string(serializer->iostream,
                                     (const unsigned char *)serializer->feature_literal_fill);
      }

      break;

    case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
    case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
    case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:
    case RAPTOR_IDENTIFIER_TYPE_UNKNOWN:
    default:
      break;
  }

  return 0;
}


/* end a serialize */
static int
raptor_dot_serializer_end(raptor_serializer* serializer)
{
  raptor_dot_context* context=(raptor_dot_context*)serializer->context;
  raptor_dot_serializer_node* node;
  int i;

  /* Print our nodes. */
  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\n\t// Resources\n");
  for( i = 0 ; i < raptor_sequence_size(context->resources) ; i++ ) {
    node = (raptor_dot_serializer_node*)raptor_sequence_get_at(context->resources, i);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\t\"R");
    raptor_dot_serializer_write_node(serializer, node->value.resource.uri,
				     RAPTOR_IDENTIFIER_TYPE_RESOURCE, NULL, NULL);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\" [ label=\"");
    raptor_dot_serializer_write_node(serializer, node->value.resource.uri,
				     RAPTOR_IDENTIFIER_TYPE_RESOURCE, NULL, NULL);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\", shape=ellipse");
    raptor_dot_serializer_write_colors(serializer,
                                       RAPTOR_IDENTIFIER_TYPE_RESOURCE);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)" ];\n");
    
  }
  raptor_free_sequence(context->resources);

  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\n\t// Anonymous nodes\n");
  for( i = 0 ; i < raptor_sequence_size(context->bnodes) ; i++ ) {
    node = (raptor_dot_serializer_node *)raptor_sequence_get_at(context->bnodes, i);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\t\"B");
    raptor_dot_serializer_write_node(serializer, node->value.resource.uri,
				   RAPTOR_IDENTIFIER_TYPE_ANONYMOUS, NULL, NULL);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\" [ label=\"");
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\", shape=circle");
    raptor_dot_serializer_write_colors(serializer,
                                       RAPTOR_IDENTIFIER_TYPE_ANONYMOUS);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)" ];\n");
  }
  raptor_free_sequence(context->bnodes);

  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\n\t// Literals\n");
  for( i = 0 ; i < raptor_sequence_size(context->literals) ; i++ ) {
    node = (raptor_dot_serializer_node *)raptor_sequence_get_at(context->literals, i);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\t\"L");
    raptor_dot_serializer_write_node(serializer, node->value.literal.string,
				     RAPTOR_IDENTIFIER_TYPE_LITERAL,
                                     node->value.literal.datatype,
				     node->value.literal.language);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\" [ label=\"");
    raptor_dot_serializer_write_node(serializer, node->value.literal.string,
				     RAPTOR_IDENTIFIER_TYPE_LITERAL,
                                     node->value.literal.datatype,
				     node->value.literal.language);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\", shape=record");
    raptor_dot_serializer_write_colors(serializer,
                                       RAPTOR_IDENTIFIER_TYPE_LITERAL);
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)" ];\n");
  }
  raptor_free_sequence(context->literals);

  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\n\tlabel=\"\\n\\nModel:\\n");
  if(serializer->base_uri)
    raptor_iostream_write_string(serializer->iostream,
                                 raptor_uri_as_string(serializer->base_uri));
  else
    raptor_iostream_write_string(serializer->iostream, "(Unknown)");

  if(raptor_sequence_size(context->namespaces)) {
    raptor_iostream_write_string(serializer->iostream,
                                 (const unsigned char *)"\\n\\nNamespaces:\\n");

    for( i = 0 ; i < raptor_sequence_size(context->namespaces) ; i++ ) {
      raptor_namespace* ns =
	(raptor_namespace*)raptor_sequence_get_at(context->namespaces, i);
      const unsigned char* prefix = raptor_namespace_get_prefix(ns);

      if(prefix) {
	raptor_iostream_write_string(serializer->iostream,
				     (const unsigned char *)ns->prefix);
	raptor_iostream_write_string(serializer->iostream,
				     (const unsigned char *)": ");
      }
      raptor_iostream_write_string(serializer->iostream,
				   raptor_uri_as_string(ns->uri));
      raptor_iostream_write_string(serializer->iostream,
				   (const unsigned char *)"\\n");
    }

    raptor_free_sequence(context->namespaces);
  }

  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\";\n");

  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *) "}\n");

  return 0;
}


/* destroy a serializer */
static void
raptor_dot_serializer_terminate(raptor_serializer* serializer)
{
  /* raptor_dot_context* context=(raptor_dot_context*)serializer->context; */

  /* Everything should have been freed in raptor_dot_serializer_end */
}

/* serialize a statement */
static int
raptor_dot_serializer_statement(raptor_serializer* serializer,
				const raptor_statement *statement)
{
  /* Cache the nodes for later. */
  raptor_dot_serializer_assert_node(serializer, statement->subject_type,
				    statement->subject, NULL, NULL);
  raptor_dot_serializer_assert_node(serializer, statement->object_type,
				    statement->object, statement->object_literal_datatype,
				    statement->object_literal_language);

  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\t\"");
  raptor_dot_serializer_write_node_type(serializer, statement->subject_type);
  raptor_dot_serializer_write_node(serializer,
				   statement->subject,
				   statement->subject_type,
				   NULL, NULL);
  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\" -> \"");
  raptor_dot_serializer_write_node_type(serializer, statement->object_type);
  raptor_dot_serializer_write_node(serializer,
				   statement->object,
				   statement->object_type,
				   statement->object_literal_datatype,
				   statement->object_literal_language);
  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\" [ label=\"");
  raptor_dot_serializer_write_node(serializer,
				   statement->predicate,
				   statement->predicate_type,
				   NULL, NULL);
  raptor_iostream_write_string(serializer->iostream,
                               (const unsigned char *)"\" ];\n");

  return 0;
}


static int
raptor_dot_serializer_register_factory(raptor_serializer_factory *factory)
{
  factory->context_length = sizeof(raptor_dot_context);

  factory->init = raptor_dot_serializer_init;
  factory->declare_namespace = raptor_dot_serializer_declare_namespace;
  factory->declare_namespace_from_namespace =
    raptor_dot_serializer_declare_namespace_from_namespace;
  factory->serialize_start     = raptor_dot_serializer_start;
  factory->serialize_statement = raptor_dot_serializer_statement;
  factory->serialize_end       = raptor_dot_serializer_end;
  factory->finish_factory      = NULL;
  factory->terminate           = raptor_dot_serializer_terminate;

  return 0;
}


int
raptor_init_serializer_dot (void)
{
  return raptor_serializer_register_factory("dot",
                                            "GraphViz DOT format",
                                            "text/x-graphviz",
                                            NULL,
                                            (const unsigned char*)"http://www.graphviz.org/doc/info/lang.html",
                                            &raptor_dot_serializer_register_factory);
}
