/* Fo
 * fo-xsl-formatter.c: Object type for libxslt XSLT processor
 *
 * Copyright (C) 2003 Sun Microsystems
 *
 * $Id: fo-xsl-formatter.c,v 1.11 2005/12/14 10:28:27 tonygraham Exp $
 *
 * See Copying for the status of this software.
 */

#include "libfo/fo-utils.h"
#include "libfo/fo-xml-doc-private.h"
#include "libfo/fo-doc-private.h"
#include "libfo/fo-doc-gp.h"
#include "libfo/fo-xsl-formatter.h"
#include "fo/fo-fo.h"
#include "area/fo-area.h"
#include "result-to-fo.h"
#include "area-to-pdf.h"

const char *fo_xsl_formatter_error_messages [] = {
  N_("FoXslFormatter error"),
  N_("XSLT format failed"),
  N_("Unable to parse stylesheet file: %s"),
  N_("No input result tree specified"),
  N_("No area tree present"),
  N_("No output FoDoc specified"),
  N_("Specified FoDoc is not FoDoc object type"),
  N_("$SGML_CATALOG_FILES environment variable not set")
};

struct _FoXslFormatter
{
  FoObject parent_instance;

  FoXmlDoc *result_tree;

  FoDoc    *fo_doc;

  FoFo     *fo_tree;
  FoArea   *area_tree;
};

struct _FoXslFormatterClass
{
  FoObjectClass parent_class;
};

enum {
  PROP_0,
  PROP_RESULT_TREE,
  PROP_FO_TREE,
  PROP_AREA_TREE,
  PROP_FO_DOC
};

static void fo_xsl_formatter_class_init   (FoXslFormatterClass *klass);
static void fo_xsl_formatter_finalize     (GObject             *object);
static void fo_xsl_formatter_get_property (GObject             *object,
					   guint                prop_id,
					   GValue              *value,
					   GParamSpec          *pspec);
static void fo_xsl_formatter_set_property (GObject             *object,
					   guint                prop_id,
					   const GValue        *value,
					   GParamSpec          *pspec);

static gpointer parent_class;

/**
 * fo_xsl_formatter_error_quark:
 * 
 * Get the error quark for #FoXslFormatter.
 *
 * If the quark does not yet exist, create it.
 * 
 * Return value: #GQuark associated with #FoXslFormatter errors.
 **/
GQuark
fo_xsl_formatter_error_quark (void)
{
  static GQuark quark = 0;
  if (quark == 0)
    quark = g_quark_from_static_string ("FoXslFormatter error");
  return quark;
}


/**
 * fo_xsl_formatter_get_type:
 * 
 * Register the #FoXslFormatter object type.
 * 
 * Return value: #GType value of the #FoXslFormatter object type.
p **/
GType fo_xsl_formatter_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    {
      static const GTypeInfo object_info =
      {
        sizeof (FoXslFormatterClass),
        NULL,           /* base_init */
        NULL,           /* base_finalize */
        (GClassInitFunc) fo_xsl_formatter_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (FoXslFormatter),
        0,              /* n_preallocs */
	NULL,		/* instance_init */
	NULL		/* value_table */
      };

      object_type = g_type_register_static (FO_TYPE_OBJECT,
                                            "FoXslFormatter",
                                            &object_info, 0);
    }

  return object_type;
}

/**
 * fo_xsl_formatter_class_init:
 * @klass: #FoXslFormatterClass object to initialise.
 * 
 * Implements #GClassInitFunc for #FoXslFormatterClass.
 **/
void
fo_xsl_formatter_class_init (FoXslFormatterClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->get_property = fo_xsl_formatter_get_property;
  object_class->set_property = fo_xsl_formatter_set_property;
  object_class->finalize     = fo_xsl_formatter_finalize;

  g_object_class_install_property
    (object_class,
     PROP_RESULT_TREE,
     g_param_spec_pointer ("result-tree",
			   _("Result Tree FoXmlDoc"),
			   _("Result of XSLT transform"),
			   G_PARAM_WRITABLE));

  g_object_class_install_property
    (object_class,
     PROP_FO_TREE,
     g_param_spec_object ("fo-tree",
			_("FO tree"),
			_("Formatting Object tree"),
			G_TYPE_OBJECT,
			G_PARAM_READABLE));

  g_object_class_install_property
    (object_class,
     PROP_AREA_TREE,
     g_param_spec_object ("area-tree",
			_("Area tree"),
			_("Area tree"),
			G_TYPE_OBJECT,
			G_PARAM_READABLE));

  g_object_class_install_property
    (object_class,
     PROP_FO_DOC,
     g_param_spec_object ("fo-doc",
			_("FoDoc"),
			_("Output document"),
			FO_TYPE_DOC,
			G_PARAM_WRITABLE));
}

/**
 * fo_xsl_formatter_finalize:
 * @object: #FoObject object to finalize.
 * 
 * Implements #GObjectFinalizeFunc for #FoXslFormatter.
 **/
void
fo_xsl_formatter_finalize  (GObject *object)
{
  FoXslFormatter *fo_xsl_formatter;

  fo_xsl_formatter = FO_XSL_FORMATTER (object);

  if (fo_xsl_formatter->result_tree != NULL)
    fo_xml_doc_unref (fo_xsl_formatter->result_tree);

  if (fo_xsl_formatter->fo_tree != NULL)
    g_object_unref (fo_xsl_formatter->fo_tree);

  if (fo_xsl_formatter->area_tree != NULL)
    g_object_unref (fo_xsl_formatter->area_tree);

  if (fo_xsl_formatter->fo_doc != NULL)
    g_object_unref (fo_xsl_formatter->fo_doc);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

/**
 * fo_xsl_formatter_get_property:
 * @object:  #GObject whose property will be retreived
 * @prop_id: Property ID assigned when property registered
 * @value:   #GValue to set with property value
 * @pspec:   Parameter specification for this property type
 * 
 * Implements #GObjectGetPropertyFunc for #FoXslFormatter
 **/
void
fo_xsl_formatter_get_property (GObject        *object,
			       guint           param_id,
			       GValue         *value,
			       GParamSpec     *pspec)
{
  FoXslFormatter *xsl_formatter = FO_XSL_FORMATTER (object);
  
  switch (param_id)
    {
    case PROP_FO_TREE:
      g_value_set_object (value, xsl_formatter->fo_tree);
      break;
    case PROP_AREA_TREE:
      g_value_set_object (value, xsl_formatter->area_tree);
      break;
    case PROP_RESULT_TREE:
    case PROP_FO_DOC:
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
      break;
    }
}

/**
 * fo_xsl_formatter_set_property:
 * @object:  #GObject whose property will be set
 * @prop_id: Property ID assigned when property registered
 * @value:   New value for property
 * @pspec:   Parameter specification for this property type
 * 
 * Implements #GObjectSetPropertyFunc for #FoXslFormatter
 **/
void
fo_xsl_formatter_set_property (GObject      *object,
			       guint         param_id,
			       const GValue *value,
			       GParamSpec   *pspec)
{
  FoXslFormatter *fo_xsl_formatter;

  fo_xsl_formatter = FO_XSL_FORMATTER (object);

  switch (param_id)
    {
    case PROP_RESULT_TREE:
      fo_xsl_formatter_set_result_tree (fo_xsl_formatter,
					g_value_get_pointer (value));
      break;
    case PROP_FO_DOC:
      fo_xsl_formatter_set_fo_doc (fo_xsl_formatter,
				   g_value_get_object (value));
      break;
    case PROP_FO_TREE:
    case PROP_AREA_TREE:
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
      break;
    }
}

/**
 * fo_xsl_formatter_new:
 * 
 * Creates a new #FoXslFormatter initialized to default value.
 * 
 * Return value: The new #FoXslFormatter.
 **/
FoXslFormatter *
fo_xsl_formatter_new (void)
{
  FoXslFormatter* xsl_formatter;

  xsl_formatter =
    g_object_new (fo_xsl_formatter_get_type (),
		  NULL);

  return xsl_formatter;
}

/**
 * fo_xsl_formatter_format:
 * @fo_xsl_formatter: #FoXslFormatter.
 * @libfo_context:    #FoLibfoContext.
 * @error:            #GError.
 * 
 * Formats @fo_xsl_formatter.
 * 
 * Outputs debug info according to debug mode set in @libfo_context.
 *
 * Return value: Success indication.
 **/
gboolean
fo_xsl_formatter_format (FoXslFormatter *fo_xsl_formatter,
			 FoLibfoContext *libfo_context,
			 GError        **error)
{
  FoDebugFlag debug = FO_DEBUG_NONE;
  GError *tmp_error = NULL;

  g_return_val_if_fail (fo_xsl_formatter != NULL, FALSE);
  g_return_val_if_fail (FO_IS_LIBFO_CONTEXT (libfo_context), FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  debug = fo_libfo_context_get_debug_mode (libfo_context);

  if (fo_xsl_formatter->result_tree == NULL)
    {
      g_set_error (error,
		   FO_XSL_FORMATTER_ERROR,
		   FO_XSL_FORMATTER_ERROR_NO_RESULT_TREE,
		   _(fo_xsl_formatter_error_messages[FO_XSL_FORMATTER_ERROR_NO_RESULT_TREE]));
      return FALSE;
    }

  if (fo_xsl_formatter->fo_doc == NULL)
    {
      g_set_error (error,
		   FO_XSL_FORMATTER_ERROR,
		   FO_XSL_FORMATTER_ERROR_NO_FO_DOC,
		   _(fo_xsl_formatter_error_messages[FO_XSL_FORMATTER_ERROR_NO_FO_DOC]));
      return FALSE;
    }

  if (!FO_IS_DOC (fo_xsl_formatter->fo_doc))
    {
      g_set_error (error,
		   FO_XSL_FORMATTER_ERROR,
		   FO_XSL_FORMATTER_ERROR_NO_FO_DOC,
		   _(fo_xsl_formatter_error_messages[FO_XSL_FORMATTER_ERROR_NOT_FO_DOC]));
      return FALSE;
    }

  if (fo_xsl_formatter->fo_tree != NULL &&
      fo_xsl_formatter->area_tree != NULL)
    {
      return TRUE;
    }
  else
    {
      fo_xml_doc_to_fo_and_area_trees (fo_xml_doc_get_xml_doc (fo_xsl_formatter->result_tree),
				       fo_xsl_formatter->fo_doc,
				       &(fo_xsl_formatter->fo_tree),
				       &(fo_xsl_formatter->area_tree),
				       fo_libfo_context_get_continue_after_error (libfo_context),
				       fo_libfo_context_get_debug_mode (libfo_context),
				       &tmp_error);

      if (debug & FO_DEBUG_FO)
	{
	  fo_object_debug_dump (fo_xsl_formatter->fo_tree,
				0);
	}

      if (debug & FO_DEBUG_AREA)
	{
	  fo_object_debug_dump (fo_xsl_formatter->area_tree,
				0);
	}

      if (tmp_error != NULL)
	{
	  g_propagate_error (error, tmp_error);
	  return FALSE;
	}
      else
	{
	  return TRUE;
	}
    }
}

/**
 * fo_xsl_formatter_draw:
 * @fo_xsl_formatter: #FoXslFormatter.
 * @libfo_context:    #FoLibfoContext.
 * @error:            #GError.
 * 
 * Formats @fo_xsl_formatter.
 * 
 * Return value: Success indication.
 **/
gboolean
fo_xsl_formatter_draw (FoXslFormatter *fo_xsl_formatter,
		       FoLibfoContext *libfo_context,
		       GError        **error)
{
  FoAreaToPDFData fo_area_to_pdf_data = {NULL, 0};

  g_return_val_if_fail (fo_xsl_formatter != NULL, FALSE);
  g_return_val_if_fail (FO_IS_LIBFO_CONTEXT (libfo_context), FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  if (fo_xsl_formatter->result_tree == NULL)
    {
      g_set_error (error,
		   FO_XSL_FORMATTER_ERROR,
		   FO_XSL_FORMATTER_ERROR_NO_RESULT_TREE,
		   _(fo_xsl_formatter_error_messages[FO_XSL_FORMATTER_ERROR_NO_RESULT_TREE]));
      return FALSE;
    }

  if (fo_xsl_formatter->fo_doc == NULL)
    {
      g_set_error (error,
		   FO_XSL_FORMATTER_ERROR,
		   FO_XSL_FORMATTER_ERROR_NO_FO_DOC,
		   _(fo_xsl_formatter_error_messages[FO_XSL_FORMATTER_ERROR_NO_FO_DOC]));
      return FALSE;
    }

  if (!FO_IS_DOC (fo_xsl_formatter->fo_doc))
    {
      g_set_error (error,
		   FO_XSL_FORMATTER_ERROR,
		   FO_XSL_FORMATTER_ERROR_NO_FO_DOC,
		   _(fo_xsl_formatter_error_messages[FO_XSL_FORMATTER_ERROR_NOT_FO_DOC]));
      return FALSE;
    }

  if (fo_xsl_formatter->area_tree == NULL)
    {
      g_set_error (error,
		   FO_XSL_FORMATTER_ERROR,
		   FO_XSL_FORMATTER_ERROR_NO_AREA_TREE,
		   _(fo_xsl_formatter_error_messages[FO_XSL_FORMATTER_ERROR_NO_AREA_TREE]));
      return FALSE;
    }


  fo_area_to_pdf_data.fo_doc = fo_xsl_formatter->fo_doc;
  fo_area_to_pdf_data.debug_level =
    fo_libfo_context_get_debug_mode (libfo_context);

  fo_area_tree_to_pdf (fo_xsl_formatter->area_tree,
		       &fo_area_to_pdf_data);

  return TRUE;
}

/**
 * fo_xsl_formatter_get_result_tree:
 * @fo_xsl_formatter: #FoXslFormatter
 * 
 * Get the filename in @fo_xsl_formatter.
 * 
 * Return value: Filename of file parsed to make @fo_xsl_formatter.
 **/
FoXmlDoc *
fo_xsl_formatter_get_result_tree (FoXslFormatter *fo_xsl_formatter)
{
  g_return_val_if_fail (fo_xsl_formatter != NULL, NULL);

  return fo_xsl_formatter->result_tree;
}

/**
 * fo_xsl_formatter_set_result_tree:
 * @fo_xsl_formatter: #FoXslFormatter
 * @result_tree:      Result of previous parse or transformation.
 * 
 * Set the result tree in @fo_xsl_formatter.  Voids the current FO and
 * area trees.
 **/
void
fo_xsl_formatter_set_result_tree (FoXslFormatter *fo_xsl_formatter,
				  FoXmlDoc       *result_tree)
{
  g_return_if_fail (fo_xsl_formatter != NULL);

  if (fo_xsl_formatter->result_tree != NULL)
    g_free (fo_xsl_formatter->result_tree);

  if (fo_xsl_formatter->fo_tree != NULL)
    {
      g_object_unref (fo_xsl_formatter->fo_tree);
      fo_xsl_formatter->fo_tree = NULL;
    }

  if (fo_xsl_formatter->area_tree != NULL)
    {
      g_object_unref (fo_xsl_formatter->area_tree);
      fo_xsl_formatter->area_tree = NULL;
    }

  fo_xsl_formatter->result_tree = fo_xml_doc_ref (result_tree);
}

/**
 * fo_xsl_formatter_set_fo_doc:
 * @fo_xsl_formatter: #FoXslFormatter
 * @fo_doc:           #FoDoc for use as output of formatter.
 * 
 * Set the #FoDoc in @fo_xsl_formatter.  Voids the current FO and area
 * trees.
 **/
void
fo_xsl_formatter_set_fo_doc (FoXslFormatter *fo_xsl_formatter,
			     FoDoc          *fo_doc)
{
  g_return_if_fail (fo_xsl_formatter != NULL);

  if (fo_xsl_formatter->fo_doc != NULL)
    g_object_unref (fo_xsl_formatter->fo_doc);

  if (fo_xsl_formatter->fo_tree != NULL)
    {
      g_object_unref (fo_xsl_formatter->fo_tree);
      fo_xsl_formatter->fo_tree = NULL;
    }

  if (fo_xsl_formatter->area_tree != NULL)
    {
      g_object_unref (fo_xsl_formatter->area_tree);
      fo_xsl_formatter->area_tree = NULL;
    }

  fo_xsl_formatter->fo_doc = g_object_ref (fo_doc);
}

/**
 * fo_xsl_formatter_get_fo_tree:
 * @fo_xsl_formatter: #FoXslFormatter
 * 
 * Get the #FoFo tree created by @fo_xsl_formatter.
 * 
 * Return value: #FoFo tree created by @fo_xsl_formatter.
 **/
GObject *
fo_xsl_formatter_get_fo_tree (FoXslFormatter *fo_xsl_formatter)
{
  g_return_val_if_fail (fo_xsl_formatter != NULL, NULL);

  return G_OBJECT (fo_xsl_formatter->fo_tree);
}

/**
 * fo_xsl_formatter_get_area_tree:
 * @fo_xsl_formatter: #FoXslFormatter
 * 
 * Get the #FoArea tree created by @fo_xsl_formatter.
 * 
 * Return value: #FoArea tree created by @fo_xsl_formatter.
 **/
GObject *
fo_xsl_formatter_get_area_tree (FoXslFormatter *fo_xsl_formatter)
{
  g_return_val_if_fail (fo_xsl_formatter != NULL, NULL);

  return G_OBJECT (fo_xsl_formatter->area_tree);
}
