/* This is -*- C -*- */
/* vim: set sw=2: */

/*
 * guppi-category.c
 *
 * Copyright (C) 2000 EMC Capital Management, Inc.
 * Copyright (C) 2001 The Free Software Foundation
 *
 * Developed by Jon Trowbridge <trow@gnu.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include <config.h>
#include "guppi-category.h"

/* #include <gnome.h> */
#include <stdlib.h>
#include <string.h>

#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>

#include <guppi-convenient.h>

static GtkObjectClass *parent_class = NULL;

static void
guppi_category_finalize (GtkObject *obj)
{
  if (parent_class->finalize)
    parent_class->finalize (obj);
}

/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */

struct OutPair {
  GuppiXMLDocument *doc;
  xmlNodePtr node;
};

static void
xml_cont_fn (const gchar *str, code_t code, gpointer data)
{
  struct OutPair *pair = (struct OutPair *) data;
  gchar buf[64];
  xmlNodePtr cat_node;

  cat_node = xmlNewNode (pair->doc->ns, "Category");

  xmlNewChild (cat_node, pair->doc->ns, "name", str);
  
  g_snprintf (buf, 64, "%d", (gint) code);
  xmlNewChild (cat_node, pair->doc->ns, "code", buf);
  
  xmlAddChild (pair->node, cat_node);
}

static void
export_xml (GuppiData *data, GuppiXMLDocument *doc, xmlNodePtr node)
{
  struct OutPair pair;
  
  pair.doc = doc;
  pair.node = node;
  
  guppi_category_foreach (GUPPI_CATEGORY (data), xml_cont_fn, &pair);
}


static void
guppi_category_class_init (GuppiCategoryClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;
  GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_DATA);

  data_class->export_xml = export_xml;

  object_class->finalize = guppi_category_finalize;
}

static void
guppi_category_init (GuppiCategory *obj)
{

}

GtkType guppi_category_get_type (void)
{
  static GtkType guppi_category_type = 0;
  if (!guppi_category_type) {
    static const GtkTypeInfo guppi_category_info = {
      "GuppiCategory",
      sizeof (GuppiCategory),
      sizeof (GuppiCategoryClass),
      (GtkClassInitFunc) guppi_category_class_init,
      (GtkObjectInitFunc) guppi_category_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_category_type =
      gtk_type_unique (GUPPI_TYPE_DATA, &guppi_category_info);
  }
  return guppi_category_type;
}

/**************************************************************************/

/* Data Op Stuff */

typedef struct _GuppiDataOp_Category GuppiDataOp_Category;
struct _GuppiDataOp_Category {
  GuppiDataOp op;

  gchar *name;
  code_t code;
};

static void
op_define (GuppiData *d, GuppiDataOp *op)
{
  GuppiDataOp_Category *cat_op = (GuppiDataOp_Category *) op;
  GuppiCategoryClass *klass;

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (d)->klass);

  g_assert (klass->define);
  klass->define (GUPPI_CATEGORY (d), cat_op->name, cat_op->code);
}

/***************************************************************************/

gsize
guppi_category_size (GuppiCategory *cat)
{
  GuppiCategoryClass *klass;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), 0);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->size);
  return klass->size (cat);
}

code_t
guppi_category_min_code (GuppiCategory *cat)
{
  GuppiCategoryClass *klass;
  code_t c = GUPPI_INVALID_CODE;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), GUPPI_INVALID_CODE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->codes);
  klass->codes (cat, &c, NULL, NULL);

  return c;
}

code_t
guppi_category_max_code (GuppiCategory *cat)
{
  GuppiCategoryClass *klass;
  code_t c = GUPPI_INVALID_CODE;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), GUPPI_INVALID_CODE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->codes);
  klass->codes (cat, NULL, &c, NULL);

  return c;
}

code_t
guppi_category_unused_code (GuppiCategory *cat)
{
  GuppiCategoryClass *klass;
  code_t mc = GUPPI_INVALID_CODE;
  code_t c = GUPPI_INVALID_CODE;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), GUPPI_INVALID_CODE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->codes);
  klass->codes (cat, NULL, &mc, &c);

  if (c == GUPPI_INVALID_CODE && mc != GUPPI_INVALID_CODE)
    c = mc + 1;

  return c;
}

gboolean
guppi_category_define (GuppiCategory *cat, const gchar *str, code_t c)
{
  GuppiCategoryClass *klass;
  GuppiDataOp_Category op;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), FALSE);
  g_return_val_if_fail (str != NULL, FALSE);
  g_return_val_if_fail (c != GUPPI_INVALID_CODE, FALSE);
  g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (cat)), FALSE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->code2name && klass->name2code);

  if (klass->code2name (cat, c) != NULL ||
      klass->name2code (cat, str) != GUPPI_INVALID_CODE)
    return FALSE;

  op.op.op = op_define;
  op.name = guppi_strdup (str);
  op.code = c;

  guppi_data_add_pending_op (GUPPI_DATA (cat), (GuppiDataOp *) & op);
  guppi_data_changed (GUPPI_DATA (cat));

  return TRUE;
}

code_t 
guppi_category_add_by_name (GuppiCategory *cat, const gchar *str)
{
  code_t c;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), GUPPI_INVALID_CODE);
  g_return_val_if_fail (str != NULL, GUPPI_INVALID_CODE);
  g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (cat)), GUPPI_INVALID_CODE);

  c = guppi_category_unused_code (cat);
  g_return_val_if_fail (c != GUPPI_INVALID_CODE, GUPPI_INVALID_CODE);

  if (!guppi_category_define (cat, str, c))
    c = GUPPI_INVALID_CODE;

  return c;
}

gboolean
guppi_category_contains (GuppiCategory *cat, const gchar *str)
{
  GuppiCategoryClass *klass;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), FALSE);
  g_return_val_if_fail (str != NULL, FALSE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->name2code);

  return klass->name2code (cat, str) != GUPPI_INVALID_CODE;
}

gboolean 
guppi_category_contains_code (GuppiCategory *cat, code_t c)
{
  GuppiCategoryClass *klass;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), FALSE);
  g_return_val_if_fail (c != GUPPI_INVALID_CODE, FALSE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->code2name);

  return klass->code2name (cat, c) != NULL;
}

code_t
guppi_category_find_by_name (GuppiCategory *cat, const gchar *str)
{
  GuppiCategoryClass *klass;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), GUPPI_INVALID_CODE);
  g_return_val_if_fail (str != NULL, GUPPI_INVALID_CODE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->name2code);

  return klass->name2code (cat, str);
}

const gchar *
guppi_category_find_by_code (GuppiCategory *cat, code_t c)
{
  GuppiCategoryClass *klass;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), NULL);
  g_return_val_if_fail (c != GUPPI_INVALID_CODE, NULL);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->code2name);

  return klass->code2name (cat, c);
}

gboolean
guppi_category_rename (GuppiCategory *cat, code_t c, const gchar *str)
{
  GuppiCategoryClass *klass;
  GuppiDataOp_Category op;
  code_t alt_code;
  const gchar *old_str;

  g_return_val_if_fail (GUPPI_IS_CATEGORY (cat), FALSE);
  g_return_val_if_fail (c != GUPPI_INVALID_CODE, FALSE);
  g_return_val_if_fail (str != NULL, FALSE);
  g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (cat)), FALSE);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->code2name && klass->name2code);

  old_str = klass->code2name (cat, c);

  /* Bad code passed in */
  if (old_str == NULL)
    return FALSE;

  /* Old string == new string, so we don't have to do anything. */
  if (!strcmp (old_str, str))
    return TRUE;

  alt_code = klass->name2code (cat, str);

  /* Oops... the new name is already used. */
  if (alt_code != GUPPI_INVALID_CODE)
    return FALSE;

  op.op.op = op_define;
  op.name = guppi_strdup (str);
  op.code = c;

  guppi_data_add_pending_op (GUPPI_DATA (cat), (GuppiDataOp *) & op);
  guppi_data_changed (GUPPI_DATA (cat));

  return TRUE;
}

void
guppi_category_foreach (GuppiCategory *cat, GuppiCategoryFn fn, gpointer user_data)
{
  GuppiCategoryClass *klass;

  g_return_if_fail (GUPPI_IS_CATEGORY (cat));
  g_return_if_fail (fn != NULL);

  klass = GUPPI_CATEGORY_CLASS (GTK_OBJECT (cat)->klass);

  g_assert (klass->foreach);
  klass->foreach (cat, fn, user_data);
}


syntax highlighted by Code2HTML, v. 0.9.1