/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they 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 these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/*
 * $XConsortium: List.cc /main/4 1996/08/06 09:18:55 rcs $
 *
 * Copyright (c) 1992 HaL Computer Systems, Inc.  All rights reserved.
 * UNPUBLISHED -- rights reserved under the Copyright Laws of the United
 * States.  Use of a copyright notice is precautionary only and does not
 * imply publication or disclosure.
 * 
 * This software contains confidential information and trade secrets of HaL
 * Computer Systems, Inc.  Use, disclosure, or reproduction is prohibited
 * without the prior express written permission of HaL Computer Systems, Inc.
 * 
 *                         RESTRICTED RIGHTS LEGEND
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions as set forth in subparagraph (c)(l)(ii) of the Rights in
 * Technical Data and Computer Software clause at DFARS 252.227-7013.
 *                        HaL Computer Systems, Inc.
 *                  1315 Dell Avenue, Campbell, CA  95008
 * 
 */

#define C_List
#define L_Basic

#include "Prelude.h"

// /////////////////////////////////////////////////////////////////
// class constructor
// /////////////////////////////////////////////////////////////////

List::List (int initial_size, int increment, grow_method_t method,
	    bool delete_elements)
: f_dtor_delete_elements (delete_elements)
{
  assert (initial_size >= 0);
  f_length = 0;
  f_internal_length = initial_size;
  f_grow_method = method;
  if (increment == 0)
    f_increment = initial_size;
  else
    f_increment = increment;
  assert (f_increment > 0);
  f_list_element = (FolioObject **)
    malloc (sizeof (FolioObject *) * initial_size);
}


// /////////////////////////////////////////////////////////////////
// class destructor
// /////////////////////////////////////////////////////////////////

List::~List()
{
  if (f_dtor_delete_elements)
    remove_all (TRUE);
  free ((char *) f_list_element);
}

// /////////////////////////////////////////////////////////////////
// check_space - create more space for elements if necessary
// /////////////////////////////////////////////////////////////////

void
List::check_space (int num_additions)
{
  if (f_length + num_additions > f_internal_length)
    {
      // Loop until internal_length has grown large enough. 
      while (f_length + num_additions > f_internal_length)
	{
	  switch (f_grow_method)
	    {
	      case GROW_ADD:
	      f_internal_length = f_internal_length + f_increment;
	      break;
	      case GROW_MULTIPLY:
	      f_internal_length = f_internal_length * f_increment;
	      break;
	      default:
	      abort ();		// harsh!
	    }
	}

      f_list_element =
	(FolioObject **) realloc ((char *) f_list_element,
				  sizeof (FolioObject *) * f_internal_length);
    }
}    


// /////////////////////////////////////////////////////////////////
// append - append an element to the list
// /////////////////////////////////////////////////////////////////

void
List::append (FolioObject &element)
{
  check_space();

  /* -------- Add the element. -------- */
  f_list_element[f_length] = &element;
  f_length++;
  notify (APPENDED, (void *)(size_t) (f_length - 1));
}


// /////////////////////////////////////////////////////////////////
// remove - remove an element from the list 
// /////////////////////////////////////////////////////////////////

void
List::remove (FolioObject &element)
{
  /* -------- Look for the element in the list -------- */
  int location = find (element);

  if (location != -1)
    remove (location);
}


// /////////////////////////////////////////////////////////////////
// remove - remove an element from the list 
// /////////////////////////////////////////////////////////////////

void
List::remove (unsigned int location)
{
  // NOTE: check bounds 
  // Shift the array back and overwrite deleted element.
  f_length--;
  for (int i = location; i < f_length; i++)
    f_list_element[i] = f_list_element[i+1];
}


// /////////////////////////////////////////////////////////////////
// insert - insert an element in the list
// /////////////////////////////////////////////////////////////////

void
List::insert (unsigned int location, FolioObject *element)
{
  check_space();

  if (location > f_length)
    abort();
 
  // Shift the array forward to make room for new insertion.
  for (unsigned int i = f_length; i > location; i--)
    f_list_element[i] = f_list_element[i-1];

  // Insert the new element in the list.
  f_list_element[location] = element;
  f_length++;
  notify (INSERTED, (void *)(size_t) location);
}


// /////////////////////////////////////////////////////////////////
// find - find an element in a list
// /////////////////////////////////////////////////////////////////

int
List::find (FolioObject &element)
{
  /* -------- Search through the list, looking for element. -------- */
  int i;

  for (i = 0; i < f_length; i++)
    if (f_list_element[i] == &element)
      return (i);

  return (-1);
}


// /////////////////////////////////////////////////////////////////
// copy
// /////////////////////////////////////////////////////////////////

List *
List::copy() const
{
    // copy the contents of the list into a new one 

    // NOTE: use length() instead of f_length because it may be more general
    List *retlist = new List(length());
    for (unsigned int i = 0 ; i < length(); i++)
      retlist->append((*this)[i]);

    return retlist ;
}


// /////////////////////////////////////////////////////////////////
// remove_all - remove all the elements from the list
// /////////////////////////////////////////////////////////////////

void
List::remove_all (bool)
{
  unsigned short length = f_length;
  // Set length to 0, in case object dtor tries to mess with the list. 
  f_length = 0;
  // Reverse order delete is important for Long_Lived objects!! 
  while (length)
    delete f_list_element[--length];
}


/*

  NOTE: Here's how to make the list use less space per list:

  Make length an internal_length shorts.

  change grow_method to a bit field of length 1.
  change increment to a bit field of length 7.

  if grow_method is multiple, use the increment as a literal.
  if grow_method is add, use a static lookup table based on
  the increment value.

*/

