/*
 * The Spar Library - modular math parser
 * Copyright (C) 2000,2001 Davide Angelocola <davide178@inwind.it>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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.
 *
 */
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>

#include <spar/sl_conf.h>
#include <spar/sl_io.h>
#include <spar/sl_module.h>
#include <spar/sl_error.h>
#include <spar/sl_util.h>

struct module_manager_node_s
{
  int key;
  sl_module module;

  void *handle;			/* dl interface */

  struct module_manager_node_s *l;
  struct module_manager_node_s *r;
};

typedef struct module_manager_node_s module_manager_node;

static module_manager_node *head;
static module_manager_node *z;

static void
_tree_init (void)
{
  z = (module_manager_node *) sl_malloc (sizeof *z);

  z->l = z;
  z->r = z;
  z->key = -1;

  head = (module_manager_node *) sl_malloc (sizeof *head);

  head->r = z;
  head->key = 0;
}

static void
_tree_insert (int v, const sl_module * m, void *handle)
{
  module_manager_node *p, *x;

  p = head;
  x = head->r;
  while (x != z)
    {
      p = x;
      x = (v < x->key) ? x->l : x->r;
    }

  x = (module_manager_node *) sl_malloc (sizeof *x);

  x->key = v;
  x->module = *m;
  x->handle = handle;
  x->l = z;
  x->r = z;

  if (v < p->key)
    p->l = x;
  else
    p->r = x;
}

static module_manager_node *
_tree_search_by_name (const char *name)
{
  module_manager_node *p;
  module_manager_node *x;

  p = head;
  x = head->r;
  while (x != z)
    {
      p = x;
      if ((strcmp (name, x->module.name)) == 0)
	return x;
      else
	x = ((strcmp (name, x->module.name)) < 0) ? x->l : x->r;
    }

  return (module_manager_node *) NULL;
}


static void
_tree_print_node (module_manager_node * x)
{
  if (x != z)
    {
      _tree_print_node (x->l);
      sl_writeln ("%s (ID %d)", x->module.name, x->key);
      _tree_print_node (x->r);
    }
}

static void
_tree_remove_node (int k)
{
  module_manager_node *c;
  module_manager_node *p;
  module_manager_node *x;
  module_manager_node *t;

  z->key = k;
  p = head;
  x = head->r;

  while (k != x->key)
    {
      p = x;
      x = (k < x->key) ? x->l : x->r;
    }

  t = x;

  if (t->r == z)
    x = x->l;
  else if (t->r->l == z)
    {
      x = x->r;
      x->l = t->l;
    }
  else
    {
      c = x->r;
      while (c->l->l != z)
	c = c->l;
      x = c->l;
      c->l = x->r;
      x->l = t->l;
      x->r = t->r;
    }

  dlclose (t->handle);
  sl_free (t->r, sizeof (*t->r));
  sl_free (t->l, sizeof (*t->l));
  sl_free (t, sizeof (*t));

  if (k < p->key)
    p->l = x;
  else
    p->r = x;
}

static void
_tree_free_node (module_manager_node * x)
{
  if (x != z)
    {
      _tree_free_node (x->l);
      _tree_free_node (x->r);
      _tree_remove_node (x->key);
    }
}

static void
_tree_free (void)
{
  _tree_free_node (head->r);
}


int
sl_module_manager_add (const sl_module * m, int module_ID, void *handle)
{
  module_manager_node *t;
  if ((t = _tree_search_by_name (m->name)) == NULL)
    {
      _tree_insert (module_ID, m, handle);
#ifdef DEBUG
      sl_writeln ("the module (ID %d) was loaded.", module_ID);
#endif
      return SL_SUCCESS;
    }
  else
    {
      sl_error_throw (SL_ERROR_CLASS_ERROR, SL_ERROR_MODULE_LOAD, "(ID %d).",
		      module_ID);

      return SL_ERROR;
    }
}

int
sl_module_manager_remove (const char *name)
{
  module_manager_node *t;
  if ((t = _tree_search_by_name (name)) != NULL)
    {
      _tree_remove_node (t->key);

      return SL_SUCCESS;
    }
  else
    {
      return SL_ERROR;
    }
}

int
sl_module_manager_print (const char *name)
{
  module_manager_node *t;

  if ((t = _tree_search_by_name (name)) != NULL)
    {
      sl_writeln ("Detailed module info: ");
      sl_writeln ("   name    : %s", t->module.name);
      sl_writeln ("   info    : %s", t->module.description);
      sl_writeln ("   version : %s", t->module.version);
      sl_writeln ("   ID 	   : %d", t->key);

      return SL_SUCCESS;
    }
  else
    {
      return SL_ERROR;
    }
}

void
sl_module_manager_remove_all (void)
{
  _tree_free ();
}

void
sl_module_manager_print_all (void)
{
  sl_writeln ("Modules loaded");
  _tree_print_node (head->r);
}

void
sl_module_manager_init (void)
{
  _tree_init ();
}

int
sl_module_manager_run (const char *name)
{
  module_manager_node *t;

  if ((t = _tree_search_by_name (name)) != NULL)
    {
      int rc;

      sl_writeln ("Running module '%s' (ID %d)", t->module.name, t->key);
      sl_writeln ("");

      rc = t->module.main ();

      if (rc == SL_ERROR)
	{
	  sl_error_throw (SL_ERROR_CLASS_ERROR, SL_ERROR_MODULE_RUN,
			  "'%s' ID (%d)", t->module.name, t->key);

	  return SL_ERROR;
	}
      else
	{
	  return SL_SUCCESS;
	}
    }
  else
    {
      sl_writeln ("modules '%s' not found.", name);

      return SL_ERROR;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1