/*
 * 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 <string.h>

#include <spar/sl_parser.h>
#include <spar/sl_error.h>
#include <spar/sl_io.h>
#include <spar/sl_util.h>
#include <spar/sl_sort.h>

static sl_variable *head;
static sl_variable *z;
static sl_size __var_counter;

static sl_variable *
__tree_search (const char name[])
{
  sl_variable *p;
  sl_variable *x;

  p = head;
  x = head->r;

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

  return (sl_variable *) NULL;
}

static void
__tree_insert (const char name[], double value)
{
  if (__var_counter > SL_MAX_VARIABLES - 1)
    {
      sl_error_throw (SL_ERROR_CLASS_PARSER, SL_ERROR_MAXVARS,
		      "the variable '%s' wasn't added", name);
    }
  else
    {
      sl_variable *p;
      sl_variable *x;

      p = head;
      x = head->r;

      while (x != z)
	{
	  p = x;
	  x =
	    ((sl_strncmp (name, x->name, SL_IDENTIFIER_LENGHT)) <
	     0) ? x->l : x->r;
	}

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

      sl_variable_edit_name (x, name);
      sl_variable_edit_comment (x, "no comment");
      sl_variable_edit_value (x, value);
      x->l = z;
      x->r = z;

      if ((sl_strncmp (name, p->name, SL_IDENTIFIER_LENGHT)) < 0)
	{
	  p->l = x;
	}
      else
	{
	  p->r = x;
	}

      __var_counter++;
    }
}

static void
__tree_print_node (sl_variable * x)
{
  if (x != z)
    {
      __tree_print_node (x->l);
      sl_writeln ("%15s = %g\t{%-s}", x->name, x->value, x->comment);
      __tree_print_node (x->r);
    }
}

static void
__tree_print (void)
{
  __tree_print_node (head->r);
}

static void
__tree_delete_entry (const char *name)
{
  sl_variable *c;
  sl_variable *p;
  sl_variable *x;
  sl_variable *t;

  sl_variable_edit_name (z, name);
  p = head;
  x = head->r;

  while ((sl_strncmp (name, x->name, SL_IDENTIFIER_LENGHT)) != 0)
    {
      p = x;

      if ((sl_strncmp (name, x->name, SL_IDENTIFIER_LENGHT)) < 0)
	{
	  x = x->l;
	}
      else
	{
	  x = 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;
    }

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

  if ((sl_strncmp (name, p->name, SL_IDENTIFIER_LENGHT)) < 0)
    {
      p->l = x;
    }
  else
    {
      x->r = x;
    }
}

static void
__tree_free_node (sl_variable * x)
{
  if (x != z)
    {
      __tree_free_node (x->l);
      __tree_free_node (x->r);
      __tree_delete_entry (x->name);
    }
}

static void
__tree_free (void)
{
  __tree_free_node (head->r);
}

int
sl_variable_comment (const char name[], const char comment[])
{
  sl_variable *tmp;

  tmp = __tree_search (name);

  if (tmp != NULL)
    {
      sl_variable_edit_comment (tmp, comment);

      return SL_SUCCESS;
    }
  else
    {
      return SL_ERROR;
    }
}

int
sl_variable_init (void)
{
  z = (sl_variable *) sl_malloc (sizeof *z);
  z->l = z;
  z->r = z;

  head = (sl_variable *) sl_malloc (sizeof *head);
  head->r = z;
  head->l = z;

  __var_counter = 0;

  return SL_SUCCESS;
}


int
sl_variable_remove_all (void)
{
  __tree_free ();

  return SL_SUCCESS;
}

int
sl_variable_old (const char name[])
{
  sl_variable *tmp;

  tmp = __tree_search (name);

  if (tmp != NULL)
    {
      sl_variable_old_value (tmp);

      return SL_SUCCESS;
    }
  else
    {
      return SL_ERROR;
    }
}


int
sl_variable_get (const char name[], double *r)
{
  sl_variable *tmp;

  tmp = __tree_search (name);

  if (tmp != NULL)
    {
      *r = tmp->value;

      return SL_SUCCESS;
    }
  else
    {
      return SL_ERROR;
    }
}

int
sl_variable_set (const char name[], double value)
{
  double tmp;

  if (sl_constant_get (name, &tmp) == SL_SUCCESS)
    {
      sl_error_throw (SL_ERROR_CLASS_PARSER, SL_ERROR_CONST, name);

      return SL_ERROR;
    }
  else
    {
      sl_variable *tmp;

      tmp = __tree_search (name);

      if (tmp == NULL)
	{
	  __tree_insert (name, value);
	}
      else
	{
	  sl_variable_edit_value (tmp, value);
	}

      return SL_SUCCESS;

    }
}

void
sl_variable_print_all (void)
{
  sl_writeln ("Vars Table");
  __tree_print ();
}


syntax highlighted by Code2HTML, v. 0.9.1