/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
 *
 * gsttype.c: Media-type management functions
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* TODO:
 * probably should set up a hash table for the type id's, since currently
 * it's a rather pathetic linear search.  Eventually there may be dozens
 * of id's, but in reality there are only so many instances of lookup, so
 * I'm not overly worried yet...
 */

#include "gst_private.h"

#include "gsttype.h"
#include "gstregistry.h"
#include "gstobject.h"
#include "gstlog.h"


/* global list of registered types */
static GList *_gst_types;
static guint16 _gst_maxtype;

static void 		gst_type_factory_class_init 	(GstTypeFactoryClass *klass);
static void 		gst_type_factory_init 		(GstTypeFactory *factory);

static GstCaps*		gst_type_type_find_dummy		(GstBuffer *buffer, gpointer priv);

static void 		gst_type_factory_unload_thyself 	(GstPluginFeature *feature);

static GstPluginFeatureClass *parent_class = NULL;
/* static guint gst_type_factory_signals[LAST_SIGNAL] = { 0 }; */

GType
gst_type_factory_get_type (void)
{
  static GType typefactory_type = 0;

  if (!typefactory_type) {
    static const GTypeInfo typefactory_info = {
      sizeof (GstTypeFactoryClass),
      NULL,
      NULL,
      (GClassInitFunc) gst_type_factory_class_init,
      NULL,
      NULL,
      sizeof(GstTypeFactory),
      0,
      (GInstanceInitFunc) gst_type_factory_init,
      NULL
    };
    typefactory_type = g_type_register_static (GST_TYPE_PLUGIN_FEATURE,
                                               "GstTypeFactory", &typefactory_info, 0);
  }
  return typefactory_type;
}

static void
gst_type_factory_class_init (GstTypeFactoryClass *klass)
{
  GObjectClass *gobject_class;
  GstObjectClass *gstobject_class;
  GstPluginFeatureClass *gstpluginfeature_class;

  gobject_class = (GObjectClass*)klass;
  gstobject_class = (GstObjectClass*)klass;
  gstpluginfeature_class = (GstPluginFeatureClass*) klass;

  parent_class = g_type_class_ref (GST_TYPE_PLUGIN_FEATURE);

  gstpluginfeature_class->unload_thyself = GST_DEBUG_FUNCPTR (gst_type_factory_unload_thyself);

  _gst_types = NULL;
  _gst_maxtype = 1;		/* type 0 is undefined */

}

static void
gst_type_factory_init (GstTypeFactory *factory)
{
}

/**
 * gst_type_factory_new:
 * @definition: the definition to use
 *
 * Creata a new typefactory from the given definition.
 *
 * Returns: the new typefactory
 */
GstTypeFactory* 
gst_type_factory_new (GstTypeDefinition *definition)
{
  GstTypeFactory *factory;

  g_return_val_if_fail (definition != NULL, NULL);
  g_return_val_if_fail (definition->name != NULL, NULL);
  g_return_val_if_fail (definition->mime != NULL, NULL);

  factory = gst_type_factory_find (definition->name);

  if (!factory) {
    factory = GST_TYPE_FACTORY (g_object_new (GST_TYPE_TYPE_FACTORY, NULL));
  }


  GST_PLUGIN_FEATURE_NAME (factory) = g_strdup (definition->name);
  factory->mime = g_strdup (definition->mime);
  factory->exts = g_strdup (definition->exts);
  factory->typefindfunc = definition->typefindfunc;

  return factory;
}

/**
 * gst_type_register:
 * @factory: the type factory to register
 *
 * Register a new type factory to the system.
 *
 * Returns: the new type id
 */
guint16
gst_type_register (GstTypeFactory *factory)
{
  guint16 id;
  GstType *type;

  g_return_val_if_fail (factory != NULL, 0);

/*  GST_INFO (GST_CAT_TYPES,"type register %s", factory->mime); */
  id = gst_type_find_by_mime (factory->mime);

  if (!id) {
    type = g_new0 (GstType, 1);

    type->id =		_gst_maxtype++;
    type->mime =	factory->mime;
    type->exts =	factory->exts;
    type->factories = 	NULL;
    _gst_types =	g_list_prepend (_gst_types, type);

    id = type->id;

  } else {
    type = gst_type_find_by_id (id);
    /* now we want to try to merge the types and return the original */

    /* FIXME: do extension merging here, not that easy */

    /* if there is no existing typefind function, try to use new one  */
  }
  GST_DEBUG (GST_CAT_TYPES,"gsttype: %s(%p) gave new mime type '%s', id %d", 
		    GST_OBJECT_NAME (factory), factory, type->mime, type->id);
  type->factories = g_slist_prepend (type->factories, factory);

  return id;
}

static guint16 
gst_type_find_by_mime_func (const gchar *mime)
{
  GList *walk;
  GstType *type;
  gint typelen,mimelen;
  gchar *search, *found;

  g_return_val_if_fail (mime != NULL, 0);

  walk = _gst_types;
/*  GST_DEBUG (GST_CAT_TYPES,"searching for '%s'",mime); */
  mimelen = strlen (mime);
  while (walk) {
    type = (GstType *)walk->data;
    search = type->mime;
/*    GST_DEBUG (GST_CAT_TYPES,"checking against '%s'",search); */
    typelen = strlen (search);
    while ((search - type->mime) < typelen) {
      found = strstr (search, mime);
      /* if the requested mime is in the list */
      if (found) {
        if ((*(found + mimelen) == ' ') ||
            (*(found + mimelen) == ',') ||
            (*(found + mimelen) == '\0')) {
          return type->id;
        } else {
          search = found + mimelen;
        }
      } else
        search += mimelen;
    }
    walk = g_list_next (walk);
  }

  return 0;
}

/**
 * gst_type_find_by_mime:
 * @mime: the mime type to find
 *
 * Find the type id of a given mime type.
 *
 * Returns: the type id
 */
guint16
gst_type_find_by_mime (const gchar *mime)
{
  return gst_type_find_by_mime_func (mime);
}

/**
 * gst_type_find_by_ext:
 * @ext: the extension to find
 *
 * Find the type id of a given extention.
 *
 * Returns: the type id
 */
guint16
gst_type_find_by_ext (const gchar *ext)
{
  /* FIXME */
  g_warning ("gsttype: find_by_ext not implemented");
  return 0;
}

/**
 * gst_type_find_by_id:
 * @id: the type id to lookup
 *
 * Find the type of a given type id.
 *
 * Returns: the type
 */
GstType*
gst_type_find_by_id (guint16 id)
{
  GList *walk = _gst_types;
  GstType *type;

  while (walk) {
    type = (GstType *)walk->data;
    if (type->id == id)
      return type;
    walk = g_list_next (walk);
  }

  return NULL;
}

/**
 * gst_type_get_list:
 *
 * Return a list of all registered types.
 *
 * Returns: a list of GstTypes
 */
const GList*
gst_type_get_list (void)
{
  return _gst_types;
}

/**
 * gst_type_factory_find:
 * @name: the name of the typefactory to find
 *
 * Return the TypeFactory with the given name. 
 *
 * Returns: a GstTypeFactory with the given name;
 */
GstTypeFactory*
gst_type_factory_find (const gchar *name)
{
  GstPluginFeature *feature;

  g_return_val_if_fail (name != NULL, NULL);

  feature = gst_registry_pool_find_feature (name, GST_TYPE_TYPE_FACTORY);
  if (feature)
    return GST_TYPE_FACTORY (feature);

  return NULL;
}

static void
gst_type_factory_unload_thyself (GstPluginFeature *feature)
{
  GstTypeFactory *factory;

  g_return_if_fail (GST_IS_TYPE_FACTORY (feature));

  factory = GST_TYPE_FACTORY (feature);

  if (factory->typefindfunc)
    factory->typefindfunc = gst_type_type_find_dummy;
}

static GstCaps*
gst_type_type_find_dummy (GstBuffer *buffer, gpointer priv)
{
  GstCaps *res = NULL;
  GstTypeFactory *factory = (GstTypeFactory *)priv;

  GST_DEBUG (GST_CAT_TYPES,"gsttype: need to load typefind function for %s", factory->mime);

  if (gst_plugin_feature_ensure_loaded (GST_PLUGIN_FEATURE (factory))) {
    if (factory->typefindfunc == gst_type_type_find_dummy) {
      /* looks like we didn't get a real typefind function */
      g_warning ("could not load valid typefind function for %s\n", factory->mime);
    }
    else if (factory->typefindfunc) {
      res = factory->typefindfunc (buffer, factory);
    }
  }

  return res;
}
