// Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
// Author(s): Patricia K. Fasel (Los Alamos Nat. Lab.), Jonathan Berry
// This software is copyrighted by the DIMACS Center at Rutgers, The State
// University of New Jersey.  IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
// RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  DISCLAIM
// ALL LIABILITY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
// DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
// IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
// NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
// MODIFICATIONS.
// The authors hereby grant permission to use, copy, modify, distribute,
// and license this software and its documentation for any purpose, provided
// that existing copyright notices are retained in all copies and that this
// notice is included verbatim in any distributions. No written agreement,
// license, or royalty fee is required for any of the authorized uses.
// Modifications to this software may be copyrighted by their authors
// and need not follow the licensing terms described here, provided that
// the new terms are clearly indicated on the first page of each file where
// they apply.
// Last File Update: 21-Jul-1997
// 
#ifndef List_CC
#define List_CC
#include <iostream.h>
/*
template <class Item>
void *
ListNode<Item>::operator new(size_t size)
{
	// as suggested in Lippman, 2nd Edition:
	register ListNode<Item>* p;
	if (!mm_free_store) {
		size_t chunk = mm_block_size * size;
		mm_free_store = p = (ListNode<Item>*) new char[chunk];
		for (; p!= &mm_free_store[mm_block_size-1];
		     p->mm_next = p+1, p++)
			;
		p->mm_next = 0;
	}
	p = mm_free_store;
	mm_free_store = mm_free_store->mm_next;
	return p;
}
template <class Item>
void
ListNode<Item>::operator delete(void *p, size_t size) 
{
	((ListNode<Item>*)p)->mm_next = mm_free_store;
	mm_free_store = (ListNode<Item>*)p;
}
*/
//
// constructor(implicit)
//
template <class Item>
List<Item>::List() : _first(0), _last(0)
{
	//cout << "standard List()" << endl;
}
//
// copy constructor
//
template <class Item>
List<Item>::List(const List<Item>& copy_list) :
                        _first(0), _last(0)
{
    //cout << "standard List(List)" << endl;
    Iterator<Item> get_item(&copy_list);
    Item item;
    while (get_item(item))
        append(item);
}
template <class Item>
List<Item>::List(const Container<Item>& c) :
                        _first(0), _last(0)
{
    Iterator<Item> get_item(&c);
    Item item;
    while (get_item(item))
        append(item);
}
template <class Item>
void
List<Item>::clear()
{
    ListNode<Item>* node = _first;
    while (!listEnd(node)) {
        ListNode<Item>* next_node = node->next();
        delete node; 
        node = next_node;
    }
    _first = _last = 0;
}
template <class Item>
ListNode<Item>*
List<Item>::cn2ListNode(const ContainerNode* n) const
{
	return ((ListNode<Item>*)n);
}
template <class Item>
ContainerNode*
List<Item>::ListNode2cn(ListNode<Item>* n) const
{
	//_current_node->_node = n;
	//return _current_node;
	return (ContainerNode*) n;
}
//
// destructor
//
template <class Item>
List<Item>::~List()
{
    clear();
}
template <class Item>
Item
List<Item>::info(const ContainerNode *node) const
{
	ListNode<Item>* cn = (ListNode<Item>*) node;
        if (!listEnd(cn))
                return  * (Item *) cn->info();
	static Item i;
        return i;
}
//
// copy one list to another
//
template <class Item>
List<Item>&
List<Item>::operator=(const List<Item>& copy_list)
{
    if (this != &copy_list) {
        clear();
        Iterator<Item> get_list(&copy_list);
        Item item;
        while (get_list(item))
            append(item);
    }
    return(*this);
}
//
// find the length of the list - note: a count is *not* kept 
// to make the list more scheme-like  STk can do list surgery
//
template <class Item>
int
List<Item>::size() const
{
    Iterator<Item> get_next(this);
    Item i;
    int count = 0;
    while (get_next(i))
	count++;
    return count;
}
//
// return last item   (no tail pointer to save space;  also more scheme-like)
//
template <class Item>
Item
List<Item>::last() const
{
    if (_last)
    	return _last->getItem();
    else {
        static Item it;
	return it;  
    }
    /**** old version (no last ptr (when LINK lists were also STk lists))
    if (listEnd(_first)) {
	warning("no last on empty list");
        static Item it;
	return it;  // empty_data;
    }
    ListNode<Item>* node;
    for (node=_first; !listEnd(node->next());node=node->next());
    */
}
//
// insert item into beginning of list
//
template <class Item>
ContainerNode*
List<Item>::prepend(const Item passed_item)
{
    ListNode<Item>* node = new ListNode<Item>(passed_item);
    node->setNext(_first);
    _first = node;
    if (!_last)
	_last = node;
    return ListNode2cn(node);
}
//
// append item to back of list
//
template <class Item>
ContainerNode*
List<Item>::append(const Item passed_item)
{
    ListNode<Item>* newNode = new ListNode<Item>(passed_item);
    if (listEnd(_first)) {
        newNode->setNext(_first);	// _first is an object with STk
        _first = _last = newNode;
    } else {
	if (listEnd(_last))
		error("List::append: non-null _first, null _last");
	newNode->setNext(_last->next());
	_last->setNext(newNode);
	_last = newNode;
	if (!listEnd(newNode->next())) {
		//cout << *this << endl;
		cout << "last: " << info(_last) << endl;
		error("List::append: error with listEnd");
	}
    }
    return ListNode2cn(newNode);
}
//
// insert item into beginning of list 
//
template <class Item>
ContainerNode*
List<Item>::insert(const Item passed_item)
{
    return prepend(passed_item);
}
//
// insert item before the node
//
template <class Item>
ContainerNode*
List<Item>::insertBefore(const Item passed_item, ContainerNode * in_node)
{
    ListNode<Item>* passed_node = cn2ListNode(in_node);
    if (listEnd(passed_node))
    {
	warning("List::insertBefore(): can't insert before null pointer");
        return ListNode2cn(passed_node);
    }
    ListNode<Item>* before_node = (ListNode<Item>*) passed_node;
    ListNode<Item>* node = new ListNode<Item>(passed_item);
   
    if ((_first == before_node) || (listEnd(_first)))  {
      if (listEnd(_first))   
		warning("List<Item>::insertBefore() called on empty list");
      if (listEnd(before_node)) {
		warning("List<Item>::insertBefore(it,NULL), returning NULL. ");
    		return ListNode2cn(before_node);
      }
      if (listEnd(_first))
	_last = node;
      node->setNext(_first);
      _first = node; 
      return ListNode2cn(node);
    }
    ListNode<Item> *n;
    for (n = _first; !listEnd(n->next()); n = n->next()) 
	if (n->next() == before_node)
	    break;
    node->setNext(n->next());
    n->setNext(node);
    return ListNode2cn(node);
}
template <class Item>
ContainerNode*
List<Item>::insertAfter(const Item passed_item, ContainerNode * in_node)
{
    ListNode<Item>* passed_node = cn2ListNode(in_node);
    if (listEnd(passed_node)) 
    {
	warning("can't insert after a null pointer");
        return ListNode2cn(passed_node);
    }
    ListNode<Item>* after_node = (ListNode<Item>*) passed_node;
    ListNode<Item>* node = new ListNode<Item>(passed_item);
    if (listEnd(after_node)) {
		warning("List<Item>::insertAfter(it,NULL), returning NULL ");
    		return ListNode2cn(after_node);
    }
    node->setNext(after_node->next());
    after_node->setNext(node);
    if (after_node == _last)
	_last = node;
    return ListNode2cn(node);
}
//
// return head of list and remove it
//
template <class Item>
Item
List<Item>::get()
{
    if (!listEnd(_first)) {
        ListNode<Item>* node = _first;
        Item pop_item = node->getItem();
        _first = node->next();
	if (listEnd(_first))
		_last = 0;
        delete node;
        return pop_item;
    }
    else
    {
	warning("can't get anything from empty list");
        static Item it;
        return it; // empty_data; 
    }
}
//
// return whether item is in list or not
//
template <class Item>
Bool
List<Item>::memberQ(const Item& passed_item) const
{
    if (listEnd(_first)) return FALSE;
    for (ListNode<Item>* node = _first; !listEnd(node); node = node->next())
        if (ElementOps<Item>::compareItems(passed_item, node->getItem())==0)
            return TRUE;
    return FALSE;
}
template <class Item>
ContainerNode* 
List<Item>::search(const Item& item) const
{
    ListNode<Item>* node;
    for (node = _first; !listEnd(node); node = node->next())
        if (ElementOps<Item>::compareItems(item, node->getItem()) ==0) 
            return ListNode2cn(node);
    return ListNode2cn(node);
} 
template <class Item>
void
List<Item>::sort(int (*compare)(const Item&, const Item&)=0) 
{
    if (listEnd(_first) || listEnd(_first->next()))
	return;
    if (!compare)
	compare = ElementOps<Item>::compareItems;
    static Item t;
    ListNode<Item> *dummy = new ListNode<Item>(t), *p, *c, *p2, *c2;
    dummy->setNext(_first);
    p=_first; 
    c=_first->next(); 
    while (!listEnd(c)) {
    	for (p2=dummy, c2=dummy->next(); 
		!listEnd(c2) && (compare(c2->getItem(), c->getItem()) <0); 
		p2=c2, c2=c2->next());
	if (compare(c->getItem(), c2->getItem()) < 0) {
		p->setNext(c->next());
		c->setNext(c2);
		p2->setNext(c);
		c = p->next();
	} else {
		p = c;
		c = c->next();
	}
    }
    _first = dummy->next();
    for (p = _first; !listEnd(p->next()); p = p->next());
	_last = p;
    delete dummy;
}
template <class Item>
Item
List<Item>::first() const
{
    if (!listEnd(_first))
	return _first->getItem();
    else {
	warning("no first on empty list");
        static Item it;
	return it; // empty_data;
    }
}
template <class Item>
ListNode<Item>*
List<Item>::firstNode() const
{
    return _first;
}
//
// remove an item from the list
//
template <class Item>
void
List<Item>::remove(const Item passed_item)
{
    ListNode<Item>* node = _first;
    ListNode<Item>* prev_node = 0;
    if (listEnd(_first))
    {
	warning("can't remove from empty list");
	return;
    }
    while (!listEnd(node)) { 
	if (ElementOps<Item>::compareItems(node->getItem(), passed_item) == 0) {
	    ListNode<Item>* next_node = (ListNode<Item>*) node->next();
	    if (listEnd(prev_node)) {
		_first = next_node;
		if (listEnd(next_node))
			_last = _first;
	    } else {
		prev_node->setNext(next_node);
		if (listEnd(next_node))
			_last = prev_node;
	    }
	    delete node;
	    return;
	} 
        else {
	    prev_node = node;
	    node = node->next();
	}
    }
    //warning("list::remove(): element is not a member of the list");
    return;
}
           
/***********************************************************************
    Considered, but didn't do:
    Removed predecessor and successor to force programmers to use the 
    Iterator.  Using successor is potentially confusing since the 
    end of list marking will not be NULL, but an object of type tc_nil
    if stk is being used.  I don't want to give the applications 
    programmer access to  this.		JWB  7/8/95
************************************************************************/
//
// Return node before the passed node
//
template <class Item>
ContainerNode* 
List<Item>::predecessor(const ContainerNode * in_node) const
{
    ListNode<Item>* n = cn2ListNode(in_node);
    ListNode<Item>* node = _first;
    ListNode<Item>* prev_node = 0;
   
    if (listEnd(n)) 
    {
	warning("no predecessor for null pointer");
	return NULL;
    }
    while (!listEnd(node)) {
       if (node == (ListNode<Item>*) n) {
          if (listEnd(prev_node))
              return NULL;
          else 
              return (ContainerNode*) prev_node;
       }
       prev_node = node;
       node = node->next();
    }
    return NULL;
}
  
//
// Return node after the passed node
//
template <class Item>
ContainerNode* 
List<Item>::successor(const ContainerNode * n) const
{
    if (n == NULL)
    {
        warning("no successor for null pointer");
        return NULL;
    } 
    return (ContainerNode*)  ((ListNode<Item>*) n)->next();
}
//
// return the position of the item in the list
//
template <class Item>
int 
List<Item>::rank(Item& item) const
{
    int position = 1;
    ListNode<Item>* node = _first;
    while (!listEnd(node)) {
        if (ElementOps<Item>::compareItems(item, node->getItem()) == 0)
            return position;
        else {
            position++;
            node = node->next();
        }
    }
    warning("this item is not in the list, return 0");
    return 0;
}
template<class Item>
int
List<Item>::iterate(Iterator<Item>& iterator, Item& item) const
{
    //cout << "iterate::::: iterator._node: " << iterator._node << endl;
    if (iterator._index == 0) {
        // first time through iteration, return first, point at next
        if (listEnd(_first))
            return 0;
        else {
	    //cout << "iterate::: first time thru" << endl;
            item = _first->getItem();
            //*****iterator._node = ListNode2cn(_first->next());
            iterator._node = (ContainerNode*) _first->next();
            iterator._index = 1;
            return 1;
        }
    } else {
	//cout << "iterate::: another time thru" << endl;
        //cout << "iterate::::: iterator._node: " << iterator._node << endl;
        //****ListNode<Item>* node = cn2ListNode(iterator._node);
        ListNode<Item>* node = (ListNode<Item>*) iterator._node;
        if (listEnd(node))
            // end of the iteration
            return 0;
        else {
            // middle of the iteration
            item = node->getItem();
            //********iterator._node = ListNode2cn(node->next());
            iterator._node = (ContainerNode*) node->next();
            iterator._index++;
            return 1;
        }
    }
}
template <class Item>
ostream&
List<Item>::display(ostream& os) const
{
    Iterator<Item> get_next(this);
    Item i;
    Bool start = FALSE;
    os << "(";
    while (get_next(i))
        if (!start) {
                ElementOps<Item>::displayItem(os, i);
                start = TRUE;
        } else {
                os << " ";
                ElementOps<Item>::displayItem(os, i);
	}
    os << ")";
    return os;
}
#endif
