/*
 * Simulator of microcontrollers (pobj.cc)
 *
 * Copyright (C) 1999,99 Drotos Daniel, Talker Bt.
 * 
 * To contact author send email to drdani@mazsola.iit.uni-miskolc.hu
 *
 */

/* This file is part of microcontroller simulator: ucsim.

UCSIM 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.

UCSIM 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 UCSIM; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
/*@1@*/

#include "ddconfig.h"

#include <stdio.h>
#include <stdlib.h>

#include "pstr.h"
/*#include "pobjt.h"*/
#include "pobjcl.h"


/*									    *
  ==========================================================================*
								    cl_base *
  ==========================================================================*
									    *
*/

/* 
 * Initializing the object
 */

cl_base::cl_base(void)
{
  name= 0;
  parent= 0;
  children= 0;
}


/* 
 * Destructing the object: calling hte virtual Done method
 */

cl_base::~cl_base(void)
{
  if (name)
    free(name);
  if (children)
    {
      int i;
      for (i= 0; i < children->count; i++)
	{
	}
      children->disconn_all();
      delete children;
    }
  parent= 0;
}

int cl_base::init(void) {return(0);}

const char *
cl_base::get_name(const char *def)
{
  if (!name)
    return(def);
  return(name);
}

const char *
cl_base::set_name(const char *new_name)
{
  if (name)
    free(name);
  if (!new_name)
    name= 0;
  else if (*new_name)
    name= strdup(new_name);
  else
    name= strdup("");
  return(name);
}

const char *
cl_base::set_name(const char *new_name, const char *def_name)
{
  char *def;

  if (!def_name ||
      *def_name == '\0')
    def= strdup("");
  else
    def= strdup(def_name);
  if (name)
    free(name);
  if (!new_name)
    name= def;
  else if (*new_name)
    name= strdup(new_name);
  else
    name= def;
  return(name);
}

bool
cl_base::is_named(const char *the_name)
{
  if (!name ||
      !*name ||
      !the_name ||
      !*the_name)
    return(DD_FALSE);
  return(strcmp(name, the_name) == 0);
}

bool
cl_base::is_inamed(const char *the_name)
{
  if (!name ||
      !*name ||
      !the_name ||
      !*the_name)
    return(DD_FALSE);
  return(strcasecmp(name, the_name) == 0);
}


int
cl_base::nuof_children(void)
{
  if (!children)
    return(0);
  return(children->count);
}

void
cl_base::add_child(class cl_base *child)
{
  if (!children)
    {
      char *s;
      s= (char*)malloc(strlen(get_name("?"))+100);
      sprintf(s, "childs of %s", get_name("?"));
      children= new cl_list(1, 1, s);
      free(s);
    }
  if (child)
    {
      children->add(child);
      child->parent= this;
    }
}

void
cl_base::remove_child(class cl_base *child)
{
  if (child &&
      children)
    {
      child->unlink();
      children->disconn(child);
    }
}

void
cl_base::remove_from_chain(void)
{
  if (parent)
    parent->remove_child(this);
}

void
cl_base::unlink(void)
{
  parent= 0;
}

class cl_base *
cl_base::first_child(void)
{
  if (!children ||
      children->count == 0)
    return(0);
  return(dynamic_cast<class cl_base *>(children->object_at(0)));
}

class cl_base *
cl_base::next_child(class cl_base *child)
{
  if (!children ||
      !child)
    return(0);
  return((class cl_base *)(children->next(child)));
}


bool
cl_base::handle_event(class cl_event &event)
{
  return(pass_event_down(event));
}

bool
cl_base::pass_event_down(class cl_event &event)
{
  int i;
  if (!children)
    return(DD_FALSE);
  for (i= 0; i < children->count; i++)
    {
      class cl_base *child=
	dynamic_cast<class cl_base *>(children->object_at(i));
      if (child)
	{
	  child->handle_event(event);
	  if (event.is_handled())
	    return(DD_TRUE);
	}
    }
  return(DD_FALSE);
}


/*
 * Event
 */

cl_event::cl_event(enum event what_event):
  cl_base()
{
  handled= DD_FALSE;
  what= what_event;
}

cl_event::~cl_event(void)
{
}


/*									    *
  ==========================================================================*
								    cl_list *
  ==========================================================================*
									    *
*/

/* 
 * Initializing a collection
 */

cl_list::cl_list(t_index alimit, t_index adelta, const char *aname):
  cl_base()
{
  count= 0;
  Items= 0;
  Limit= 0;
  Delta= adelta;
  set_limit(alimit);
  set_name(aname, "unnamed list");
}


/* 
 * Disposing object's variables
 */

cl_list::~cl_list(void)
{
  //delete Items;
  free(Items);
}


/*
 * Get indexed item from the collection
 */

void *
cl_list::at(t_index index)
{
  if (index < 0 ||
      index >= count)
    error(1, index);
  return(Items[index]);
}

class cl_base *
cl_list::object_at(t_index index)
{
  if (index < 0 ||
      index >= count)
    error(1, index);
  return((class cl_base *)(Items[index]));
}

/*void *
cl_list::operator[](t_index index)
{
  if (index < 0 ||
      index >= count)
    error(1, 0);
  return(Items[index]);  
}*/


/*
 * Deleting the indexed item from the collection
 */
#include "globals.h"
void
cl_list::disconn_at(t_index index)
{
  if (index < 0 ||
      index >= count)
    error(1, 0);
  count--;
  /*{ char s[1000];
  s[0]='\0';
  sprintf(s, "disconn_at(%d) PC=0x%x", index,
	  application?
	  ((application->sim)?
	   ((application->sim->uc)?(application->sim->uc->PC):
	    -3):
	   -1):
	  -2);
	  strcat(s,"\n");}*/
  memmove(&Items[index], &Items[index+1], (count-index)*sizeof(void *));
}


/*
 * Deleting an item from the collection but not disposing it
 */

void
cl_list::disconn(void *item)
{
  t_index i;

  if (index_of(item, &i))
    disconn_at(i);
}


/* 
 * Deleting all the items from the collection but not disposing them
 */

void
cl_list::disconn_all(void)
{
  count= 0;
}


/*
 * Deleting the indexed item from the collection and disposing it
 */

void
cl_list::free_at(t_index index)
{
  void *Item= at(index);

  disconn_at(index);
  free_item(Item);
}

void
cl_list::free_all(void)
{
  t_index i;

  if (count)
    {
      for (i= count-1; i; i--)
	free_at(i);
      free_at(0);
    }
}


/*
 * Inserting a new item to the exact position
 */

void
cl_list::add_at(t_index index, void *item)
{
  if (index < 0 )
    error(1, 0);
  if (count == Limit)
    set_limit(count + Delta);

  { char s[1000];
  s[0]='\0';
  sprintf(s, "%s add_at(%d,%p) PC=0x%x (count=%d)", get_name("?"), index, item,
	  application?
	  ((application->sim)?
	   ((application->sim->uc)?(application->sim->uc->PC):
	    -3):
	   -1):
	  -2, count);
  strcat(s,"\n");}
  memmove(&Items[index+1], &Items[index], (count-index)*sizeof(void *));
  count++;

  Items[index]= item;
}


/* 
 * Put a new item to the collection. This function replaces an existing
 * item with a new one but it does not delete or dispose the old item!
 */

void
cl_list::put_at(t_index index, void *item)
{
  if (index >= count)
    error(1, 0);
  Items[index]= item;
}


/* 
 * Action taken when an error occure
 */

void
cl_list::error(t_index code, t_index info)
{
  fprintf(stderr, 
	  "Collection index error. Code= %d, Info= %d.\n",
	  code, info);
  exit(code);
}


/* 
 * Iterator method. This function calls 'Test' using every items as Test's
 * argument until Test returns TRUE.
 */

void *
cl_list::first_that(match_func test, const void *arg)
{
  for (t_index i= 0; i < count; i++)
    {
      if (test(Items[i], arg)) return(Items[i]);
    }
  return(0);
}


/* 
 * Iterator method. This function calls 'Action' using every items as
 * Action's argument.
 */

void
cl_list::for_each(iterator_func action, void *arg)
{
  for(t_index i= 0; i < count; i++)
    action(Items[i], arg);
}


/* 
 * Disposing an item.
 */

void
cl_list::free_item(void *item)
{
  delete (class cl_base*)item;
}


/* 
 * Get the number of collected items.
 */

int
cl_list::get_count(void)
{
  return(count);
}

void *
cl_list::pop(void)
{
  void *i;

  if (!count)
    return(0);
  i= Items[0];
  disconn_at(0);
  return(i);
}

void *
cl_list::top(void)
{
  if (!count)
    return(0);
  return(Items[0]);
}


/* 
 * Returning the index of an item.
 */

t_index
cl_list::index_of(void *item)
{
  for (t_index i= 0; i < count; i++)
    if (item == Items[i])
      return(i);
  error(1, 0);
  return(0);    /* Needed by Sun! */
}

bool
cl_list::index_of(void *item, t_index *idx)
{
  for (t_index i= 0; i < count; i++)
    if (item == Items[i])
      {
	if (idx)
	  *idx= i;
	return(DD_TRUE);
      }
  return(DD_FALSE);
}

void *
cl_list::next(void *item)
{
  for (t_index i= 0; i < count; i++)
    if (item == Items[i])
      {
	if (count >= 2 &&
	    i < count-1)
	  return(Items[i+1]);
      }
  return(0);
}


/* 
 * Inserting a new item to the collection.
 */

t_index
cl_list::add(void *item)
{
  t_index loc= count;

  add_at(count, item);
  return(loc);
}

t_index
cl_list::add(class cl_base *item, class cl_base *parent)
{
  if (parent && item)
    parent->add_child(item);
  return(add(item));
}

void
cl_list::push(void *item)
{
  if (count)
    add_at(0, item);
  else
    add(item);
}


/* 
 * Iterator method. This function calls 'Test' using every items
 * (in reversed order) as Test's argument until Test returns TRUE.
 */

void *
cl_list::last_that(match_func test, void *arg)
{
  for(t_index i= count; i > 0; i--)
    if (test(Items[i-1], arg))
      return(Items[i-1]);
  return(0);
}


/*
 * ???
 */

/*void
cl_list::pack(void)
{
  void **CurDst= Items;
  void **CurSrc= Items;
  void **Last  = Items + count;

  while (CurSrc < Last)
    {
      if (*CurSrc != 0)
	*CurDst++= *CurSrc;
      *CurSrc++;
    }
}*/


/*
 * Setting up the maximum number of items. This function may expand
 * the size of the collection.
 */

void
cl_list::set_limit(t_index alimit)
{
  void **AItems;

  if (alimit < count)
    alimit= count;
  if (alimit > (int)max_list_size)
    alimit= max_list_size;
  if (alimit != Limit)
    {
      if (alimit == 0)
	AItems= 0;
      else
	{
	  //AItems = new void *[alimit];
	  int i= alimit*(sizeof(void *));
	  AItems= (void **)malloc(i);
	  if (count)
	    memcpy(AItems, Items, count*sizeof(void *));
	}
      //delete Items;
      free(Items);
      Items= AItems;
      Limit= alimit;
    }
}


/*									    *
  ==========================================================================*
							     cl_sorted_list *
  ==========================================================================*
									    *
*/

/* 
 * Initilizing the sorted collection
 */

cl_sorted_list::cl_sorted_list(t_index alimit, t_index adelta, const char *aname):
  cl_list(alimit, adelta, aname)
{
  Duplicates= DD_FALSE;
}


cl_sorted_list::~cl_sorted_list(void) {}


/* 
 * Get the address of the key field in an item.
 */

const void *
cl_sorted_list::key_of(void *item)
{
  return(item);
}


/* 
 * Get index of an item.
 */

t_index
cl_sorted_list::index_of(void *item)
{
  t_index	i;

  if (search(key_of(item), i) == 0)
    return(ccNotFound);
  else
    {
      if (Duplicates)
	while (i < count &&
	       item != Items[i])
	  i++;
      if (i < count)
	return(i);
      else
	return(ccNotFound);
    }
}


/*
 * Inserting a new item to the collection
 */

t_index
cl_sorted_list::add(void *item)
{
  t_index i;

  if (search(key_of(item), i) == 0 ||
      Duplicates)   				// order dependency!
    add_at(i, item);				// must do Search
						// before calling
						// AtInsert
  return(i);
}


/*
 * Searching an item using binary search.
 */

bool
cl_sorted_list::search(const void *key, t_index &index)
{
  t_index l  = 0;
  t_index h  = count - 1;
  bool    res= DD_FALSE;
  
  while (l <= h)
    {
      t_index i= (l + h) >> 1;
      t_index c= compare(key_of(Items[i]), key);
      if (c < 0) l= i + 1;
      else
	{
	  h= i - 1;
	  if (c == 0)
	    {
	      res= DD_TRUE;
	      if (!Duplicates)
		l= i;
	    }
	}
    }
  index= l;
  return(res);
}


/*									    *
  ==========================================================================*
							         cl_strings *
  ==========================================================================*
									    *
*/

/* 
 * Initilizing the string collection
 */

cl_strings::cl_strings(t_index alimit, t_index adelta, const char *aname):
  cl_sorted_list(alimit, adelta, aname)
{
  Duplicates= DD_TRUE;
}


cl_strings::~cl_strings(void) {}


/* 
 * Comapare two string from the collection
 */

int
cl_strings::compare(const void *key1, const void *key2)
{
  return(strcmp(static_cast<const char *>(key1), static_cast<const char *>(key2)));
}


/* 
 * Deallocate string item of the collection
 */

void
cl_strings::free_item(void* item)
{
  delete (class cl_base*)item;
}


/*									    *
  ==========================================================================*
							        cl_ustrings *
  ==========================================================================*
									    *
*/

/*
 * Initilizing the unsorted string collection
 */

cl_ustrings::cl_ustrings(t_index alimit, t_index adelta, const char *aname):
  cl_strings(alimit, adelta, aname)
{}


cl_ustrings::~cl_ustrings(void) {}


/*
 * Comapare two string from the collection
 */

int
cl_ustrings::compare(const void *key1, const void *key2)
{
  return(-1);
}


/* 
 * Searching an item using linear search.
 */

bool
cl_ustrings::search(const void *key, t_index& index)
{
  t_index i    = 0;
  bool    found= DD_FALSE;
  const void *Actual;

  if ((count) && key)
    {
      while (!found && (i < count))
	{
	  Actual= key_of(at(i));
	  found = (Actual != 0) &&
		  (compare(key, Actual) == 0);
	  i++;
	}
    }
  if (found)
    index= i-1;
  else
    index= count;

  return(found);
}

/* End of pobj.cc */
