/* -*-Mode: C;-*-
 * $Id: generic.c 1.2 Sun, 28 Jan 2007 12:21:11 -0800 jmacd $
 *
 * Copyright (C) 1998, 1999, Josh MacDonald.
 * All Rights Reserved.
 *
 * Author: Josh MacDonald <jmacd@CS.Berkeley.EDU>
 */

#include "edsio.h"

/* Type-based selectors for unknown types
 */

typedef struct {
  const char* name;
  gboolean (*unserialize_func) ();
  gboolean (*serialize_func) ();
  guint    (*count_func) ();
  void     (*print_func) ();
  guint32    val;
} SerEntry;

static GArray   *ser_array;
static gboolean  ser_array_sorted;

void
serializeio_initialize_type (const char* name,
			     guint32    val,
			     gboolean (*unserialize_func) (),
			     gboolean (*serialize_func) (),
			     guint    (*count_func) (),
			     void      (*print_func) ())
{
  SerEntry it;

  it.name = name;
  it.val = val;
  it.unserialize_func = unserialize_func;
  it.serialize_func = serialize_func;
  it.count_func = count_func;
  it.print_func = print_func;

  if (ser_array == NULL)
    ser_array = g_array_new (FALSE, TRUE, sizeof (SerEntry));

  g_array_append_val (ser_array, it);

  ser_array_sorted = FALSE;
}

static int
ser_entry_compare (const void* va, const void* vb)
{
  SerEntry* a = (SerEntry*) va;
  SerEntry* b = (SerEntry*) vb;

  return a->val - b->val;
}

static SerEntry*
serializeio_find_entry (SerialType type)
{
  if (! edsio_library_check (type & EDSIO_LIBRARY_OFFSET_MASK))
    return NULL;

  if (ser_array)
    {
      gint high_index = ser_array->len;
      gint low_index = 0;
      gint index;
      gint this_val;

      if (! ser_array_sorted)
	{
	  ser_array_sorted = TRUE;
	  qsort (ser_array->data, ser_array->len, sizeof (SerEntry), ser_entry_compare);
	}

    again:

      index = (low_index + high_index) / 2;

      this_val = g_array_index (ser_array, SerEntry, index).val;

      if (this_val < type)
	{
	  low_index = index + 1;
	  goto again;
	}
      else if (this_val > type)
	{
	  high_index = index - 1;
	  goto again;
	}
      else
	{
	  return & g_array_index (ser_array, SerEntry, index);
	}
    }

  edsio_generate_intint_event (EC_EdsioUnregisteredType,
			       type & EDSIO_LIBRARY_OFFSET_MASK,
			       type >> EDSIO_LIBRARY_OFFSET_BITS);
  return NULL;
}

gboolean
serializeio_unserialize_generic_internal (SerialSource *source,
					  SerialType   *object_type,
					  void        **object,
					  gboolean      set_allocation)
{
  SerialType type = (* source->source_type) (source, set_allocation);
  SerEntry* ent;
  gboolean res = FALSE;

  if (type < 0)
    return FALSE;

  ent = serializeio_find_entry (type);

  (*object_type) = type;

  if (ent)
    {
      res = ent->unserialize_func (source, object);
    }

  return res;
}

gboolean
serializeio_unserialize_generic (SerialSource *source,
				 SerialType   *object_type,
				 void        **object)
{
  return serializeio_unserialize_generic_internal (source, object_type, object, TRUE);
}

gboolean
serializeio_serialize_generic (SerialSink    *sink,
			       SerialType     object_type,
			       void          *object)
{
  return serializeio_serialize_generic_internal (sink, object_type, object, TRUE);
}

gboolean
serializeio_serialize_generic_internal (SerialSink    *sink,
					SerialType     object_type,
					void          *object,
					gboolean       set_allocation)
{
  SerEntry* ent;
  gboolean res = FALSE;

  if (! (* sink->sink_type) (sink, object_type, set_allocation ? serializeio_generic_count (object_type, object) : 0, set_allocation))
    return FALSE;

  ent = serializeio_find_entry (object_type);

  if (ent)
    res = ent->serialize_func (sink, object);

  return res;
}

const char*
serializeio_generic_type_to_string (SerialType type)
{
  SerEntry* ent;
  const char* res = "*Unknown*";

  ent = serializeio_find_entry (type);

  if (ent)
    res = ent->name;

  return res;
}

guint
serializeio_generic_count (SerialType     object_type,
			   void          *object)
{
  SerEntry* ent;
  gboolean res = FALSE;

  ent = serializeio_find_entry (object_type);

  if (ent)
    res = ent->count_func (object);

  return res;
}

void
serializeio_generic_print (SerialType type, void* object, guint indent_spaces)
{
  SerEntry* ent;

  ent = serializeio_find_entry (type);

  if (ent)
    ent->print_func (object, indent_spaces);
  else
    {
      int i = 0;

      for (; i < indent_spaces; i += 1)
	g_print (" ");

      g_print ("*Type Not Registered*\n");
    }
}

gboolean
serializeio_unserialize_generic_acceptable (SerialSource *source,
					    guint32       accept,
					    SerialType   *object_type,
					    void        **object)
{
  gboolean s;

  s = serializeio_unserialize_generic (source, object_type, object);

  if (s)
    {
      if (accept != -1)
	{
	  if ((*object_type & EDSIO_LIBRARY_OFFSET_MASK) != (accept & EDSIO_LIBRARY_OFFSET_MASK))
	    {
	      edsio_generate_intint_event (EC_EdsioUnexpectedLibraryType,
					   accept & EDSIO_LIBRARY_OFFSET_MASK,
					   *object_type & EDSIO_LIBRARY_OFFSET_MASK);

	      return FALSE;
	    }

	  if (! ((*object_type & ~EDSIO_LIBRARY_OFFSET_MASK) |
		 (accept       & ~EDSIO_LIBRARY_OFFSET_MASK)))
	    {
	      edsio_generate_void_event (EC_EdsioUnexpectedType);

	      return FALSE;
	    }
	}
    }

  return s;
}
