/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */


// wfList.cpp
// A doubly linked list manager
//
// Created by dp Suresh <dp@netscape.com>

#ifdef TEST
#include <stream.h>
#endif /* TEST */


#include <string.h>         // for NULL

#include "wfList.h"


//-------------//
// Constructor //
//-------------//

wfList::wfList(WF_FREE_FUNC freeFunc)
: head(NULL), tail(NULL), freeItemFunc(freeFunc)
{
}


//------------//
// Destructor //
//------------//

wfList::~wfList()
{
	removeAll();
}


//----------------------------------//
// Implementation of public methods //
//----------------------------------//


wfList::ERROR_CODE
wfList::add(void *item)
{
	struct wfListElement *ele = new (struct wfListElement);
	
	if (!ele) return (NO_MEMORY);
	
	ele->item = item;
	ele->next = NULL;
	ele->prev = NULL;
	
	ERROR_CODE ret = listAdd(ele);
	if (ret != SUCCESS)
	  {
		delete ele;
	  }
	
	return (ret);
}

wfList::ERROR_CODE
wfList::remove (void *item)
{
	struct wfListElement *ele = find(item);
	if (ele == NULL)
	  {
		return (NOT_FOUND);
	  }
	
	ERROR_CODE ret = listRemove(ele);
	if (ret == SUCCESS)
	  {
		listDeleteItem(ele->item);
		delete ele;
	  }
	return (ret);
}

wfList::ERROR_CODE
wfList::removeAll (void)
{
	ERROR_CODE ret = SUCCESS;
	while (head && ret == SUCCESS)
	  {
		wfListElement *ele = head;
		ERROR_CODE ret = listRemove(ele);
		if (ret != SUCCESS)
		  {
			break;
		  }
		listDeleteItem(ele->item);
		delete ele;
	  }
	return (ret);
}

wfList::ERROR_CODE
wfList::isEmpty()
{
	return ((head == NULL) ? WF_TRUE : WF_FALSE);
}

wfList::ERROR_CODE
wfList::isExist(void *item)
{
	wfListElement *tmp = find(item);
	return((tmp != NULL) ? WF_TRUE : WF_FALSE);
}

wfList::ERROR_CODE
wfList::iterate(IteratorFunction fn)
{
	struct wfListElement *tmp = head;
	if (!fn) return(GENERIC_ERROR);
	if (!tmp) return(GENERIC_ERROR);
	
	while (tmp)
	  {
		void *item = tmp->item;
		struct wfListElement *next = tmp->next;
		
		ERROR_CODE ret = (*fn)(item);
		switch (ret) {
		case GENERIC_ERROR:
		  return (ret);
		case SUCCESS:
		default:
		  break;
		}
		
		tmp = next;
	  }
	return (SUCCESS);
}

int
wfList::count(void)
{
	wfListElement *tmp = head;
	int n = 0;
    for (; tmp; tmp = tmp->next)
	  {
		n++;
	  }
	return (n);
}

//-----------------------------------//
// Implementation of Private methods //
//-----------------------------------//

wfList::ERROR_CODE
wfList::listAdd(struct wfListElement *ele)
{
	if (isEmpty() == WF_TRUE)
	  {
		// Empty list
		head = tail = ele;
	  }
	else
	  {
		// Add at the tail
		tail->next = ele;
		ele->prev = tail;
		tail = ele;
	  }
	return (SUCCESS);
}


wfList::ERROR_CODE
wfList::listRemove (struct wfListElement *ele)
{
	if (ele->prev)
	  {
		// Not the first element
		ele->prev->next = ele->next;
	  }
	else
	  {
		// First element
		head = ele->next;
	  }
	
	if (ele->next)
	  {
		// Not the last element
		ele->next->prev = ele->prev;
	  }
	else
	  {
		// Last element
		tail = ele->prev;
	  }
	return (SUCCESS);
}

void
wfList::listDeleteItem(void *item)
{
	if (freeItemFunc != NULL)
	{
		(*freeItemFunc)(this, item);
	}
	return;
}


struct wfListElement *
wfList::find(void *item)
{
	wfListElement *tmp = head;
	while (tmp && tmp->item != item) tmp = tmp->next;
	return (tmp);
}


#ifdef TEST
wfList::ERROR_CODE
print(void *item)
{
	cout << "--> " << (int)item << " ";
	return (wfList::SUCCESS);
}

main()
{
	wfList l;

	cout << "isEmpty() : " << l.isEmpty() << "\n";
	cout << "isExist(5): " << l.isExist((void *)5) << "\n";
	cout << "remove(5) : " << l.remove((void *)5) << "\n";
	cout << "count() : " << l.count() << "\n";
	l.iterate(print); cout << "\n";

	cout << "add(5) : " << l.add((void *)5) << "\n";
	cout << "add(6) : " << l.add((void *)6) << "\n";
	cout << "add(7) : " << l.add((void *)7) << "\n";
	cout << "add(8) : " << l.add((void *)8) << "\n";
	cout << "add(9) : " << l.add((void *)9) << "\n";
	cout << "isEmpty() : " << l.isEmpty() << "\n";
	cout << "count() : " << l.count() << "\n";

	l.iterate(print); cout << "\n";

	cout << "isExist(5): " << l.isExist((void *)5) << "\n";
	cout << "isExist(8): " << l.isExist((void *)8) << "\n";
	cout << "isExist(0xCAFE): " << l.isExist((void *)0xCAFE) << "\n";
	cout << "isExist(9): " << l.isExist((void *)9) << "\n";

	cout << "remove(5) : " << l.remove((void *)5) << "\n";
	l.iterate(print); cout << "\n";
	cout << "remove(8) : " << l.remove((void *)8) << "\n";
	l.iterate(print); cout << "\n";
	cout << "remove(0xCAFE) : " << l.remove((void *)0xCAFE) << "\n";
	l.iterate(print); cout << "\n";
	cout << "remove(9) : " << l.remove((void *)9) << "\n";
	cout << "count() : " << l.count() << "\n";
	l.iterate(print); cout << "\n";

	return(0);
}

#if 0
// The above test should yeild this result
isEmpty() : 1
isExist(5): 0
remove(5) : 4

add(5) : 2
add(6) : 2
add(7) : 2
add(8) : 2
add(9) : 2
isEmpty() : 0
--> 5 --> 6 --> 7 --> 8 --> 9 
isExist(5): 1
isExist(8): 1
isExist(0xCAFE): 0
isExist(9): 1
remove(5) : 2
--> 6 --> 7 --> 8 --> 9 
remove(8) : 2
--> 6 --> 7 --> 9 
remove(0xCAFE) : 4
--> 6 --> 7 --> 9 
remove(9) : 2
--> 6 --> 7 
wfList::delete (deleting 6...)
wfList::delete (deleting 7...)
#endif /* 0 */
#endif /* TEST */

